Skip to content

Commit

Permalink
Enable the new efficient MCX decompose (Qiskit#12628)
Browse files Browse the repository at this point in the history
* enable the new efficient MCX decompose

* fix tests

* revert explicit

* apply review comments

* update test_circuit_qasm.py

* update test_decompose.py

* revert C3X C4X names

* fix qasm2 exporter tests

use regex to fetch the mcx_<random id> name

* fix lint and add reno

---------

Co-authored-by: Julien Gacon <jules.gacon@googlemail.com>
  • Loading branch information
2 people authored and Procatv committed Aug 1, 2024
1 parent 23e0385 commit 7e3e0b4
Show file tree
Hide file tree
Showing 6 changed files with 26 additions and 17 deletions.
4 changes: 2 additions & 2 deletions qiskit/circuit/quantumcircuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -5402,12 +5402,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

0 comments on commit 7e3e0b4

Please sign in to comment.