Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable the new efficient MCX decompose #12628

Merged
merged 10 commits into from
Jun 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions qiskit/circuit/quantumcircuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
6 changes: 6 additions & 0 deletions releasenotes/notes/fix-mcx-performance-de86bcc9f969b81e.yaml
Original file line number Diff line number Diff line change
@@ -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.
12 changes: 7 additions & 5 deletions test/python/circuit/test_circuit_qasm.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 (?P<mcx_id>mcx_[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"""
Expand Down
4 changes: 2 additions & 2 deletions test/python/circuit/test_controlled_gate.py
Original file line number Diff line number Diff line change
Expand Up @@ -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])

Expand Down
13 changes: 7 additions & 6 deletions test/python/qasm2/test_export.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 (?P<mcx_id>mcx_[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
Expand Down
4 changes: 2 additions & 2 deletions test/python/transpiler/test_decompose.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand Down
Loading