diff --git a/qiskit/circuit/quantumcircuit.py b/qiskit/circuit/quantumcircuit.py index 08bac04c9e6f..ddd1aaf64d85 100644 --- a/qiskit/circuit/quantumcircuit.py +++ b/qiskit/circuit/quantumcircuit.py @@ -5404,12 +5404,12 @@ def mcx( ValueError: if the given mode is not known, or if too few ancilla qubits are passed. AttributeError: if no ancilla qubits are passed, but some are needed. """ - from .library.standard_gates.x import MCXGrayCode, MCXRecursive, MCXVChain + from .library.standard_gates.x import MCXGate, MCXRecursive, MCXVChain num_ctrl_qubits = len(control_qubits) available_implementations = { - "noancilla": MCXGrayCode(num_ctrl_qubits, ctrl_state=ctrl_state), + "noancilla": MCXGate(num_ctrl_qubits, ctrl_state=ctrl_state), "recursion": MCXRecursive(num_ctrl_qubits, ctrl_state=ctrl_state), "v-chain": MCXVChain(num_ctrl_qubits, False, ctrl_state=ctrl_state), "v-chain-dirty": MCXVChain(num_ctrl_qubits, dirty_ancillas=True, ctrl_state=ctrl_state), diff --git a/releasenotes/notes/fix-mcx-performance-de86bcc9f969b81e.yaml b/releasenotes/notes/fix-mcx-performance-de86bcc9f969b81e.yaml new file mode 100644 index 000000000000..8cee3356ac4c --- /dev/null +++ b/releasenotes/notes/fix-mcx-performance-de86bcc9f969b81e.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - | + Improve the decomposition of the gate generated by :meth:`.QuantumCircuit.mcx` + without using ancilla qubits, so that the number of :class:`.CXGate` will grow + quadratically in the number of qubits and not exponentially. diff --git a/test/python/circuit/test_circuit_qasm.py b/test/python/circuit/test_circuit_qasm.py index c1ece0230d39..13882281cff1 100644 --- a/test/python/circuit/test_circuit_qasm.py +++ b/test/python/circuit/test_circuit_qasm.py @@ -394,12 +394,14 @@ def test_circuit_qasm_with_mcx_gate(self): # qasm output doesn't support parameterized gate yet. # param0 for "gate mcuq(param0) is not used inside the definition - expected_qasm = """OPENQASM 2.0; + pattern = r"""OPENQASM 2.0; include "qelib1.inc"; -gate mcx q0,q1,q2,q3 { h q3; p(pi/8) q0; p(pi/8) q1; p(pi/8) q2; p(pi/8) q3; cx q0,q1; p(-pi/8) q1; cx q0,q1; cx q1,q2; p(-pi/8) q2; cx q0,q2; p(pi/8) q2; cx q1,q2; p(-pi/8) q2; cx q0,q2; cx q2,q3; p(-pi/8) q3; cx q1,q3; p(pi/8) q3; cx q2,q3; p(-pi/8) q3; cx q0,q3; p(pi/8) q3; cx q2,q3; p(-pi/8) q3; cx q1,q3; p(pi/8) q3; cx q2,q3; p(-pi/8) q3; cx q0,q3; h q3; } -qreg q[4]; -mcx q[0],q[1],q[2],q[3];""" - self.assertEqual(dumps(qc), expected_qasm) +gate mcx q0,q1,q2,q3 { h q3; p\(pi/8\) q0; p\(pi/8\) q1; p\(pi/8\) q2; p\(pi/8\) q3; cx q0,q1; p\(-pi/8\) q1; cx q0,q1; cx q1,q2; p\(-pi/8\) q2; cx q0,q2; p\(pi/8\) q2; cx q1,q2; p\(-pi/8\) q2; cx q0,q2; cx q2,q3; p\(-pi/8\) q3; cx q1,q3; p\(pi/8\) q3; cx q2,q3; p\(-pi/8\) q3; cx q0,q3; p\(pi/8\) q3; cx q2,q3; p\(-pi/8\) q3; cx q1,q3; p\(pi/8\) q3; cx q2,q3; p\(-pi/8\) q3; cx q0,q3; h q3; } +gate (?Pmcx_[0-9]*) q0,q1,q2,q3 { mcx q0,q1,q2,q3; } +qreg q\[4\]; +(?P=mcx_id) q\[0\],q\[1\],q\[2\],q\[3\];""" + expected_qasm = re.compile(pattern, re.MULTILINE) + self.assertRegex(dumps(qc), expected_qasm) def test_circuit_qasm_with_mcx_gate_variants(self): """Test circuit qasm() method with MCXGrayCode, MCXRecursive, MCXVChain""" diff --git a/test/python/circuit/test_controlled_gate.py b/test/python/circuit/test_controlled_gate.py index 8ba70ee852ca..f26ab987f4fd 100644 --- a/test/python/circuit/test_controlled_gate.py +++ b/test/python/circuit/test_controlled_gate.py @@ -764,9 +764,9 @@ def test_small_mcx_gates_yield_cx_count(self, num_ctrl_qubits): @data(1, 2, 3, 4) def test_mcxgraycode_gates_yield_explicit_gates(self, num_ctrl_qubits): - """Test creating an mcx gate calls MCXGrayCode and yeilds explicit definition.""" + """Test an MCXGrayCode yields explicit definition.""" qc = QuantumCircuit(num_ctrl_qubits + 1) - qc.mcx(list(range(num_ctrl_qubits)), [num_ctrl_qubits]) + qc.append(MCXGrayCode(num_ctrl_qubits), list(range(qc.num_qubits)), []) explicit = {1: CXGate, 2: CCXGate, 3: C3XGate, 4: C4XGate} self.assertEqual(type(qc[0].operation), explicit[num_ctrl_qubits]) diff --git a/test/python/qasm2/test_export.py b/test/python/qasm2/test_export.py index a0a3ade6ce86..85172ec3ce8f 100644 --- a/test/python/qasm2/test_export.py +++ b/test/python/qasm2/test_export.py @@ -387,13 +387,14 @@ def test_mcx_gate(self): # qasm output doesn't support parameterized gate yet. # param0 for "gate mcuq(param0) is not used inside the definition - expected_qasm = """\ -OPENQASM 2.0; + pattern = r"""OPENQASM 2.0; include "qelib1.inc"; -gate mcx q0,q1,q2,q3 { h q3; p(pi/8) q0; p(pi/8) q1; p(pi/8) q2; p(pi/8) q3; cx q0,q1; p(-pi/8) q1; cx q0,q1; cx q1,q2; p(-pi/8) q2; cx q0,q2; p(pi/8) q2; cx q1,q2; p(-pi/8) q2; cx q0,q2; cx q2,q3; p(-pi/8) q3; cx q1,q3; p(pi/8) q3; cx q2,q3; p(-pi/8) q3; cx q0,q3; p(pi/8) q3; cx q2,q3; p(-pi/8) q3; cx q1,q3; p(pi/8) q3; cx q2,q3; p(-pi/8) q3; cx q0,q3; h q3; } -qreg q[4]; -mcx q[0],q[1],q[2],q[3];""" - self.assertEqual(qasm2.dumps(qc), expected_qasm) +gate mcx q0,q1,q2,q3 { h q3; p\(pi/8\) q0; p\(pi/8\) q1; p\(pi/8\) q2; p\(pi/8\) q3; cx q0,q1; p\(-pi/8\) q1; cx q0,q1; cx q1,q2; p\(-pi/8\) q2; cx q0,q2; p\(pi/8\) q2; cx q1,q2; p\(-pi/8\) q2; cx q0,q2; cx q2,q3; p\(-pi/8\) q3; cx q1,q3; p\(pi/8\) q3; cx q2,q3; p\(-pi/8\) q3; cx q0,q3; p\(pi/8\) q3; cx q2,q3; p\(-pi/8\) q3; cx q1,q3; p\(pi/8\) q3; cx q2,q3; p\(-pi/8\) q3; cx q0,q3; h q3; } +gate (?Pmcx_[0-9]*) q0,q1,q2,q3 { mcx q0,q1,q2,q3; } +qreg q\[4\]; +(?P=mcx_id) q\[0\],q\[1\],q\[2\],q\[3\];""" + expected_qasm = re.compile(pattern, re.MULTILINE) + self.assertRegex(qasm2.dumps(qc), expected_qasm) def test_mcx_gate_variants(self): n = 5 diff --git a/test/python/transpiler/test_decompose.py b/test/python/transpiler/test_decompose.py index 91ebede9fa86..7b364f3ac10f 100644 --- a/test/python/transpiler/test_decompose.py +++ b/test/python/transpiler/test_decompose.py @@ -216,7 +216,7 @@ def test_decompose_only_given_label(self): def test_decompose_only_given_name(self): """Test decomposition parameters so that only given name is decomposed.""" - decom_circ = self.complex_circuit.decompose(["mcx"]) + decom_circ = self.complex_circuit.decompose(["mcx"], reps=2) dag = circuit_to_dag(decom_circ) self.assertEqual(len(dag.op_nodes()), 13) @@ -236,7 +236,7 @@ def test_decompose_only_given_name(self): def test_decompose_mixture_of_names_and_labels(self): """Test decomposition parameters so that mixture of names and labels is decomposed""" - decom_circ = self.complex_circuit.decompose(["mcx", "gate2"]) + decom_circ = self.complex_circuit.decompose(["mcx", "gate2"], reps=2) dag = circuit_to_dag(decom_circ) self.assertEqual(len(dag.op_nodes()), 15)