Skip to content

Commit 0bf91a6

Browse files
Minor fixes/improvements to some MCX synthesis methods (#14093)
* updates to synth_mcx_n_dirty_i15 and to synth_mcx_1_clean_b95 * add explicit tests for MCX synthesis algorithms * improving comment * rename variable * slightly generalize and simplify _msu_real_diagonal * updating qasm tests * improving treatment of special cases in synth_mcx_n_dirty_i15 * remove duplicated tests * add tests for counting CX gates * add some more cx_count tests * remove tests * improving docstring comments (minor) * pass over tests * docstring * increasing the number of control qubits in some tests * changing circuit's name to mcx_vchain for compatibility * cleanup * fixing MCX qasms * updating docstrings * reno * tests and docs * fix qasm tests * addressing review comments * futher expanding review comment --------- Co-authored-by: ShellyGarion <shelly@il.ibm.com>
1 parent 906f553 commit 0bf91a6

7 files changed

Lines changed: 91 additions & 82 deletions

File tree

qiskit/synthesis/multi_controlled/mcx_synthesis.py

Lines changed: 44 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,12 @@ def synth_mcx_n_dirty_i15(
3232
action_only: bool = False,
3333
) -> QuantumCircuit:
3434
r"""
35-
Synthesize a multi-controlled X gate with :math:`k` controls using :math:`k - 2`
36-
dirty ancillary qubits producing a circuit with :math:`2 * k - 1` qubits and at most
37-
:math:`8 * k - 6` CX gates, by Iten et. al. [1].
35+
Synthesize a multi-controlled X gate with :math:`k` controls based on the paper
36+
by Iten et al. [1].
37+
38+
For :math:`k\ge 4` the method uses :math:`k - 2` dirty ancillary qubits, producing a circuit
39+
with :math:`2 * k - 1` qubits and at most :math:`8 * k - 6` CX gates. For :math:`k\le 3`
40+
explicit efficient circuits are used instead.
3841
3942
Args:
4043
num_ctrl_qubits: The number of control qubits.
@@ -53,27 +56,26 @@ def synth_mcx_n_dirty_i15(
5356
`arXiv:1501.06911 <http://arxiv.org/abs/1501.06911>`_
5457
"""
5558

59+
# First, handle some special cases
5660
if num_ctrl_qubits == 1:
57-
num_qubits = 2
58-
else:
59-
num_qubits = 2 * num_ctrl_qubits - 1
60-
q = QuantumRegister(num_qubits, name="q")
61-
qc = QuantumCircuit(q, name="mcx_vchain")
62-
q_controls = q[:num_ctrl_qubits]
63-
q_target = q[num_ctrl_qubits]
64-
q_ancillas = q[num_ctrl_qubits + 1 :]
65-
66-
if num_ctrl_qubits == 1:
67-
qc.cx(q_controls, q_target)
61+
qc = QuantumCircuit(2)
62+
qc.cx(0, 1)
6863
return qc
6964
elif num_ctrl_qubits == 2:
70-
qc.ccx(q_controls[0], q_controls[1], q_target)
65+
qc = QuantumCircuit(3)
66+
qc.ccx(0, 1, 2)
7167
return qc
72-
elif not relative_phase and num_ctrl_qubits == 3:
73-
circuit = synth_c3x()
74-
qc.compose(circuit, [*q_controls, q_target], inplace=True, copy=False)
68+
elif num_ctrl_qubits == 3 and not relative_phase:
69+
qc = synth_c3x()
70+
qc.name = "mcx_vchain"
7571
return qc
7672

73+
num_qubits = 2 * num_ctrl_qubits - 1
74+
q = QuantumRegister(num_qubits, name="q")
75+
qc = QuantumCircuit(q, name="mcx_vchain")
76+
q_controls = q[:num_ctrl_qubits]
77+
q_target = q[num_ctrl_qubits]
78+
q_ancillas = q[num_ctrl_qubits + 1 :]
7779
num_ancillas = num_ctrl_qubits - 2
7880
targets = [q_target] + q_ancillas[:num_ancillas][::-1]
7981

@@ -181,7 +183,7 @@ def synth_mcx_1_clean_b95(num_ctrl_qubits: int) -> QuantumCircuit:
181183
r"""
182184
Synthesize a multi-controlled X gate with :math:`k` controls using a single
183185
clean ancillary qubit producing a circuit with :math:`k + 2` qubits and at most
184-
:math:`16 * k - 8` CX gates, by Barenco et al. [1].
186+
:math:`16 * k - 24` CX gates, by [1], [2].
185187
186188
Args:
187189
num_ctrl_qubits: The number of control qubits.
@@ -190,8 +192,10 @@ def synth_mcx_1_clean_b95(num_ctrl_qubits: int) -> QuantumCircuit:
190192
The synthesized quantum circuit.
191193
192194
References:
193-
1. Barenco et. al., Phys.Rev. A52 3457 (1995),
195+
1. Barenco et. al., *Elementary gates for quantum computation*, Phys.Rev. A52 3457 (1995),
194196
`arXiv:quant-ph/9503016 <https://arxiv.org/abs/quant-ph/9503016>`_
197+
2. Iten et. al., *Quantum Circuits for Isometries*, Phys. Rev. A 93, 032318 (2016),
198+
`arXiv:1501.06911 <http://arxiv.org/abs/1501.06911>`_
195199
"""
196200

197201
if num_ctrl_qubits == 3:
@@ -208,32 +212,27 @@ def synth_mcx_1_clean_b95(num_ctrl_qubits: int) -> QuantumCircuit:
208212
q_ancilla = q[-1]
209213
q_target = q[-2]
210214
middle = ceil(num_ctrl_qubits / 2)
211-
first_half = [*q[:middle]]
212-
second_half = [*q[middle : num_ctrl_qubits - 1], q_ancilla]
213215

214-
qc_first_half = synth_mcx_n_dirty_i15(num_ctrl_qubits=len(first_half))
215-
qc_second_half = synth_mcx_n_dirty_i15(num_ctrl_qubits=len(second_half))
216-
217-
qc.append(
218-
qc_first_half,
219-
qargs=[*first_half, q_ancilla, *q[middle : middle + len(first_half) - 2]],
220-
cargs=[],
221-
)
222-
qc.append(
223-
qc_second_half,
224-
qargs=[*second_half, q_target, *q[: len(second_half) - 2]],
225-
cargs=[],
226-
)
227-
qc.append(
228-
qc_first_half,
229-
qargs=[*first_half, q_ancilla, *q[middle : middle + len(first_half) - 2]],
230-
cargs=[],
231-
)
232-
qc.append(
233-
qc_second_half,
234-
qargs=[*second_half, q_target, *q[: len(second_half) - 2]],
235-
cargs=[],
236-
)
216+
# The contruction involving 4 MCX gates is described in Lemma 7.3 of [1], and also
217+
# appears as Lemma 9 in [2]. The optimization that the first and third MCX gates
218+
# can be synthesized up to relative phase follows from Lemma 7 in [2], as a diagonal
219+
# gate following the first MCX gate commutes with the second MCX gate, and
220+
# thus cancels with the inverse diagonal gate preceding the third MCX gate. The
221+
# same optimization cannot be applied to the second MCX gate, since a diagonal
222+
# gate following the second MCX gate would not satisfy the preconditions of Lemma 7,
223+
# and would not necessarily commute with the third MCX gate.
224+
controls1 = [*q[:middle]]
225+
mcx1 = synth_mcx_n_dirty_i15(num_ctrl_qubits=len(controls1), relative_phase=True)
226+
qubits1 = [*controls1, q_ancilla, *q[middle : middle + mcx1.num_qubits - len(controls1) - 1]]
227+
228+
controls2 = [*q[middle : num_ctrl_qubits - 1], q_ancilla]
229+
mcx2 = synth_mcx_n_dirty_i15(num_ctrl_qubits=len(controls2))
230+
qc2_qubits = [*controls2, q_target, *q[0 : mcx2.num_qubits - len(controls2) - 1]]
231+
232+
qc.compose(mcx1, qubits1, inplace=True)
233+
qc.compose(mcx2, qc2_qubits, inplace=True)
234+
qc.compose(mcx1.inverse(), qubits1, inplace=True)
235+
qc.compose(mcx2, qc2_qubits, inplace=True)
237236

238237
return qc
239238

qiskit/synthesis/multi_controlled/multi_control_rotation_gates.py

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -171,30 +171,29 @@ def _mcsu2_real_diagonal(
171171
k_1 = math.ceil(num_controls / 2.0)
172172
k_2 = math.floor(num_controls / 2.0)
173173

174-
circuit = QuantumCircuit(num_controls + 1, name="MCSU2")
175174
controls = list(range(num_controls)) # control indices, defined for code legibility
176175
target = num_controls # target index, defined for code legibility
177176

177+
mcx1 = synth_mcx_n_dirty_i15(num_ctrl_qubits=k_1)
178+
mcx1_num_ancillas = mcx1.num_qubits - k_1 - 1
179+
mcx1_qubits = controls[:k_1] + [target] + controls[k_1 : k_1 + mcx1_num_ancillas]
180+
181+
mcx2 = synth_mcx_n_dirty_i15(num_ctrl_qubits=k_2)
182+
mcx2_num_ancillas = mcx2.num_qubits - k_2 - 1
183+
mcx2_qubits = controls[k_1:] + [target] + controls[k_1 - mcx2_num_ancillas : k_1]
184+
185+
circuit = QuantumCircuit(num_controls + 1, name="MCSU2")
186+
178187
if not is_secondary_diag_real:
179188
circuit.h(target)
180189

181-
mcx_1 = synth_mcx_n_dirty_i15(num_ctrl_qubits=k_1)
182-
circuit.compose(mcx_1, controls[:k_1] + [target] + controls[k_1 : 2 * k_1 - 2], inplace=True)
190+
circuit.compose(mcx1, mcx1_qubits, inplace=True)
183191
circuit.append(s_gate, [target])
184-
185-
# TODO: improve CX count by using action_only=True (based on #9687)
186-
mcx_2 = synth_mcx_n_dirty_i15(num_ctrl_qubits=k_2).to_gate()
187-
circuit.compose(
188-
mcx_2.inverse(), controls[k_1:] + [target] + controls[k_1 - k_2 + 2 : k_1], inplace=True
189-
)
192+
circuit.compose(mcx2, mcx2_qubits, inplace=True)
190193
circuit.append(s_gate.inverse(), [target])
191-
192-
mcx_3 = synth_mcx_n_dirty_i15(num_ctrl_qubits=k_1).to_gate()
193-
circuit.compose(mcx_3, controls[:k_1] + [target] + controls[k_1 : 2 * k_1 - 2], inplace=True)
194+
circuit.compose(mcx1, mcx1_qubits, inplace=True)
194195
circuit.append(s_gate, [target])
195-
196-
mcx_4 = synth_mcx_n_dirty_i15(num_ctrl_qubits=k_2).to_gate()
197-
circuit.compose(mcx_4, controls[k_1:] + [target] + controls[k_1 - k_2 + 2 : k_1], inplace=True)
196+
circuit.compose(mcx2, mcx2_qubits, inplace=True)
198197
circuit.append(s_gate.inverse(), [target])
199198

200199
if not is_secondary_diag_real:

qiskit/transpiler/passes/synthesis/hls_plugins.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1008,9 +1008,10 @@ class MCXSynthesisNDirtyI15(HighLevelSynthesisPlugin):
10081008
This plugin name is :``mcx.n_dirty_i15`` which can be used as the key on
10091009
an :class:`~.HLSConfig` object to use this method with :class:`~.HighLevelSynthesis`.
10101010
1011-
For a multi-controlled X gate with :math:`k\ge 3` control qubits this synthesis
1011+
For a multi-controlled X gate with :math:`k\ge 4` control qubits this synthesis
10121012
method requires :math:`k - 2` additional dirty auxiliary qubits. The synthesized
10131013
circuit consists of :math:`2 * k - 1` qubits and at most :math:`8 * k - 6` CX gates.
1014+
For :math:`k\le 3` explicit efficient circuits are used instead.
10141015
10151016
The plugin supports the following plugin-specific options:
10161017
@@ -1105,14 +1106,14 @@ class MCXSynthesis1CleanB95(HighLevelSynthesisPlugin):
11051106
11061107
For a multi-controlled X gate with :math:`k\ge 5` control qubits this synthesis
11071108
method requires a single additional clean auxiliary qubit. The synthesized
1108-
circuit consists of :math:`k + 2` qubits and at most :math:`16 * k - 8` CX gates.
1109+
circuit consists of :math:`k + 2` qubits and at most :math:`16 * k - 24` CX gates.
11091110
11101111
The plugin supports the following plugin-specific options:
11111112
11121113
* num_clean_ancillas: The number of clean auxiliary qubits available.
11131114
11141115
References:
1115-
1. Barenco et. al., Phys.Rev. A52 3457 (1995),
1116+
1. Barenco et. al., *Elementary gates for quantum computation*, Phys.Rev. A52 3457 (1995),
11161117
`arXiv:quant-ph/9503016 <https://arxiv.org/abs/quant-ph/9503016>`_
11171118
"""
11181119

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
features_synthesis:
3+
- |
4+
The synthesis function :func:`.synth_mcx_1_clean_b95` now produces a circuit
5+
with fewer CX-gates.
6+
upgrade_synthesis:
7+
- |
8+
The synthesis function :func:`.synth_mcx_1_clean_b95` now produces a circuit
9+
with fewer layers of wrappings.
10+
fixes:
11+
- |
12+
When synthesizing an :class:`.MCXGate` gate with 3 controls, the synthesis
13+
functon :func:`.synth_mcx_n_dirty_i15` used to require one auxiliary qubit,
14+
producing a circuit with 5 qubits (3 control, 1 target, and 1 auxiliary).
15+
However, the actual synthesis algorithm does not make use of this auxiliary
16+
qubit. This behavior is now fixed: the synthesized circuit is over 4 qubits
17+
(3 control and 1 target), allowing to apply the synthesis function in a
18+
slighty larger number of cases.

test/python/circuit/test_circuit_qasm.py

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -406,20 +406,16 @@ def test_circuit_qasm_with_mcx_gate_variants(self):
406406
qc.append(cl.MCXGrayCode(n), range(n + 1))
407407
qc.append(cl.MCXRecursive(n), range(n + 2))
408408
qc.append(cl.MCXVChain(n), range(2 * n - 1))
409-
mcx_vchain_id = id(qc.data[-1].operation)
410409

411-
# qasm output doesn't support parameterized gate yet.
412-
# param0 for "gate mcuq(param0) is not used inside the definition
413-
expected_qasm = f"""OPENQASM 2.0;
410+
expected_qasm = """OPENQASM 2.0;
414411
include "qelib1.inc";
415-
gate mcx_gray q0,q1,q2,q3,q4,q5 {{ h q5; cu1(pi/16) q4,q5; cx q4,q3; cu1(-pi/16) q3,q5; cx q4,q3; cu1(pi/16) q3,q5; cx q3,q2; cu1(-pi/16) q2,q5; cx q4,q2; cu1(pi/16) q2,q5; cx q3,q2; cu1(-pi/16) q2,q5; cx q4,q2; cu1(pi/16) q2,q5; cx q2,q1; cu1(-pi/16) q1,q5; cx q4,q1; cu1(pi/16) q1,q5; cx q3,q1; cu1(-pi/16) q1,q5; cx q4,q1; cu1(pi/16) q1,q5; cx q2,q1; cu1(-pi/16) q1,q5; cx q4,q1; cu1(pi/16) q1,q5; cx q3,q1; cu1(-pi/16) q1,q5; cx q4,q1; cu1(pi/16) q1,q5; cx q1,q0; cu1(-pi/16) q0,q5; cx q4,q0; cu1(pi/16) q0,q5; cx q3,q0; cu1(-pi/16) q0,q5; cx q4,q0; cu1(pi/16) q0,q5; cx q2,q0; cu1(-pi/16) q0,q5; cx q4,q0; cu1(pi/16) q0,q5; cx q3,q0; cu1(-pi/16) q0,q5; cx q4,q0; cu1(pi/16) q0,q5; cx q1,q0; cu1(-pi/16) q0,q5; cx q4,q0; cu1(pi/16) q0,q5; cx q3,q0; cu1(-pi/16) q0,q5; cx q4,q0; cu1(pi/16) q0,q5; cx q2,q0; cu1(-pi/16) q0,q5; cx q4,q0; cu1(pi/16) q0,q5; cx q3,q0; cu1(-pi/16) q0,q5; cx q4,q0; cu1(pi/16) q0,q5; h q5; }}
416-
gate mcx_vchain q0,q1,q2,q3,q4 {{ 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; }}
417-
gate mcx_recursive q0,q1,q2,q3,q4,q5,q6 {{ mcx_vchain q0,q1,q2,q6,q3; mcx_vchain q3,q4,q6,q5,q0; mcx_vchain q0,q1,q2,q6,q3; mcx_vchain q3,q4,q6,q5,q0; }}
418-
gate mcx_vchain_{mcx_vchain_id} q0,q1,q2,q3,q4,q5,q6,q7,q8 {{ rccx q0,q1,q6; rccx q2,q6,q7; rccx q3,q7,q8; ccx q4,q8,q5; rccx q3,q7,q8; rccx q2,q6,q7; rccx q0,q1,q6; }}
412+
gate mcx_gray q0,q1,q2,q3,q4,q5 { h q5; cu1(pi/16) q4,q5; cx q4,q3; cu1(-pi/16) q3,q5; cx q4,q3; cu1(pi/16) q3,q5; cx q3,q2; cu1(-pi/16) q2,q5; cx q4,q2; cu1(pi/16) q2,q5; cx q3,q2; cu1(-pi/16) q2,q5; cx q4,q2; cu1(pi/16) q2,q5; cx q2,q1; cu1(-pi/16) q1,q5; cx q4,q1; cu1(pi/16) q1,q5; cx q3,q1; cu1(-pi/16) q1,q5; cx q4,q1; cu1(pi/16) q1,q5; cx q2,q1; cu1(-pi/16) q1,q5; cx q4,q1; cu1(pi/16) q1,q5; cx q3,q1; cu1(-pi/16) q1,q5; cx q4,q1; cu1(pi/16) q1,q5; cx q1,q0; cu1(-pi/16) q0,q5; cx q4,q0; cu1(pi/16) q0,q5; cx q3,q0; cu1(-pi/16) q0,q5; cx q4,q0; cu1(pi/16) q0,q5; cx q2,q0; cu1(-pi/16) q0,q5; cx q4,q0; cu1(pi/16) q0,q5; cx q3,q0; cu1(-pi/16) q0,q5; cx q4,q0; cu1(pi/16) q0,q5; cx q1,q0; cu1(-pi/16) q0,q5; cx q4,q0; cu1(pi/16) q0,q5; cx q3,q0; cu1(-pi/16) q0,q5; cx q4,q0; cu1(pi/16) q0,q5; cx q2,q0; cu1(-pi/16) q0,q5; cx q4,q0; cu1(pi/16) q0,q5; cx q3,q0; cu1(-pi/16) q0,q5; cx q4,q0; cu1(pi/16) q0,q5; h q5; }
413+
gate mcx_recursive q0,q1,q2,q3,q4,q5,q6 { h q6; t q6; cx q2,q6; tdg q6; cx q3,q6; h q3; t q3; cx q0,q3; tdg q3; cx q1,q3; t q3; cx q0,q3; tdg q3; h q3; cx q3,q6; t q6; cx q2,q6; tdg q6; h q6; h q3; t q3; cx q0,q3; tdg q3; cx q1,q3; t q3; cx q0,q3; tdg q3; h q3; h q5; p(pi/8) q3; p(pi/8) q4; p(pi/8) q6; p(pi/8) q5; cx q3,q4; p(-pi/8) q4; cx q3,q4; cx q4,q6; p(-pi/8) q6; cx q3,q6; p(pi/8) q6; cx q4,q6; p(-pi/8) q6; cx q3,q6; cx q6,q5; p(-pi/8) q5; cx q4,q5; p(pi/8) q5; cx q6,q5; p(-pi/8) q5; cx q3,q5; p(pi/8) q5; cx q6,q5; p(-pi/8) q5; cx q4,q5; p(pi/8) q5; cx q6,q5; p(-pi/8) q5; cx q3,q5; h q5; h q3; t q3; cx q0,q3; tdg q3; cx q1,q3; t q3; cx q0,q3; tdg q3; h q3; h q6; t q6; cx q2,q6; tdg q6; cx q3,q6; h q3; t q3; cx q0,q3; tdg q3; cx q1,q3; t q3; cx q0,q3; tdg q3; h q3; cx q3,q6; t q6; cx q2,q6; tdg q6; h q6; h q5; p(pi/8) q3; p(pi/8) q4; p(pi/8) q6; p(pi/8) q5; cx q3,q4; p(-pi/8) q4; cx q3,q4; cx q4,q6; p(-pi/8) q6; cx q3,q6; p(pi/8) q6; cx q4,q6; p(-pi/8) q6; cx q3,q6; cx q6,q5; p(-pi/8) q5; cx q4,q5; p(pi/8) q5; cx q6,q5; p(-pi/8) q5; cx q3,q5; p(pi/8) q5; cx q6,q5; p(-pi/8) q5; cx q4,q5; p(pi/8) q5; cx q6,q5; p(-pi/8) q5; cx q3,q5; h q5; }
414+
gate mcx_vchain q0,q1,q2,q3,q4,q5,q6,q7,q8 { rccx q0,q1,q6; rccx q2,q6,q7; rccx q3,q7,q8; ccx q4,q8,q5; rccx q3,q7,q8; rccx q2,q6,q7; rccx q0,q1,q6; }
419415
qreg q[9];
420416
mcx_gray q[0],q[1],q[2],q[3],q[4],q[5];
421417
mcx_recursive q[0],q[1],q[2],q[3],q[4],q[5],q[6];
422-
mcx_vchain_{mcx_vchain_id} q[0],q[1],q[2],q[3],q[4],q[5],q[6],q[7],q[8];"""
418+
mcx_vchain q[0],q[1],q[2],q[3],q[4],q[5],q[6],q[7],q[8];"""
423419

424420
self.assertEqual(dumps(qc), expected_qasm)
425421

test/python/qasm2/test_export.py

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -396,20 +396,16 @@ def test_mcx_gate_variants(self):
396396
qc.append(lib.MCXGrayCode(n), range(n + 1))
397397
qc.append(lib.MCXRecursive(n), range(n + 2))
398398
qc.append(lib.MCXVChain(n), range(2 * n - 1))
399-
mcx_vchain_id = id(qc.data[-1].operation)
400399

401-
# qasm output doesn't support parameterized gate yet.
402-
# param0 for "gate mcuq(param0) is not used inside the definition
403-
expected_qasm = f"""OPENQASM 2.0;
400+
expected_qasm = """OPENQASM 2.0;
404401
include "qelib1.inc";
405-
gate mcx_gray q0,q1,q2,q3,q4,q5 {{ h q5; cu1(pi/16) q4,q5; cx q4,q3; cu1(-pi/16) q3,q5; cx q4,q3; cu1(pi/16) q3,q5; cx q3,q2; cu1(-pi/16) q2,q5; cx q4,q2; cu1(pi/16) q2,q5; cx q3,q2; cu1(-pi/16) q2,q5; cx q4,q2; cu1(pi/16) q2,q5; cx q2,q1; cu1(-pi/16) q1,q5; cx q4,q1; cu1(pi/16) q1,q5; cx q3,q1; cu1(-pi/16) q1,q5; cx q4,q1; cu1(pi/16) q1,q5; cx q2,q1; cu1(-pi/16) q1,q5; cx q4,q1; cu1(pi/16) q1,q5; cx q3,q1; cu1(-pi/16) q1,q5; cx q4,q1; cu1(pi/16) q1,q5; cx q1,q0; cu1(-pi/16) q0,q5; cx q4,q0; cu1(pi/16) q0,q5; cx q3,q0; cu1(-pi/16) q0,q5; cx q4,q0; cu1(pi/16) q0,q5; cx q2,q0; cu1(-pi/16) q0,q5; cx q4,q0; cu1(pi/16) q0,q5; cx q3,q0; cu1(-pi/16) q0,q5; cx q4,q0; cu1(pi/16) q0,q5; cx q1,q0; cu1(-pi/16) q0,q5; cx q4,q0; cu1(pi/16) q0,q5; cx q3,q0; cu1(-pi/16) q0,q5; cx q4,q0; cu1(pi/16) q0,q5; cx q2,q0; cu1(-pi/16) q0,q5; cx q4,q0; cu1(pi/16) q0,q5; cx q3,q0; cu1(-pi/16) q0,q5; cx q4,q0; cu1(pi/16) q0,q5; h q5; }}
406-
gate mcx_vchain q0,q1,q2,q3,q4 {{ 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; }}
407-
gate mcx_recursive q0,q1,q2,q3,q4,q5,q6 {{ mcx_vchain q0,q1,q2,q6,q3; mcx_vchain q3,q4,q6,q5,q0; mcx_vchain q0,q1,q2,q6,q3; mcx_vchain q3,q4,q6,q5,q0; }}
408-
gate mcx_vchain_{mcx_vchain_id} q0,q1,q2,q3,q4,q5,q6,q7,q8 {{ rccx q0,q1,q6; rccx q2,q6,q7; rccx q3,q7,q8; ccx q4,q8,q5; rccx q3,q7,q8; rccx q2,q6,q7; rccx q0,q1,q6; }}
402+
gate mcx_gray q0,q1,q2,q3,q4,q5 { h q5; cu1(pi/16) q4,q5; cx q4,q3; cu1(-pi/16) q3,q5; cx q4,q3; cu1(pi/16) q3,q5; cx q3,q2; cu1(-pi/16) q2,q5; cx q4,q2; cu1(pi/16) q2,q5; cx q3,q2; cu1(-pi/16) q2,q5; cx q4,q2; cu1(pi/16) q2,q5; cx q2,q1; cu1(-pi/16) q1,q5; cx q4,q1; cu1(pi/16) q1,q5; cx q3,q1; cu1(-pi/16) q1,q5; cx q4,q1; cu1(pi/16) q1,q5; cx q2,q1; cu1(-pi/16) q1,q5; cx q4,q1; cu1(pi/16) q1,q5; cx q3,q1; cu1(-pi/16) q1,q5; cx q4,q1; cu1(pi/16) q1,q5; cx q1,q0; cu1(-pi/16) q0,q5; cx q4,q0; cu1(pi/16) q0,q5; cx q3,q0; cu1(-pi/16) q0,q5; cx q4,q0; cu1(pi/16) q0,q5; cx q2,q0; cu1(-pi/16) q0,q5; cx q4,q0; cu1(pi/16) q0,q5; cx q3,q0; cu1(-pi/16) q0,q5; cx q4,q0; cu1(pi/16) q0,q5; cx q1,q0; cu1(-pi/16) q0,q5; cx q4,q0; cu1(pi/16) q0,q5; cx q3,q0; cu1(-pi/16) q0,q5; cx q4,q0; cu1(pi/16) q0,q5; cx q2,q0; cu1(-pi/16) q0,q5; cx q4,q0; cu1(pi/16) q0,q5; cx q3,q0; cu1(-pi/16) q0,q5; cx q4,q0; cu1(pi/16) q0,q5; h q5; }
403+
gate mcx_recursive q0,q1,q2,q3,q4,q5,q6 { h q6; t q6; cx q2,q6; tdg q6; cx q3,q6; h q3; t q3; cx q0,q3; tdg q3; cx q1,q3; t q3; cx q0,q3; tdg q3; h q3; cx q3,q6; t q6; cx q2,q6; tdg q6; h q6; h q3; t q3; cx q0,q3; tdg q3; cx q1,q3; t q3; cx q0,q3; tdg q3; h q3; h q5; p(pi/8) q3; p(pi/8) q4; p(pi/8) q6; p(pi/8) q5; cx q3,q4; p(-pi/8) q4; cx q3,q4; cx q4,q6; p(-pi/8) q6; cx q3,q6; p(pi/8) q6; cx q4,q6; p(-pi/8) q6; cx q3,q6; cx q6,q5; p(-pi/8) q5; cx q4,q5; p(pi/8) q5; cx q6,q5; p(-pi/8) q5; cx q3,q5; p(pi/8) q5; cx q6,q5; p(-pi/8) q5; cx q4,q5; p(pi/8) q5; cx q6,q5; p(-pi/8) q5; cx q3,q5; h q5; h q3; t q3; cx q0,q3; tdg q3; cx q1,q3; t q3; cx q0,q3; tdg q3; h q3; h q6; t q6; cx q2,q6; tdg q6; cx q3,q6; h q3; t q3; cx q0,q3; tdg q3; cx q1,q3; t q3; cx q0,q3; tdg q3; h q3; cx q3,q6; t q6; cx q2,q6; tdg q6; h q6; h q5; p(pi/8) q3; p(pi/8) q4; p(pi/8) q6; p(pi/8) q5; cx q3,q4; p(-pi/8) q4; cx q3,q4; cx q4,q6; p(-pi/8) q6; cx q3,q6; p(pi/8) q6; cx q4,q6; p(-pi/8) q6; cx q3,q6; cx q6,q5; p(-pi/8) q5; cx q4,q5; p(pi/8) q5; cx q6,q5; p(-pi/8) q5; cx q3,q5; p(pi/8) q5; cx q6,q5; p(-pi/8) q5; cx q4,q5; p(pi/8) q5; cx q6,q5; p(-pi/8) q5; cx q3,q5; h q5; }
404+
gate mcx_vchain q0,q1,q2,q3,q4,q5,q6,q7,q8 { rccx q0,q1,q6; rccx q2,q6,q7; rccx q3,q7,q8; ccx q4,q8,q5; rccx q3,q7,q8; rccx q2,q6,q7; rccx q0,q1,q6; }
409405
qreg q[9];
410406
mcx_gray q[0],q[1],q[2],q[3],q[4],q[5];
411407
mcx_recursive q[0],q[1],q[2],q[3],q[4],q[5],q[6];
412-
mcx_vchain_{mcx_vchain_id} q[0],q[1],q[2],q[3],q[4],q[5],q[6],q[7],q[8];"""
408+
mcx_vchain q[0],q[1],q[2],q[3],q[4],q[5],q[6],q[7],q[8];"""
413409

414410
self.assertEqual(qasm2.dumps(qc), expected_qasm)
415411

test/python/synthesis/test_multi_controlled_synthesis.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ def test_mcx_1_clean_b95_cx_count(self, num_ctrl_qubits: int):
270270
transpiled_circuit = self.pm.run(synthesized_circuit)
271271
cx_count = transpiled_circuit.count_ops()["cx"]
272272
# The bound from the documentation of synth_mcx_1_clean_b95
273-
self.assertLessEqual(cx_count, 16 * num_ctrl_qubits - 8)
273+
self.assertLessEqual(cx_count, 16 * num_ctrl_qubits - 24)
274274

275275
@data(3, 5, 10, 15)
276276
def test_mcx_1_clean_kg24_cx_count(self, num_ctrl_qubits: int):

0 commit comments

Comments
 (0)