qiskit 2.0.2__cp39-abi3-win_amd64.whl → 2.1.0rc1__cp39-abi3-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- qiskit/VERSION.txt +1 -1
- qiskit/__init__.py +19 -1
- qiskit/_accelerate.pyd +0 -0
- qiskit/circuit/__init__.py +13 -21
- qiskit/circuit/_add_control.py +57 -31
- qiskit/circuit/_classical_resource_map.py +4 -0
- qiskit/circuit/annotation.py +404 -0
- qiskit/circuit/classical/expr/__init__.py +1 -1
- qiskit/circuit/classical/expr/expr.py +104 -446
- qiskit/circuit/classical/expr/visitors.py +6 -0
- qiskit/circuit/classical/types/types.py +7 -130
- qiskit/circuit/controlflow/box.py +32 -7
- qiskit/circuit/delay.py +11 -9
- qiskit/circuit/library/arithmetic/adders/adder.py +5 -5
- qiskit/circuit/library/arithmetic/multipliers/multiplier.py +3 -3
- qiskit/circuit/library/arithmetic/piecewise_chebyshev.py +7 -3
- qiskit/circuit/library/arithmetic/piecewise_linear_pauli_rotations.py +23 -15
- qiskit/circuit/library/arithmetic/piecewise_polynomial_pauli_rotations.py +22 -14
- qiskit/circuit/library/arithmetic/quadratic_form.py +6 -0
- qiskit/circuit/library/arithmetic/weighted_adder.py +43 -24
- qiskit/circuit/library/basis_change/qft.py +2 -2
- qiskit/circuit/library/blueprintcircuit.py +6 -0
- qiskit/circuit/library/boolean_logic/inner_product.py +2 -2
- qiskit/circuit/library/boolean_logic/quantum_and.py +2 -2
- qiskit/circuit/library/boolean_logic/quantum_or.py +3 -3
- qiskit/circuit/library/boolean_logic/quantum_xor.py +2 -2
- qiskit/circuit/library/data_preparation/_z_feature_map.py +2 -2
- qiskit/circuit/library/data_preparation/_zz_feature_map.py +2 -2
- qiskit/circuit/library/data_preparation/pauli_feature_map.py +2 -2
- qiskit/circuit/library/fourier_checking.py +2 -2
- qiskit/circuit/library/generalized_gates/diagonal.py +5 -1
- qiskit/circuit/library/generalized_gates/gms.py +5 -1
- qiskit/circuit/library/generalized_gates/linear_function.py +2 -2
- qiskit/circuit/library/generalized_gates/permutation.py +5 -1
- qiskit/circuit/library/generalized_gates/uc.py +1 -1
- qiskit/circuit/library/generalized_gates/unitary.py +21 -2
- qiskit/circuit/library/graph_state.py +2 -2
- qiskit/circuit/library/grover_operator.py +2 -2
- qiskit/circuit/library/hidden_linear_function.py +2 -2
- qiskit/circuit/library/iqp.py +2 -2
- qiskit/circuit/library/n_local/efficient_su2.py +2 -2
- qiskit/circuit/library/n_local/evolved_operator_ansatz.py +1 -1
- qiskit/circuit/library/n_local/excitation_preserving.py +7 -9
- qiskit/circuit/library/n_local/n_local.py +4 -3
- qiskit/circuit/library/n_local/pauli_two_design.py +2 -2
- qiskit/circuit/library/n_local/real_amplitudes.py +2 -2
- qiskit/circuit/library/n_local/two_local.py +2 -2
- qiskit/circuit/library/overlap.py +2 -2
- qiskit/circuit/library/pauli_evolution.py +3 -2
- qiskit/circuit/library/phase_estimation.py +2 -2
- qiskit/circuit/library/standard_gates/dcx.py +11 -12
- qiskit/circuit/library/standard_gates/ecr.py +21 -24
- qiskit/circuit/library/standard_gates/equivalence_library.py +232 -96
- qiskit/circuit/library/standard_gates/global_phase.py +5 -6
- qiskit/circuit/library/standard_gates/h.py +22 -45
- qiskit/circuit/library/standard_gates/i.py +1 -1
- qiskit/circuit/library/standard_gates/iswap.py +13 -31
- qiskit/circuit/library/standard_gates/p.py +19 -26
- qiskit/circuit/library/standard_gates/r.py +11 -17
- qiskit/circuit/library/standard_gates/rx.py +21 -45
- qiskit/circuit/library/standard_gates/rxx.py +7 -22
- qiskit/circuit/library/standard_gates/ry.py +21 -39
- qiskit/circuit/library/standard_gates/ryy.py +13 -28
- qiskit/circuit/library/standard_gates/rz.py +18 -35
- qiskit/circuit/library/standard_gates/rzx.py +7 -22
- qiskit/circuit/library/standard_gates/rzz.py +7 -19
- qiskit/circuit/library/standard_gates/s.py +44 -39
- qiskit/circuit/library/standard_gates/swap.py +25 -38
- qiskit/circuit/library/standard_gates/sx.py +34 -41
- qiskit/circuit/library/standard_gates/t.py +18 -27
- qiskit/circuit/library/standard_gates/u.py +8 -24
- qiskit/circuit/library/standard_gates/u1.py +28 -52
- qiskit/circuit/library/standard_gates/u2.py +9 -9
- qiskit/circuit/library/standard_gates/u3.py +24 -40
- qiskit/circuit/library/standard_gates/x.py +190 -336
- qiskit/circuit/library/standard_gates/xx_minus_yy.py +12 -50
- qiskit/circuit/library/standard_gates/xx_plus_yy.py +13 -52
- qiskit/circuit/library/standard_gates/y.py +19 -23
- qiskit/circuit/library/standard_gates/z.py +31 -38
- qiskit/circuit/parameter.py +14 -5
- qiskit/circuit/parameterexpression.py +109 -75
- qiskit/circuit/quantumcircuit.py +168 -98
- qiskit/circuit/quantumcircuitdata.py +1 -0
- qiskit/circuit/random/__init__.py +37 -2
- qiskit/circuit/random/utils.py +445 -56
- qiskit/circuit/tools/pi_check.py +5 -13
- qiskit/compiler/transpiler.py +1 -1
- qiskit/converters/circuit_to_instruction.py +2 -2
- qiskit/dagcircuit/dagnode.py +8 -3
- qiskit/primitives/__init__.py +2 -2
- qiskit/primitives/base/base_estimator.py +2 -2
- qiskit/primitives/containers/data_bin.py +0 -3
- qiskit/primitives/containers/observables_array.py +192 -108
- qiskit/primitives/primitive_job.py +29 -10
- qiskit/providers/fake_provider/generic_backend_v2.py +2 -0
- qiskit/qasm3/__init__.py +106 -12
- qiskit/qasm3/ast.py +15 -1
- qiskit/qasm3/exporter.py +59 -36
- qiskit/qasm3/printer.py +12 -0
- qiskit/qpy/__init__.py +183 -7
- qiskit/qpy/binary_io/circuits.py +256 -24
- qiskit/qpy/binary_io/parse_sympy_repr.py +5 -0
- qiskit/qpy/binary_io/schedules.py +12 -32
- qiskit/qpy/binary_io/value.py +36 -18
- qiskit/qpy/common.py +11 -3
- qiskit/qpy/formats.py +17 -1
- qiskit/qpy/interface.py +52 -12
- qiskit/qpy/type_keys.py +7 -1
- qiskit/quantum_info/__init__.py +10 -0
- qiskit/quantum_info/operators/__init__.py +1 -0
- qiskit/quantum_info/operators/symplectic/__init__.py +1 -0
- qiskit/quantum_info/operators/symplectic/clifford_circuits.py +26 -0
- qiskit/quantum_info/operators/symplectic/pauli.py +2 -2
- qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py +1 -1
- qiskit/result/sampled_expval.py +3 -1
- qiskit/synthesis/__init__.py +10 -0
- qiskit/synthesis/arithmetic/__init__.py +1 -1
- qiskit/synthesis/arithmetic/adders/__init__.py +1 -0
- qiskit/synthesis/arithmetic/adders/draper_qft_adder.py +6 -2
- qiskit/synthesis/arithmetic/adders/rv_ripple_carry_adder.py +156 -0
- qiskit/synthesis/discrete_basis/generate_basis_approximations.py +14 -126
- qiskit/synthesis/discrete_basis/solovay_kitaev.py +161 -121
- qiskit/synthesis/evolution/lie_trotter.py +10 -7
- qiskit/synthesis/evolution/product_formula.py +10 -7
- qiskit/synthesis/evolution/qdrift.py +10 -7
- qiskit/synthesis/evolution/suzuki_trotter.py +10 -7
- qiskit/synthesis/multi_controlled/__init__.py +4 -0
- qiskit/synthesis/multi_controlled/mcx_synthesis.py +402 -178
- qiskit/synthesis/multi_controlled/multi_control_rotation_gates.py +14 -15
- qiskit/synthesis/qft/qft_decompose_lnn.py +7 -25
- qiskit/synthesis/unitary/qsd.py +80 -9
- qiskit/transpiler/__init__.py +19 -8
- qiskit/transpiler/instruction_durations.py +2 -20
- qiskit/transpiler/passes/__init__.py +4 -2
- qiskit/transpiler/passes/layout/dense_layout.py +26 -6
- qiskit/transpiler/passes/layout/disjoint_utils.py +1 -166
- qiskit/transpiler/passes/layout/sabre_layout.py +22 -3
- qiskit/transpiler/passes/layout/sabre_pre_layout.py +1 -1
- qiskit/transpiler/passes/layout/vf2_layout.py +49 -13
- qiskit/transpiler/passes/layout/vf2_utils.py +13 -1
- qiskit/transpiler/passes/optimization/__init__.py +1 -1
- qiskit/transpiler/passes/optimization/optimize_1q_decomposition.py +2 -1
- qiskit/transpiler/passes/optimization/optimize_clifford_t.py +68 -0
- qiskit/transpiler/passes/optimization/template_matching/template_substitution.py +3 -9
- qiskit/transpiler/passes/routing/sabre_swap.py +12 -2
- qiskit/transpiler/passes/routing/star_prerouting.py +106 -81
- qiskit/transpiler/passes/scheduling/__init__.py +1 -1
- qiskit/transpiler/passes/scheduling/alignments/check_durations.py +1 -1
- qiskit/transpiler/passes/scheduling/padding/__init__.py +1 -0
- qiskit/transpiler/passes/scheduling/padding/context_aware_dynamical_decoupling.py +876 -0
- qiskit/transpiler/passes/synthesis/__init__.py +1 -0
- qiskit/transpiler/passes/synthesis/clifford_unitary_synth_plugin.py +123 -0
- qiskit/transpiler/passes/synthesis/hls_plugins.py +472 -92
- qiskit/transpiler/passes/synthesis/solovay_kitaev_synthesis.py +27 -22
- qiskit/transpiler/passmanager_config.py +3 -0
- qiskit/transpiler/preset_passmanagers/builtin_plugins.py +149 -28
- qiskit/transpiler/preset_passmanagers/common.py +101 -0
- qiskit/transpiler/preset_passmanagers/generate_preset_pass_manager.py +6 -0
- qiskit/transpiler/preset_passmanagers/level3.py +2 -2
- qiskit/utils/optionals.py +6 -5
- qiskit/visualization/circuit/_utils.py +5 -3
- qiskit/visualization/circuit/latex.py +9 -2
- qiskit/visualization/circuit/matplotlib.py +26 -4
- qiskit/visualization/circuit/qcstyle.py +9 -157
- qiskit/visualization/dag/__init__.py +13 -0
- qiskit/visualization/dag/dagstyle.py +103 -0
- qiskit/visualization/dag/styles/__init__.py +13 -0
- qiskit/visualization/dag/styles/color.json +10 -0
- qiskit/visualization/dag/styles/plain.json +5 -0
- qiskit/visualization/dag_visualization.py +169 -98
- qiskit/visualization/style.py +223 -0
- {qiskit-2.0.2.dist-info → qiskit-2.1.0rc1.dist-info}/METADATA +14 -13
- {qiskit-2.0.2.dist-info → qiskit-2.1.0rc1.dist-info}/RECORD +177 -168
- {qiskit-2.0.2.dist-info → qiskit-2.1.0rc1.dist-info}/entry_points.txt +6 -0
- qiskit/synthesis/discrete_basis/commutator_decompose.py +0 -265
- qiskit/synthesis/discrete_basis/gate_sequence.py +0 -421
- {qiskit-2.0.2.dist-info → qiskit-2.1.0rc1.dist-info}/WHEEL +0 -0
- {qiskit-2.0.2.dist-info → qiskit-2.1.0rc1.dist-info}/licenses/LICENSE.txt +0 -0
- {qiskit-2.0.2.dist-info → qiskit-2.1.0rc1.dist-info}/top_level.txt +0 -0
@@ -194,6 +194,26 @@ not sufficient, the corresponding synthesis method will return `None`.
|
|
194
194
|
- `0`
|
195
195
|
- `k-2`
|
196
196
|
- at most `8*k-6` CX gates
|
197
|
+
* - ``"2_clean_kg24"``
|
198
|
+
- :class:`~.MCXSynthesis2CleanKG24`
|
199
|
+
- `2`
|
200
|
+
- `0`
|
201
|
+
- at most `12*k-18` CX gates
|
202
|
+
* - ``"2_dirty_kg24"``
|
203
|
+
- :class:`~.MCXSynthesis2DirtyKG24`
|
204
|
+
- `0`
|
205
|
+
- `2`
|
206
|
+
- at most `24*k-48` CX gates
|
207
|
+
* - ``"1_clean_kg24"``
|
208
|
+
- :class:`~.MCXSynthesis1CleanKG24`
|
209
|
+
- `1`
|
210
|
+
- `0`
|
211
|
+
- at most `12*k-18` CX gates
|
212
|
+
* - ``"1_dirty_kg24"``
|
213
|
+
- :class:`~.MCXSynthesis1DirtyKG24`
|
214
|
+
- `0`
|
215
|
+
- `1`
|
216
|
+
- at most `24*k-48` CX gates
|
197
217
|
* - ``"1_clean_b95"``
|
198
218
|
- :class:`~.MCXSynthesis1CleanB95`
|
199
219
|
- `1`
|
@@ -212,6 +232,10 @@ not sufficient, the corresponding synthesis method will return `None`.
|
|
212
232
|
MCXSynthesisNoAuxV24
|
213
233
|
MCXSynthesisNCleanM15
|
214
234
|
MCXSynthesisNDirtyI15
|
235
|
+
MCXSynthesis2CleanKG24
|
236
|
+
MCXSynthesis2DirtyKG24
|
237
|
+
MCXSynthesis1CleanKG24
|
238
|
+
MCXSynthesis1DirtyKG24
|
215
239
|
MCXSynthesis1CleanB95
|
216
240
|
MCXSynthesisDefault
|
217
241
|
|
@@ -379,6 +403,10 @@ Half Adder Synthesis
|
|
379
403
|
- :class:`.HalfAdderSynthesisC04`
|
380
404
|
- 1
|
381
405
|
- a ripple-carry adder
|
406
|
+
* - ``"ripple_r25"``
|
407
|
+
- :class:`.HalfAdderSynthesisR25`
|
408
|
+
- 0
|
409
|
+
- a ripple-carry adder with no ancillas
|
382
410
|
* - ``"ripple_vbe"``
|
383
411
|
- :class:`.HalfAdderSynthesisV95`
|
384
412
|
- :math:`n-1`, for :math:`n`-bit numbers
|
@@ -398,6 +426,7 @@ Half Adder Synthesis
|
|
398
426
|
HalfAdderSynthesisC04
|
399
427
|
HalfAdderSynthesisD00
|
400
428
|
HalfAdderSynthesisV95
|
429
|
+
HalfAdderSynthesisR25
|
401
430
|
HalfAdderSynthesisDefault
|
402
431
|
|
403
432
|
Full Adder Synthesis
|
@@ -488,6 +517,7 @@ from qiskit.circuit.annotated_operation import (
|
|
488
517
|
ControlModifier,
|
489
518
|
InverseModifier,
|
490
519
|
PowerModifier,
|
520
|
+
_canonicalize_modifiers,
|
491
521
|
)
|
492
522
|
from qiskit.transpiler.coupling import CouplingMap
|
493
523
|
|
@@ -521,7 +551,11 @@ from qiskit.synthesis.qft import (
|
|
521
551
|
)
|
522
552
|
from qiskit.synthesis.multi_controlled import (
|
523
553
|
synth_mcx_n_dirty_i15,
|
554
|
+
synth_mcx_2_dirty_kg24,
|
555
|
+
synth_mcx_1_dirty_kg24,
|
524
556
|
synth_mcx_n_clean_m15,
|
557
|
+
synth_mcx_2_clean_kg24,
|
558
|
+
synth_mcx_1_clean_kg24,
|
525
559
|
synth_mcx_1_clean_b95,
|
526
560
|
synth_mcx_gray_code,
|
527
561
|
synth_mcx_noaux_v24,
|
@@ -532,14 +566,16 @@ from qiskit.synthesis.arithmetic import (
|
|
532
566
|
adder_ripple_c04,
|
533
567
|
adder_qft_d00,
|
534
568
|
adder_ripple_v95,
|
569
|
+
adder_ripple_r25,
|
535
570
|
multiplier_qft_r17,
|
536
571
|
multiplier_cumulative_h18,
|
537
572
|
)
|
538
573
|
from qiskit.quantum_info.operators import Clifford
|
539
574
|
from qiskit.transpiler.passes.routing.algorithms import ApproximateTokenSwapper
|
540
575
|
from qiskit.transpiler.exceptions import TranspilerError
|
576
|
+
from qiskit.circuit._add_control import EFFICIENTLY_CONTROLLED_GATES
|
541
577
|
|
542
|
-
from qiskit._accelerate.high_level_synthesis import synthesize_operation
|
578
|
+
from qiskit._accelerate.high_level_synthesis import synthesize_operation, HighLevelSynthesisData
|
543
579
|
from .plugin import HighLevelSynthesisPlugin
|
544
580
|
|
545
581
|
|
@@ -1008,9 +1044,10 @@ class MCXSynthesisNDirtyI15(HighLevelSynthesisPlugin):
|
|
1008
1044
|
This plugin name is :``mcx.n_dirty_i15`` which can be used as the key on
|
1009
1045
|
an :class:`~.HLSConfig` object to use this method with :class:`~.HighLevelSynthesis`.
|
1010
1046
|
|
1011
|
-
For a multi-controlled X gate with :math:`k\ge
|
1047
|
+
For a multi-controlled X gate with :math:`k\ge 4` control qubits this synthesis
|
1012
1048
|
method requires :math:`k - 2` additional dirty auxiliary qubits. The synthesized
|
1013
1049
|
circuit consists of :math:`2 * k - 1` qubits and at most :math:`8 * k - 6` CX gates.
|
1050
|
+
For :math:`k\le 3` explicit efficient circuits are used instead.
|
1014
1051
|
|
1015
1052
|
The plugin supports the following plugin-specific options:
|
1016
1053
|
|
@@ -1105,14 +1142,14 @@ class MCXSynthesis1CleanB95(HighLevelSynthesisPlugin):
|
|
1105
1142
|
|
1106
1143
|
For a multi-controlled X gate with :math:`k\ge 5` control qubits this synthesis
|
1107
1144
|
method requires a single additional clean auxiliary qubit. The synthesized
|
1108
|
-
circuit consists of :math:`k + 2` qubits and at most :math:`16 * k -
|
1145
|
+
circuit consists of :math:`k + 2` qubits and at most :math:`16 * k - 24` CX gates.
|
1109
1146
|
|
1110
1147
|
The plugin supports the following plugin-specific options:
|
1111
1148
|
|
1112
1149
|
* num_clean_ancillas: The number of clean auxiliary qubits available.
|
1113
1150
|
|
1114
1151
|
References:
|
1115
|
-
1. Barenco et. al., Phys.Rev. A52 3457 (1995),
|
1152
|
+
1. Barenco et. al., *Elementary gates for quantum computation*, Phys.Rev. A52 3457 (1995),
|
1116
1153
|
`arXiv:quant-ph/9503016 <https://arxiv.org/abs/quant-ph/9503016>`_
|
1117
1154
|
"""
|
1118
1155
|
|
@@ -1142,6 +1179,174 @@ class MCXSynthesis1CleanB95(HighLevelSynthesisPlugin):
|
|
1142
1179
|
return decomposition
|
1143
1180
|
|
1144
1181
|
|
1182
|
+
class MCXSynthesis2CleanKG24(HighLevelSynthesisPlugin):
|
1183
|
+
r"""Synthesis plugin for a multi-controlled X gate based on the paper by Khattar and
|
1184
|
+
Gidney (2024).
|
1185
|
+
|
1186
|
+
See [1] for details.
|
1187
|
+
|
1188
|
+
The plugin name is :``mcx.2_clean_kg24`` which can be used as the key on an :class:`~.HLSConfig`
|
1189
|
+
object to use this method with :class:`~.HighLevelSynthesis`.
|
1190
|
+
|
1191
|
+
For a multi-controlled X gate with :math:`k\ge 3` control qubits this synthesis method requires
|
1192
|
+
:math:`2` additional clean ancillary qubits. The synthesized circuit consists of :math:`k + 2`
|
1193
|
+
qubits and at most :math:`12 * k - 18` CX gates.
|
1194
|
+
|
1195
|
+
The plugin supports the following plugin-specific options:
|
1196
|
+
|
1197
|
+
* num_clean_ancillas: The number of clean ancillary qubits available.
|
1198
|
+
|
1199
|
+
References:
|
1200
|
+
1. Khattar and Gidney, Rise of conditionally clean ancillae for optimizing quantum circuits
|
1201
|
+
`arXiv:2407.17966 <https://arxiv.org/abs/2407.17966>`__
|
1202
|
+
"""
|
1203
|
+
|
1204
|
+
def run(self, high_level_object, coupling_map=None, target=None, qubits=None, **options):
|
1205
|
+
"""Run synthesis for the given MCX gate."""
|
1206
|
+
|
1207
|
+
if not isinstance(high_level_object, (MCXGate, C3XGate, C4XGate)):
|
1208
|
+
# Unfortunately we occasionally have custom instructions called "mcx"
|
1209
|
+
# which get wrongly caught by the plugin interface. A simple solution is
|
1210
|
+
# to return None in this case, since HLS would proceed to examine
|
1211
|
+
# their definition as it should.
|
1212
|
+
return None
|
1213
|
+
|
1214
|
+
num_ctrl_qubits = high_level_object.num_ctrl_qubits
|
1215
|
+
num_clean_ancillas = options.get("num_clean_ancillas", 0)
|
1216
|
+
|
1217
|
+
if num_clean_ancillas < 2:
|
1218
|
+
return None
|
1219
|
+
|
1220
|
+
decomposition = synth_mcx_2_clean_kg24(num_ctrl_qubits)
|
1221
|
+
return decomposition
|
1222
|
+
|
1223
|
+
|
1224
|
+
class MCXSynthesis2DirtyKG24(HighLevelSynthesisPlugin):
|
1225
|
+
r"""Synthesis plugin for a multi-controlled X gate based on the paper by Khattar and
|
1226
|
+
Gidney (2024).
|
1227
|
+
|
1228
|
+
See [1] for details.
|
1229
|
+
|
1230
|
+
The plugin name is :``mcx.2_dirty_kg24`` which can be used as the key on an :class:`~.HLSConfig`
|
1231
|
+
object to use this method with :class:`~.HighLevelSynthesis`.
|
1232
|
+
|
1233
|
+
For a multi-controlled X gate with :math:`k\ge 3` control qubits this synthesis method requires
|
1234
|
+
:math:`2` additional dirty ancillary qubits. The synthesized circuit consists of :math:`k + 2`
|
1235
|
+
qubits and at most :math:`24 * k - 48` CX gates.
|
1236
|
+
|
1237
|
+
The plugin supports the following plugin-specific options:
|
1238
|
+
|
1239
|
+
* num_clean_ancillas: The number of clean ancillary qubits available.
|
1240
|
+
|
1241
|
+
References:
|
1242
|
+
1. Khattar and Gidney, Rise of conditionally clean ancillae for optimizing quantum circuits
|
1243
|
+
`arXiv:2407.17966 <https://arxiv.org/abs/2407.17966>`__
|
1244
|
+
"""
|
1245
|
+
|
1246
|
+
def run(self, high_level_object, coupling_map=None, target=None, qubits=None, **options):
|
1247
|
+
"""Run synthesis for the given MCX gate."""
|
1248
|
+
|
1249
|
+
if not isinstance(high_level_object, (MCXGate, C3XGate, C4XGate)):
|
1250
|
+
# Unfortunately we occasionally have custom instructions called "mcx"
|
1251
|
+
# which get wrongly caught by the plugin interface. A simple solution is
|
1252
|
+
# to return None in this case, since HLS would proceed to examine
|
1253
|
+
# their definition as it should.
|
1254
|
+
return None
|
1255
|
+
|
1256
|
+
num_ctrl_qubits = high_level_object.num_ctrl_qubits
|
1257
|
+
num_dirty_ancillas = options.get("num_dirty_ancillas", 0)
|
1258
|
+
|
1259
|
+
if num_dirty_ancillas < 2:
|
1260
|
+
return None
|
1261
|
+
|
1262
|
+
decomposition = synth_mcx_2_dirty_kg24(num_ctrl_qubits)
|
1263
|
+
return decomposition
|
1264
|
+
|
1265
|
+
|
1266
|
+
class MCXSynthesis1CleanKG24(HighLevelSynthesisPlugin):
|
1267
|
+
r"""Synthesis plugin for a multi-controlled X gate based on the paper by Khattar and
|
1268
|
+
Gidney (2024).
|
1269
|
+
|
1270
|
+
See [1] for details.
|
1271
|
+
|
1272
|
+
The plugin name is :``mcx.1_clean_kg24`` which can be used as the key on an :class:`~.HLSConfig`
|
1273
|
+
object to use this method with :class:`~.HighLevelSynthesis`.
|
1274
|
+
|
1275
|
+
For a multi-controlled X gate with :math:`k\ge 3` control qubits this synthesis method requires
|
1276
|
+
:math:`1` additional clean ancillary qubit. The synthesized circuit consists of :math:`k + 2`
|
1277
|
+
qubits and at most :math:`12 * k - 18` CX gates.
|
1278
|
+
|
1279
|
+
The plugin supports the following plugin-specific options:
|
1280
|
+
|
1281
|
+
* num_clean_ancillas: The number of clean ancillary qubits available.
|
1282
|
+
|
1283
|
+
References:
|
1284
|
+
1. Khattar and Gidney, Rise of conditionally clean ancillae for optimizing quantum circuits
|
1285
|
+
`arXiv:2407.17966 <https://arxiv.org/abs/2407.17966>`__
|
1286
|
+
"""
|
1287
|
+
|
1288
|
+
def run(self, high_level_object, coupling_map=None, target=None, qubits=None, **options):
|
1289
|
+
"""Run synthesis for the given MCX gate."""
|
1290
|
+
|
1291
|
+
if not isinstance(high_level_object, (MCXGate, C3XGate, C4XGate)):
|
1292
|
+
# Unfortunately we occasionally have custom instructions called "mcx"
|
1293
|
+
# which get wrongly caught by the plugin interface. A simple solution is
|
1294
|
+
# to return None in this case, since HLS would proceed to examine
|
1295
|
+
# their definition as it should.
|
1296
|
+
return None
|
1297
|
+
|
1298
|
+
num_ctrl_qubits = high_level_object.num_ctrl_qubits
|
1299
|
+
num_clean_ancillas = options.get("num_clean_ancillas", 0)
|
1300
|
+
|
1301
|
+
if num_clean_ancillas < 1:
|
1302
|
+
return None
|
1303
|
+
|
1304
|
+
decomposition = synth_mcx_1_clean_kg24(num_ctrl_qubits)
|
1305
|
+
return decomposition
|
1306
|
+
|
1307
|
+
|
1308
|
+
class MCXSynthesis1DirtyKG24(HighLevelSynthesisPlugin):
|
1309
|
+
r"""Synthesis plugin for a multi-controlled X gate based on the paper by Khattar and
|
1310
|
+
Gidney (2024).
|
1311
|
+
|
1312
|
+
See [1] for details.
|
1313
|
+
|
1314
|
+
The plugin name is :``mcx.1_dirty_kg24`` which can be used as the key on an :class:`~.HLSConfig`
|
1315
|
+
object to use this method with :class:`~.HighLevelSynthesis`.
|
1316
|
+
|
1317
|
+
For a multi-controlled X gate with :math:`k\ge 3` control qubits this synthesis method requires
|
1318
|
+
:math:`1` additional dirty ancillary qubit. The synthesized circuit consists of :math:`k + 2`
|
1319
|
+
qubits and at most :math:`24 * k - 48` CX gates.
|
1320
|
+
|
1321
|
+
The plugin supports the following plugin-specific options:
|
1322
|
+
|
1323
|
+
* num_clean_ancillas: The number of clean ancillary qubits available.
|
1324
|
+
|
1325
|
+
References:
|
1326
|
+
1. Khattar and Gidney, Rise of conditionally clean ancillae for optimizing quantum circuits
|
1327
|
+
`arXiv:2407.17966 <https://arxiv.org/abs/2407.17966>`__
|
1328
|
+
"""
|
1329
|
+
|
1330
|
+
def run(self, high_level_object, coupling_map=None, target=None, qubits=None, **options):
|
1331
|
+
"""Run synthesis for the given MCX gate."""
|
1332
|
+
|
1333
|
+
if not isinstance(high_level_object, (MCXGate, C3XGate, C4XGate)):
|
1334
|
+
# Unfortunately we occasionally have custom instructions called "mcx"
|
1335
|
+
# which get wrongly caught by the plugin interface. A simple solution is
|
1336
|
+
# to return None in this case, since HLS would proceed to examine
|
1337
|
+
# their definition as it should.
|
1338
|
+
return None
|
1339
|
+
|
1340
|
+
num_ctrl_qubits = high_level_object.num_ctrl_qubits
|
1341
|
+
num_dirty_ancillas = options.get("num_dirty_ancillas", 0)
|
1342
|
+
|
1343
|
+
if num_dirty_ancillas < 1:
|
1344
|
+
return None
|
1345
|
+
|
1346
|
+
decomposition = synth_mcx_1_dirty_kg24(num_ctrl_qubits)
|
1347
|
+
return decomposition
|
1348
|
+
|
1349
|
+
|
1145
1350
|
class MCXSynthesisGrayCode(HighLevelSynthesisPlugin):
|
1146
1351
|
r"""Synthesis plugin for a multi-controlled X gate based on the Gray code.
|
1147
1352
|
|
@@ -1225,27 +1430,23 @@ class MCXSynthesisDefault(HighLevelSynthesisPlugin):
|
|
1225
1430
|
|
1226
1431
|
# Iteratively run other synthesis methods available
|
1227
1432
|
|
1228
|
-
|
1229
|
-
|
1230
|
-
|
1231
|
-
|
1232
|
-
|
1233
|
-
|
1234
|
-
|
1235
|
-
|
1236
|
-
|
1237
|
-
|
1238
|
-
|
1239
|
-
|
1240
|
-
|
1241
|
-
|
1242
|
-
|
1243
|
-
decomposition := MCXSynthesis1CleanB95().run(
|
1244
|
-
high_level_object, coupling_map, target, qubits, **options
|
1245
|
-
)
|
1246
|
-
) is not None:
|
1247
|
-
return decomposition
|
1433
|
+
for synthesis_method in [
|
1434
|
+
MCXSynthesis2CleanKG24,
|
1435
|
+
MCXSynthesis1CleanKG24,
|
1436
|
+
MCXSynthesisNCleanM15,
|
1437
|
+
MCXSynthesisNDirtyI15,
|
1438
|
+
MCXSynthesis2DirtyKG24,
|
1439
|
+
MCXSynthesis1DirtyKG24,
|
1440
|
+
MCXSynthesis1CleanB95,
|
1441
|
+
]:
|
1442
|
+
if (
|
1443
|
+
decomposition := synthesis_method().run(
|
1444
|
+
high_level_object, coupling_map, target, qubits, **options
|
1445
|
+
)
|
1446
|
+
) is not None:
|
1447
|
+
return decomposition
|
1248
1448
|
|
1449
|
+
# If no synthesis method was successful, fall back to the default
|
1249
1450
|
return MCXSynthesisNoAuxV24().run(
|
1250
1451
|
high_level_object, coupling_map, target, qubits, **options
|
1251
1452
|
)
|
@@ -1463,7 +1664,7 @@ class ModularAdderSynthesisD00(HighLevelSynthesisPlugin):
|
|
1463
1664
|
if not isinstance(high_level_object, ModularAdderGate):
|
1464
1665
|
return None
|
1465
1666
|
|
1466
|
-
return adder_qft_d00(high_level_object.num_state_qubits, kind="fixed")
|
1667
|
+
return adder_qft_d00(high_level_object.num_state_qubits, kind="fixed", annotated=True)
|
1467
1668
|
|
1468
1669
|
|
1469
1670
|
class HalfAdderSynthesisDefault(HighLevelSynthesisPlugin):
|
@@ -1490,13 +1691,17 @@ class HalfAdderSynthesisDefault(HighLevelSynthesisPlugin):
|
|
1490
1691
|
if not isinstance(high_level_object, HalfAdderGate):
|
1491
1692
|
return None
|
1492
1693
|
|
1493
|
-
# For up to 3 qubits,
|
1494
|
-
if
|
1495
|
-
|
1496
|
-
|
1694
|
+
# For up to 3 qubits, ripple_r25 is better
|
1695
|
+
if (
|
1696
|
+
high_level_object.num_state_qubits <= 3
|
1697
|
+
and (
|
1698
|
+
decomposition := HalfAdderSynthesisR25().run(
|
1699
|
+
high_level_object, coupling_map, target, qubits, **options
|
1700
|
+
)
|
1497
1701
|
)
|
1498
|
-
|
1499
|
-
|
1702
|
+
is not None
|
1703
|
+
):
|
1704
|
+
return decomposition
|
1500
1705
|
|
1501
1706
|
# The next best option is to use ripple_c04 (if there are enough ancilla qubits)
|
1502
1707
|
if (
|
@@ -1506,8 +1711,8 @@ class HalfAdderSynthesisDefault(HighLevelSynthesisPlugin):
|
|
1506
1711
|
) is not None:
|
1507
1712
|
return decomposition
|
1508
1713
|
|
1509
|
-
# The
|
1510
|
-
return
|
1714
|
+
# The ripple_rv_25 adder does not require ancilla qubits and should always succeed
|
1715
|
+
return HalfAdderSynthesisR25().run(
|
1511
1716
|
high_level_object, coupling_map, target, qubits, **options
|
1512
1717
|
)
|
1513
1718
|
|
@@ -1564,6 +1769,22 @@ class HalfAdderSynthesisV95(HighLevelSynthesisPlugin):
|
|
1564
1769
|
return adder_ripple_v95(num_state_qubits, kind="half")
|
1565
1770
|
|
1566
1771
|
|
1772
|
+
class HalfAdderSynthesisR25(HighLevelSynthesisPlugin):
|
1773
|
+
"""A ripple-carry adder with a carry-out bit with no ancillary qubits.
|
1774
|
+
|
1775
|
+
This plugin name is:``HalfAdder.ripple_r25`` which can be used as the key on an
|
1776
|
+
:class:`~.HLSConfig` object to use this method with :class:`~.HighLevelSynthesis`.
|
1777
|
+
|
1778
|
+
"""
|
1779
|
+
|
1780
|
+
def run(self, high_level_object, coupling_map=None, target=None, qubits=None, **options):
|
1781
|
+
if not isinstance(high_level_object, HalfAdderGate):
|
1782
|
+
return None
|
1783
|
+
|
1784
|
+
num_state_qubits = high_level_object.num_state_qubits
|
1785
|
+
return adder_ripple_r25(num_state_qubits)
|
1786
|
+
|
1787
|
+
|
1567
1788
|
class HalfAdderSynthesisD00(HighLevelSynthesisPlugin):
|
1568
1789
|
"""A QFT-based adder with a carry-in and a carry-out bit.
|
1569
1790
|
|
@@ -1575,7 +1796,7 @@ class HalfAdderSynthesisD00(HighLevelSynthesisPlugin):
|
|
1575
1796
|
if not isinstance(high_level_object, HalfAdderGate):
|
1576
1797
|
return None
|
1577
1798
|
|
1578
|
-
return adder_qft_d00(high_level_object.num_state_qubits, kind="half")
|
1799
|
+
return adder_qft_d00(high_level_object.num_state_qubits, kind="half", annotated=True)
|
1579
1800
|
|
1580
1801
|
|
1581
1802
|
class FullAdderSynthesisDefault(HighLevelSynthesisPlugin):
|
@@ -1696,13 +1917,15 @@ class PauliEvolutionSynthesisDefault(HighLevelSynthesisPlugin):
|
|
1696
1917
|
# Don't do anything if a gate is called "evolution" but is not an
|
1697
1918
|
# actual PauliEvolutionGate
|
1698
1919
|
return None
|
1699
|
-
|
1700
1920
|
algo = high_level_object.synthesis
|
1701
1921
|
|
1922
|
+
original_preserve_order = algo.preserve_order
|
1702
1923
|
if "preserve_order" in options and isinstance(algo, ProductFormula):
|
1703
1924
|
algo.preserve_order = options["preserve_order"]
|
1704
1925
|
|
1705
|
-
|
1926
|
+
synth_object = algo.synthesize(high_level_object)
|
1927
|
+
algo.preserve_order = original_preserve_order
|
1928
|
+
return synth_object
|
1706
1929
|
|
1707
1930
|
|
1708
1931
|
class PauliEvolutionSynthesisRustiq(HighLevelSynthesisPlugin):
|
@@ -1753,6 +1976,7 @@ class PauliEvolutionSynthesisRustiq(HighLevelSynthesisPlugin):
|
|
1753
1976
|
)
|
1754
1977
|
return None
|
1755
1978
|
|
1979
|
+
original_preserve_order = algo.preserve_order
|
1756
1980
|
if "preserve_order" in options:
|
1757
1981
|
algo.preserve_order = options["preserve_order"]
|
1758
1982
|
|
@@ -1765,7 +1989,7 @@ class PauliEvolutionSynthesisRustiq(HighLevelSynthesisPlugin):
|
|
1765
1989
|
upto_phase = options.get("upto_phase", False)
|
1766
1990
|
resynth_clifford_method = options.get("resynth_clifford_method", 1)
|
1767
1991
|
|
1768
|
-
|
1992
|
+
synth_object = synth_pauli_network_rustiq(
|
1769
1993
|
num_qubits=num_qubits,
|
1770
1994
|
pauli_network=pauli_network,
|
1771
1995
|
optimize_count=optimize_count,
|
@@ -1774,6 +1998,8 @@ class PauliEvolutionSynthesisRustiq(HighLevelSynthesisPlugin):
|
|
1774
1998
|
upto_phase=upto_phase,
|
1775
1999
|
resynth_clifford_method=resynth_clifford_method,
|
1776
2000
|
)
|
2001
|
+
algo.preserve_order = original_preserve_order
|
2002
|
+
return synth_object
|
1777
2003
|
|
1778
2004
|
|
1779
2005
|
class AnnotatedSynthesisDefault(HighLevelSynthesisPlugin):
|
@@ -1784,9 +2010,17 @@ class AnnotatedSynthesisDefault(HighLevelSynthesisPlugin):
|
|
1784
2010
|
"""
|
1785
2011
|
|
1786
2012
|
def run(self, high_level_object, coupling_map=None, target=None, qubits=None, **options):
|
2013
|
+
# The plugin is triggered based on the name (i.e. for operations called "annotated").
|
2014
|
+
# However, we should only do something when the operation is truthfully an AnnotatedOperation.
|
1787
2015
|
if not isinstance(high_level_object, AnnotatedOperation):
|
1788
2016
|
return None
|
1789
2017
|
|
2018
|
+
# Combine the modifiers. If there were no modifiers, or the modifiers magically canceled out,
|
2019
|
+
# return the quantum circuit containing the base operation.
|
2020
|
+
high_level_object = self._canonicalize_op(high_level_object)
|
2021
|
+
if not isinstance(high_level_object, AnnotatedOperation):
|
2022
|
+
return self._instruction_to_circuit(high_level_object)
|
2023
|
+
|
1790
2024
|
operation = high_level_object
|
1791
2025
|
modifiers = high_level_object.modifiers
|
1792
2026
|
|
@@ -1802,67 +2036,110 @@ class AnnotatedSynthesisDefault(HighLevelSynthesisPlugin):
|
|
1802
2036
|
"The AnnotatedSynthesisDefault plugin should receive data and input_qubits via options."
|
1803
2037
|
)
|
1804
2038
|
|
1805
|
-
|
1806
|
-
|
1807
|
-
|
1808
|
-
|
1809
|
-
|
1810
|
-
|
1811
|
-
|
1812
|
-
|
1813
|
-
|
1814
|
-
|
1815
|
-
|
1816
|
-
|
1817
|
-
|
1818
|
-
|
1819
|
-
|
1820
|
-
|
1821
|
-
|
1822
|
-
|
1823
|
-
|
1824
|
-
|
1825
|
-
|
1826
|
-
|
1827
|
-
|
1828
|
-
|
1829
|
-
|
1830
|
-
|
1831
|
-
|
1832
|
-
|
1833
|
-
control_qubits = input_qubits[:num_ctrl]
|
1834
|
-
base_qubits = input_qubits[num_ctrl:]
|
1835
|
-
annotated_tracker.disable(control_qubits) # do not access control qubits
|
1836
|
-
if power != 0 or is_inverted:
|
1837
|
-
annotated_tracker.set_dirty(base_qubits)
|
1838
|
-
|
1839
|
-
# Note that synthesize_operation also returns the output qubits on which the
|
1840
|
-
# operation is defined, however currently the plugin mechanism has no way
|
1841
|
-
# to return these (and instead the upstream code greedily grabs some ancilla
|
1842
|
-
# qubits from the circuit). We should refactor the plugin "run" iterface to
|
1843
|
-
# return the actual ancilla qubits used.
|
1844
|
-
synthesized_base_op_result = synthesize_operation(
|
1845
|
-
operation.base_op, input_qubits[num_ctrl:], data, annotated_tracker
|
1846
|
-
)
|
2039
|
+
# The synthesis consists of two steps:
|
2040
|
+
# - First, we synthesize the base operation.
|
2041
|
+
# - Second, we apply modifiers to this circuit.
|
2042
|
+
#
|
2043
|
+
# An important optimization (similar to the code in ``add_control.py``) is to synthesize
|
2044
|
+
# the base operation with respect to a larger set of "basis" gates, to which the control
|
2045
|
+
# logic can be added more efficiently. In addition, we add annotated operations to be
|
2046
|
+
# in this larger set, exploiting the fact that adding control to annotated operations
|
2047
|
+
# returns a new annotated operation with an extended list of modifiers.
|
2048
|
+
#
|
2049
|
+
# Note that it is fine for this function to return a circuit with high-level objects
|
2050
|
+
# (including annotated operations) as the HighLevelSynthesis transpiler pass will
|
2051
|
+
# recursively re-synthesize this circuit, However, we should always guarantee that some
|
2052
|
+
# progress is made.
|
2053
|
+
basis = set(EFFICIENTLY_CONTROLLED_GATES + ["annotated", "mcx", "qft"])
|
2054
|
+
|
2055
|
+
base_synthesis_data = HighLevelSynthesisData(
|
2056
|
+
hls_config=data.hls_config,
|
2057
|
+
hls_plugin_manager=data.hls_plugin_manager,
|
2058
|
+
coupling_map=None,
|
2059
|
+
target=None,
|
2060
|
+
equivalence_library=data.equivalence_library,
|
2061
|
+
hls_op_names=data.hls_op_names,
|
2062
|
+
device_insts=basis,
|
2063
|
+
use_physical_indices=data.use_physical_indices,
|
2064
|
+
min_qubits=0,
|
2065
|
+
unroll_definitions=data.unroll_definitions,
|
2066
|
+
)
|
1847
2067
|
|
1848
|
-
|
1849
|
-
|
1850
|
-
|
1851
|
-
|
1852
|
-
|
1853
|
-
|
1854
|
-
|
1855
|
-
|
1856
|
-
|
1857
|
-
|
2068
|
+
num_ctrl = sum(mod.num_ctrl_qubits for mod in modifiers if isinstance(mod, ControlModifier))
|
2069
|
+
power = sum(mod.power for mod in modifiers if isinstance(mod, PowerModifier))
|
2070
|
+
is_inverted = sum(1 for mod in modifiers if isinstance(mod, InverseModifier)) % 2
|
2071
|
+
|
2072
|
+
# First, synthesize the base operation of this annotated operation.
|
2073
|
+
# As this step cannot use any control qubits as auxiliary qubits, we use a dedicated
|
2074
|
+
# tracker (annotated_tracker).
|
2075
|
+
# The logic is as follows:
|
2076
|
+
# - annotated_tracker.disable control qubits
|
2077
|
+
# - if have power or inverse modifiers, annotated_tracker.set_dirty(base_qubits)
|
2078
|
+
# - synthesize the base operation using annotated tracker
|
2079
|
+
# - main_tracker.set_dirty(base_qubits)
|
2080
|
+
#
|
2081
|
+
# Note that we need to set the base_qubits to dirty if we have power or inverse
|
2082
|
+
# modifiers. For power: even if the power is a positive integer (that is, we need
|
2083
|
+
# to repeat the same circuit multiple times), even if the target is initially at |0>,
|
2084
|
+
# it will generally not be at |0> after one iteration. For inverse: as we
|
2085
|
+
# flip the order of operations, we cannot exploit which qubits are at |0> as "viewed from
|
2086
|
+
# the back of the circuit". If we just have control modifiers, we can use the state
|
2087
|
+
# of base qubits when synthesizing the controlled operation.
|
2088
|
+
#
|
2089
|
+
# In addition, all of the other global qubits that are not a part of the annotated
|
2090
|
+
# operation can be used as they are in all cases, since we are assuming that all of
|
2091
|
+
# the synthesis methods preserve the states of ancilla qubits.
|
2092
|
+
annotated_tracker = tracker.copy()
|
2093
|
+
control_qubits = input_qubits[:num_ctrl]
|
2094
|
+
base_qubits = input_qubits[num_ctrl:]
|
2095
|
+
annotated_tracker.disable(control_qubits) # do not access control qubits
|
2096
|
+
if power != 0 or is_inverted:
|
2097
|
+
annotated_tracker.set_dirty(base_qubits)
|
2098
|
+
|
2099
|
+
# Note that synthesize_operation also returns the output qubits on which the
|
2100
|
+
# operation is defined, however currently the plugin mechanism has no way
|
2101
|
+
# to return these (and instead the upstream code greedily grabs some ancilla
|
2102
|
+
# qubits from the circuit). We should refactor the plugin "run" iterface to
|
2103
|
+
# return the actual ancilla qubits used.
|
2104
|
+
synthesized_base_op_result = synthesize_operation(
|
2105
|
+
operation.base_op, base_qubits, base_synthesis_data, annotated_tracker
|
2106
|
+
)
|
1858
2107
|
|
2108
|
+
# The base operation does not need to be synthesized.
|
2109
|
+
# For simplicity, we wrap the instruction into a circuit. Note that
|
2110
|
+
# this should not deteriorate the quality of the result.
|
2111
|
+
if synthesized_base_op_result is None:
|
2112
|
+
synthesized_base_op = self._instruction_to_circuit(operation.base_op)
|
2113
|
+
else:
|
2114
|
+
synthesized_base_op = QuantumCircuit._from_circuit_data(synthesized_base_op_result[0])
|
2115
|
+
tracker.set_dirty(base_qubits)
|
2116
|
+
|
2117
|
+
# As one simple optimization, we apply conjugate decomposition to the circuit obtained
|
2118
|
+
# while synthesizing the base operator.
|
2119
|
+
conjugate_decomp = self._conjugate_decomposition(synthesized_base_op)
|
2120
|
+
|
2121
|
+
if conjugate_decomp is None:
|
2122
|
+
# Apply annotations to the whole circuit.
|
1859
2123
|
# This step currently does not introduce ancilla qubits. However it makes
|
1860
2124
|
# a lot of sense to allow this in the future.
|
1861
2125
|
synthesized = self._apply_annotations(synthesized_base_op, operation.modifiers)
|
2126
|
+
else:
|
2127
|
+
# Apply annotations only to the middle part of the circuit.
|
2128
|
+
(front, middle, back) = conjugate_decomp
|
2129
|
+
synthesized = QuantumCircuit(operation.num_qubits)
|
2130
|
+
synthesized.compose(
|
2131
|
+
front, synthesized.qubits[num_ctrl : operation.num_qubits], inplace=True
|
2132
|
+
)
|
2133
|
+
synthesized.compose(
|
2134
|
+
self._apply_annotations(middle, operation.modifiers),
|
2135
|
+
synthesized.qubits,
|
2136
|
+
inplace=True,
|
2137
|
+
)
|
2138
|
+
synthesized.compose(
|
2139
|
+
back, synthesized.qubits[num_ctrl : operation.num_qubits], inplace=True
|
2140
|
+
)
|
1862
2141
|
|
1863
|
-
|
1864
|
-
|
1865
|
-
return None
|
2142
|
+
return synthesized
|
1866
2143
|
|
1867
2144
|
@staticmethod
|
1868
2145
|
def _apply_annotations(circuit: QuantumCircuit, modifiers: list[Modifier]) -> QuantumCircuit:
|
@@ -1926,6 +2203,109 @@ class AnnotatedSynthesisDefault(HighLevelSynthesisPlugin):
|
|
1926
2203
|
circuit.append(op, circuit.qubits, circuit.clbits)
|
1927
2204
|
return circuit
|
1928
2205
|
|
2206
|
+
@staticmethod
|
2207
|
+
def _instruction_to_circuit(op: Operation) -> QuantumCircuit:
|
2208
|
+
"""Wraps a single operation into a quantum circuit."""
|
2209
|
+
circuit = QuantumCircuit(op.num_qubits, op.num_clbits)
|
2210
|
+
circuit.append(op, circuit.qubits, circuit.clbits)
|
2211
|
+
return circuit
|
2212
|
+
|
2213
|
+
@staticmethod
|
2214
|
+
def _canonicalize_op(op: Operation) -> Operation:
|
2215
|
+
"""
|
2216
|
+
Combines recursive annotated operations and canonicalizes modifiers.
|
2217
|
+
"""
|
2218
|
+
cur = op
|
2219
|
+
all_modifiers = []
|
2220
|
+
|
2221
|
+
while isinstance(cur, AnnotatedOperation):
|
2222
|
+
all_modifiers.append(cur.modifiers)
|
2223
|
+
cur = cur.base_op
|
2224
|
+
|
2225
|
+
new_modifiers = []
|
2226
|
+
for modifiers in all_modifiers[::-1]:
|
2227
|
+
new_modifiers.extend(modifiers)
|
2228
|
+
|
2229
|
+
canonical_modifiers = _canonicalize_modifiers(new_modifiers)
|
2230
|
+
|
2231
|
+
if not canonical_modifiers:
|
2232
|
+
return cur
|
2233
|
+
|
2234
|
+
return AnnotatedOperation(cur, canonical_modifiers)
|
2235
|
+
|
2236
|
+
@staticmethod
|
2237
|
+
def _are_inverse_ops(inst1: "CircuitInstruction", inst2: "CircuitInstruction"):
|
2238
|
+
"""A very naive function that checks whether two circuit instructions are inverse of
|
2239
|
+
each other. The main use-case covered is a ``QFTGate`` and its inverse, represented as
|
2240
|
+
an ``AnnotatedOperation`` with a single ``InverseModifier``.
|
2241
|
+
"""
|
2242
|
+
res = False
|
2243
|
+
|
2244
|
+
if (
|
2245
|
+
inst1.qubits != inst2.qubits
|
2246
|
+
or inst1.clbits != inst2.clbits
|
2247
|
+
or len(inst1.params) != len(inst2.params)
|
2248
|
+
):
|
2249
|
+
return False
|
2250
|
+
|
2251
|
+
op1 = inst1.operation
|
2252
|
+
op2 = inst2.operation
|
2253
|
+
|
2254
|
+
ann1 = isinstance(op1, AnnotatedOperation)
|
2255
|
+
ann2 = isinstance(op2, AnnotatedOperation)
|
2256
|
+
|
2257
|
+
if not ann1 and not ann2:
|
2258
|
+
res = op1 == op2.inverse()
|
2259
|
+
elif not ann1 and ann2 and op2.modifiers == [InverseModifier()]:
|
2260
|
+
res = op1 == op2.base_op
|
2261
|
+
elif not ann2 and ann1 and op1.modifiers == [InverseModifier()]:
|
2262
|
+
res = op1.base_op == op2
|
2263
|
+
|
2264
|
+
return res
|
2265
|
+
|
2266
|
+
@staticmethod
|
2267
|
+
def _conjugate_decomposition(
|
2268
|
+
circuit: QuantumCircuit,
|
2269
|
+
) -> tuple[QuantumCircuit, QuantumCircuit, QuantumCircuit] | None:
|
2270
|
+
"""
|
2271
|
+
Decomposes a circuit ``A`` into 3 sub-circuits ``P``, ``Q``, ``R`` such that
|
2272
|
+
``A = P -- Q -- R`` and ``R = P^{-1}``.
|
2273
|
+
|
2274
|
+
This is accomplished by iteratively finding inverse nodes at the front and at the back of the
|
2275
|
+
circuit.
|
2276
|
+
|
2277
|
+
The function returns ``None`` when ``P`` and ``R`` are empty.
|
2278
|
+
"""
|
2279
|
+
num_gates = circuit.size()
|
2280
|
+
|
2281
|
+
idx = 0
|
2282
|
+
ridx = num_gates - 1
|
2283
|
+
|
2284
|
+
while True:
|
2285
|
+
if idx >= ridx:
|
2286
|
+
break
|
2287
|
+
if AnnotatedSynthesisDefault._are_inverse_ops(circuit[idx], circuit[ridx]):
|
2288
|
+
idx += 1
|
2289
|
+
ridx -= 1
|
2290
|
+
else:
|
2291
|
+
break
|
2292
|
+
|
2293
|
+
if idx == 0:
|
2294
|
+
return None
|
2295
|
+
|
2296
|
+
front_circuit = circuit.copy_empty_like()
|
2297
|
+
front_circuit.global_phase = 0
|
2298
|
+
for i in range(0, idx):
|
2299
|
+
front_circuit.append(circuit[i])
|
2300
|
+
middle_circuit = circuit.copy_empty_like() # inherits the global phase
|
2301
|
+
for i in range(idx, ridx + 1):
|
2302
|
+
middle_circuit.append(circuit[i])
|
2303
|
+
back_circuit = circuit.copy_empty_like()
|
2304
|
+
back_circuit.global_phase = 0
|
2305
|
+
for i in range(ridx + 1, num_gates):
|
2306
|
+
back_circuit.append(circuit[i])
|
2307
|
+
return (front_circuit, middle_circuit, back_circuit)
|
2308
|
+
|
1929
2309
|
|
1930
2310
|
class WeightedSumSynthesisDefault(HighLevelSynthesisPlugin):
|
1931
2311
|
"""Synthesize a :class:`.WeightedSumGate` using the default synthesis algorithm.
|