Skip to content

Commit 072548f

Browse files
Adding MCX synthesis plugins (Qiskit#12961)
* moving high-level-synthesis plugins to a separate file * Adding the remaining MCX synthesis functions and exposting all of them as HLS synthesis plugins. * adding entry points for MCX plugins * adding documentation section for MCX plugins * renaming file * Adding pending deprecation warnings * placeholder for MCX plugin tests * adding flag pending=True to deprecate * changing checks from isinstance to name-based: a CCCX gate is called mcx but is not an MCXGate * futher exposing C3X and C4X synthesis * updating MCX synthesis functions to avoid returning C3X and C4X gates For better or for worse, C3X and C4X gates have name 'mcx', and therefore are caught by mcx plugins. We need to avoid recursion with HLS calling an MCX-synthesis plugin for a C3X-gate, which in turns returns a C3X-gate. * fix compose to append * renaming synthesized circuits for c3x and for c4x back to 'mcx' to avoid qasm changes * test qasm fixes * randomly spotted typo * fixing how QuantumCircuit.decompose works in the presence of gates_to_decompose * updating MCX plugins to check isinstance * fixing test * pylint fixes * properly fixing test * additional tests * adding new synthesis functions to synthesis docs * release notes * docstrings improvements followin review * Adding refernce to Vale et al paper for the MCXPhase gate implementation * fixes to deprecation warnings and adding deprecation for get_num_ancilla_qubits * docstring fixes * renaming mcphase to v24 * removing unncessary checks * addressing the rest of review comments * and of course updating qasm checking after we've slightly changed the decomposition * yet another renaming * Update qiskit/circuit/library/standard_gates/x.py Co-authored-by: Julien Gacon <[email protected]> * Update qiskit/circuit/library/standard_gates/x.py Co-authored-by: Julien Gacon <[email protected]> * Update releasenotes/notes/add-mcx-plugins-85e5b248692a36db.yaml Co-authored-by: Julien Gacon <[email protected]> * release notes * formatting * fixing docs * removing references from the first sentence of plugin descriptions --------- Co-authored-by: Julien Gacon <[email protected]>
1 parent fb81116 commit 072548f

File tree

14 files changed

+1400
-679
lines changed

14 files changed

+1400
-679
lines changed

pyproject.toml

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -75,23 +75,29 @@ aqc = "qiskit.transpiler.passes.synthesis.aqc_plugin:AQCSynthesisPlugin"
7575
sk = "qiskit.transpiler.passes.synthesis.solovay_kitaev_synthesis:SolovayKitaevSynthesis"
7676

7777
[project.entry-points."qiskit.synthesis"]
78-
"clifford.default" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:DefaultSynthesisClifford"
79-
"clifford.ag" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:AGSynthesisClifford"
80-
"clifford.bm" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:BMSynthesisClifford"
81-
"clifford.greedy" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:GreedySynthesisClifford"
82-
"clifford.layers" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:LayerSynthesisClifford"
83-
"clifford.lnn" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:LayerLnnSynthesisClifford"
84-
"linear_function.default" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:DefaultSynthesisLinearFunction"
85-
"linear_function.kms" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:KMSSynthesisLinearFunction"
86-
"linear_function.pmh" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:PMHSynthesisLinearFunction"
87-
"permutation.default" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:BasicSynthesisPermutation"
88-
"permutation.kms" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:KMSSynthesisPermutation"
89-
"permutation.basic" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:BasicSynthesisPermutation"
90-
"permutation.acg" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:ACGSynthesisPermutation"
91-
"qft.full" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:QFTSynthesisFull"
92-
"qft.line" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:QFTSynthesisLine"
93-
"qft.default" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:QFTSynthesisFull"
94-
"permutation.token_swapper" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:TokenSwapperSynthesisPermutation"
78+
"clifford.default" = "qiskit.transpiler.passes.synthesis.hls_plugins:DefaultSynthesisClifford"
79+
"clifford.ag" = "qiskit.transpiler.passes.synthesis.hls_plugins:AGSynthesisClifford"
80+
"clifford.bm" = "qiskit.transpiler.passes.synthesis.hls_plugins:BMSynthesisClifford"
81+
"clifford.greedy" = "qiskit.transpiler.passes.synthesis.hls_plugins:GreedySynthesisClifford"
82+
"clifford.layers" = "qiskit.transpiler.passes.synthesis.hls_plugins:LayerSynthesisClifford"
83+
"clifford.lnn" = "qiskit.transpiler.passes.synthesis.hls_plugins:LayerLnnSynthesisClifford"
84+
"linear_function.default" = "qiskit.transpiler.passes.synthesis.hls_plugins:DefaultSynthesisLinearFunction"
85+
"linear_function.kms" = "qiskit.transpiler.passes.synthesis.hls_plugins:KMSSynthesisLinearFunction"
86+
"linear_function.pmh" = "qiskit.transpiler.passes.synthesis.hls_plugins:PMHSynthesisLinearFunction"
87+
"mcx.n_dirty_i15" = "qiskit.transpiler.passes.synthesis.hls_plugins:MCXSynthesisNDirtyI15"
88+
"mcx.n_clean_m15" = "qiskit.transpiler.passes.synthesis.hls_plugins:MCXSynthesisNCleanM15"
89+
"mcx.1_clean_b95" = "qiskit.transpiler.passes.synthesis.hls_plugins:MCXSynthesis1CleanB95"
90+
"mcx.gray_code" = "qiskit.transpiler.passes.synthesis.hls_plugins:MCXSynthesisGrayCode"
91+
"mcx.noaux_v24" = "qiskit.transpiler.passes.synthesis.hls_plugins:MCXSynthesisNoAuxV24"
92+
"mcx.default" = "qiskit.transpiler.passes.synthesis.hls_plugins:MCXSynthesisDefault"
93+
"permutation.default" = "qiskit.transpiler.passes.synthesis.hls_plugins:BasicSynthesisPermutation"
94+
"permutation.kms" = "qiskit.transpiler.passes.synthesis.hls_plugins:KMSSynthesisPermutation"
95+
"permutation.basic" = "qiskit.transpiler.passes.synthesis.hls_plugins:BasicSynthesisPermutation"
96+
"permutation.acg" = "qiskit.transpiler.passes.synthesis.hls_plugins:ACGSynthesisPermutation"
97+
"qft.full" = "qiskit.transpiler.passes.synthesis.hls_plugins:QFTSynthesisFull"
98+
"qft.line" = "qiskit.transpiler.passes.synthesis.hls_plugins:QFTSynthesisLine"
99+
"qft.default" = "qiskit.transpiler.passes.synthesis.hls_plugins:QFTSynthesisFull"
100+
"permutation.token_swapper" = "qiskit.transpiler.passes.synthesis.hls_plugins:TokenSwapperSynthesisPermutation"
95101

96102
[project.entry-points."qiskit.transpiler.init"]
97103
default = "qiskit.transpiler.preset_passmanagers.builtin_plugins:DefaultInitPassManager"

qiskit/circuit/library/standard_gates/x.py

Lines changed: 53 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from qiskit.circuit.quantumregister import QuantumRegister
2121
from qiskit.circuit._utils import _ctrl_state_to_int, with_gate_array, with_controlled_gate_array
2222
from qiskit._accelerate.circuit import StandardGate
23+
from qiskit.utils.deprecation import deprecate_func
2324

2425
_X_ARRAY = [[0, 1], [1, 0]]
2526
_SX_ARRAY = [[0.5 + 0.5j, 0.5 - 0.5j], [0.5 - 0.5j, 0.5 + 0.5j]]
@@ -1168,6 +1169,19 @@ def inverse(self, annotated: bool = False):
11681169
return MCXGate(num_ctrl_qubits=self.num_ctrl_qubits, ctrl_state=self.ctrl_state)
11691170

11701171
@staticmethod
1172+
@deprecate_func(
1173+
additional_msg=(
1174+
"For an MCXGate it is no longer possible to know the number of ancilla qubits "
1175+
"that would be eventually used by the transpiler when the gate is created. "
1176+
"Instead, it is recommended to use MCXGate and let HighLevelSynthesis choose "
1177+
"the best synthesis method depending on the number of ancilla qubits available. "
1178+
"However, if a specific synthesis method using a specific number of ancilla "
1179+
"qubits is require, one can create a custom gate by calling the corresponding "
1180+
"synthesis function directly."
1181+
),
1182+
since="1.3",
1183+
pending=True,
1184+
)
11711185
def get_num_ancilla_qubits(num_ctrl_qubits: int, mode: str = "noancilla") -> int:
11721186
"""Get the number of required ancilla qubits without instantiating the class.
11731187
@@ -1185,23 +1199,10 @@ def get_num_ancilla_qubits(num_ctrl_qubits: int, mode: str = "noancilla") -> int
11851199
def _define(self):
11861200
"""This definition is based on MCPhaseGate implementation."""
11871201
# pylint: disable=cyclic-import
1188-
from qiskit.circuit.quantumcircuit import QuantumCircuit
1202+
from qiskit.synthesis.multi_controlled import synth_mcx_noaux_v24
11891203

1190-
q = QuantumRegister(self.num_qubits, name="q")
1191-
qc = QuantumCircuit(q)
1192-
if self.num_qubits == 4:
1193-
qc._append(C3XGate(), q[:], [])
1194-
self.definition = qc
1195-
elif self.num_qubits == 5:
1196-
qc._append(C4XGate(), q[:], [])
1197-
self.definition = qc
1198-
else:
1199-
q_controls = list(range(self.num_ctrl_qubits))
1200-
q_target = self.num_ctrl_qubits
1201-
qc.h(q_target)
1202-
qc.mcp(numpy.pi, q_controls, q_target)
1203-
qc.h(q_target)
1204-
self.definition = qc
1204+
qc = synth_mcx_noaux_v24(self.num_ctrl_qubits)
1205+
self.definition = qc
12051206

12061207
@property
12071208
def num_ancilla_qubits(self):
@@ -1280,6 +1281,17 @@ def __new__(
12801281
return gate
12811282
return super().__new__(cls)
12821283

1284+
@deprecate_func(
1285+
additional_msg=(
1286+
"It is recommended to use MCXGate and let HighLevelSynthesis choose "
1287+
"the best synthesis method depending on the number of ancilla qubits available. "
1288+
"If this specific synthesis method is required, one can specify it using the "
1289+
"high-level-synthesis plugin `gray_code` for MCX gates, or, alternatively, "
1290+
"one can use synth_mcx_gray_code to construct the gate directly."
1291+
),
1292+
since="1.3",
1293+
pending=True,
1294+
)
12831295
def __init__(
12841296
self,
12851297
num_ctrl_qubits: int,
@@ -1305,15 +1317,9 @@ def inverse(self, annotated: bool = False):
13051317
def _define(self):
13061318
"""Define the MCX gate using the Gray code."""
13071319
# pylint: disable=cyclic-import
1308-
from qiskit.circuit.quantumcircuit import QuantumCircuit
1309-
from .u1 import MCU1Gate
1310-
from .h import HGate
1320+
from qiskit.synthesis.multi_controlled import synth_mcx_gray_code
13111321

1312-
q = QuantumRegister(self.num_qubits, name="q")
1313-
qc = QuantumCircuit(q, name=self.name)
1314-
qc._append(HGate(), [q[-1]], [])
1315-
qc._append(MCU1Gate(numpy.pi, num_ctrl_qubits=self.num_ctrl_qubits), q[:], [])
1316-
qc._append(HGate(), [q[-1]], [])
1322+
qc = synth_mcx_gray_code(self.num_ctrl_qubits)
13171323
self.definition = qc
13181324

13191325

@@ -1330,6 +1336,17 @@ class MCXRecursive(MCXGate):
13301336
2. Iten et al., 2015. https://arxiv.org/abs/1501.06911
13311337
"""
13321338

1339+
@deprecate_func(
1340+
additional_msg=(
1341+
"It is recommended to use MCXGate and let HighLevelSynthesis choose "
1342+
"the best synthesis method depending on the number of ancilla qubits available. "
1343+
"If this specific synthesis method is required, one can specify it using the "
1344+
"high-level-synthesis plugin '1_clean_b95' for MCX gates, or, alternatively, "
1345+
"one can use synth_mcx_1_clean to construct the gate directly."
1346+
),
1347+
since="1.3",
1348+
pending=True,
1349+
)
13331350
def __init__(
13341351
self,
13351352
num_ctrl_qubits: int,
@@ -1409,6 +1426,18 @@ def __new__(
14091426
unit=unit,
14101427
)
14111428

1429+
@deprecate_func(
1430+
additional_msg=(
1431+
"It is recommended to use MCXGate and let HighLevelSynthesis choose "
1432+
"the best synthesis method depending on the number of ancilla qubits available. "
1433+
"If this specific synthesis method is required, one can specify it using the "
1434+
"high-level-synthesis plugins `n_clean_m15` (using clean ancillas) or "
1435+
"`n_dirty_i15` (using dirty ancillas) for MCX gates. Alternatively, one can "
1436+
"use synth_mcx_n_dirty_i15 and synth_mcx_n_clean_m15 to construct the gate directly."
1437+
),
1438+
since="1.3",
1439+
pending=True,
1440+
)
14121441
def __init__(
14131442
self,
14141443
num_ctrl_qubits: int,

qiskit/circuit/quantumcircuit.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3212,10 +3212,17 @@ def decompose(
32123212
from qiskit.converters.dag_to_circuit import dag_to_circuit
32133213

32143214
dag = circuit_to_dag(self, copy_operations=True)
3215-
dag = HighLevelSynthesis().run(dag)
3215+
3216+
if gates_to_decompose is None:
3217+
# We should not rewrite the circuit using HLS when we have gates_to_decompose,
3218+
# or else HLS will rewrite all objects with available plugins (e.g., Cliffords,
3219+
# PermutationGates, and now also MCXGates)
3220+
dag = HighLevelSynthesis().run(dag)
3221+
32163222
pass_ = Decompose(gates_to_decompose)
32173223
for _ in range(reps):
32183224
dag = pass_.run(dag)
3225+
32193226
# do not copy operations, this is done in the conversion with circuit_to_dag
32203227
return dag_to_circuit(dag, copy_operations=False)
32213228

qiskit/synthesis/__init__.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,10 @@
128128
.. autofunction:: synth_mcx_n_dirty_i15
129129
.. autofunction:: synth_mcx_n_clean_m15
130130
.. autofunction:: synth_mcx_1_clean_b95
131+
.. autofunction:: synth_mcx_noaux_v24
132+
.. autofunction:: synth_mcx_gray_code
133+
.. autofunction:: synth_c3x
134+
.. autofunction:: synth_c4x
131135
132136
"""
133137

@@ -180,8 +184,12 @@
180184
two_qubit_cnot_decompose,
181185
TwoQubitWeylDecomposition,
182186
)
183-
from .multi_controlled.mcx_with_ancillas_synth import (
187+
from .multi_controlled import (
184188
synth_mcx_n_dirty_i15,
185189
synth_mcx_n_clean_m15,
186190
synth_mcx_1_clean_b95,
191+
synth_mcx_noaux_v24,
192+
synth_mcx_gray_code,
193+
synth_c3x,
194+
synth_c4x,
187195
)

qiskit/synthesis/multi_controlled/__init__.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,12 @@
1212

1313
"""Module containing multi-controlled circuits synthesis"""
1414

15-
from .mcx_with_ancillas_synth import (
15+
from .mcx_synthesis import (
1616
synth_mcx_n_dirty_i15,
1717
synth_mcx_n_clean_m15,
1818
synth_mcx_1_clean_b95,
19+
synth_mcx_gray_code,
20+
synth_mcx_noaux_v24,
21+
synth_c3x,
22+
synth_c4x,
1923
)

0 commit comments

Comments
 (0)