qiskit 1.0.2__cp38-abi3-win32.whl → 1.1.0__cp38-abi3-win32.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 +27 -16
- qiskit/_accelerate.pyd +0 -0
- qiskit/_numpy_compat.py +73 -0
- qiskit/assembler/__init__.py +5 -10
- qiskit/assembler/disassemble.py +5 -6
- qiskit/circuit/__init__.py +1061 -232
- qiskit/circuit/_classical_resource_map.py +10 -6
- qiskit/circuit/_utils.py +18 -8
- qiskit/circuit/annotated_operation.py +21 -0
- qiskit/circuit/barrier.py +10 -13
- qiskit/circuit/bit.py +0 -1
- qiskit/circuit/classical/__init__.py +2 -2
- qiskit/circuit/classical/expr/__init__.py +39 -5
- qiskit/circuit/classical/expr/constructors.py +84 -1
- qiskit/circuit/classical/expr/expr.py +83 -13
- qiskit/circuit/classical/expr/visitors.py +83 -0
- qiskit/circuit/classical/types/__init__.py +5 -4
- qiskit/circuit/classicalfunction/__init__.py +1 -0
- qiskit/circuit/commutation_checker.py +86 -51
- qiskit/circuit/controlflow/_builder_utils.py +9 -1
- qiskit/circuit/controlflow/break_loop.py +8 -22
- qiskit/circuit/controlflow/builder.py +116 -1
- qiskit/circuit/controlflow/continue_loop.py +8 -22
- qiskit/circuit/controlflow/control_flow.py +47 -8
- qiskit/circuit/controlflow/for_loop.py +8 -23
- qiskit/circuit/controlflow/if_else.py +13 -27
- qiskit/circuit/controlflow/switch_case.py +14 -21
- qiskit/circuit/controlflow/while_loop.py +9 -23
- qiskit/circuit/controlledgate.py +2 -2
- qiskit/circuit/delay.py +7 -5
- qiskit/circuit/gate.py +20 -7
- qiskit/circuit/instruction.py +31 -30
- qiskit/circuit/instructionset.py +9 -22
- qiskit/circuit/library/__init__.py +3 -13
- qiskit/circuit/library/arithmetic/integer_comparator.py +2 -2
- qiskit/circuit/library/arithmetic/quadratic_form.py +3 -2
- qiskit/circuit/library/blueprintcircuit.py +29 -7
- qiskit/circuit/library/data_preparation/state_preparation.py +6 -5
- qiskit/circuit/library/generalized_gates/diagonal.py +5 -4
- qiskit/circuit/library/generalized_gates/isometry.py +51 -254
- qiskit/circuit/library/generalized_gates/pauli.py +2 -2
- qiskit/circuit/library/generalized_gates/permutation.py +4 -1
- qiskit/circuit/library/generalized_gates/rv.py +15 -11
- qiskit/circuit/library/generalized_gates/uc.py +2 -98
- qiskit/circuit/library/generalized_gates/unitary.py +9 -4
- qiskit/circuit/library/hamiltonian_gate.py +11 -5
- qiskit/circuit/library/n_local/efficient_su2.py +5 -5
- qiskit/circuit/library/n_local/n_local.py +100 -49
- qiskit/circuit/library/n_local/two_local.py +3 -59
- qiskit/circuit/library/overlap.py +3 -3
- qiskit/circuit/library/phase_oracle.py +1 -1
- qiskit/circuit/library/quantum_volume.py +39 -38
- qiskit/circuit/library/standard_gates/equivalence_library.py +50 -0
- qiskit/circuit/library/standard_gates/global_phase.py +4 -2
- qiskit/circuit/library/standard_gates/i.py +1 -2
- qiskit/circuit/library/standard_gates/iswap.py +1 -2
- qiskit/circuit/library/standard_gates/multi_control_rotation_gates.py +11 -5
- qiskit/circuit/library/standard_gates/p.py +31 -15
- qiskit/circuit/library/standard_gates/r.py +4 -3
- qiskit/circuit/library/standard_gates/rx.py +7 -4
- qiskit/circuit/library/standard_gates/rxx.py +4 -3
- qiskit/circuit/library/standard_gates/ry.py +7 -4
- qiskit/circuit/library/standard_gates/ryy.py +4 -3
- qiskit/circuit/library/standard_gates/rz.py +7 -4
- qiskit/circuit/library/standard_gates/rzx.py +4 -3
- qiskit/circuit/library/standard_gates/rzz.py +4 -3
- qiskit/circuit/library/standard_gates/s.py +4 -8
- qiskit/circuit/library/standard_gates/t.py +2 -4
- qiskit/circuit/library/standard_gates/u.py +16 -11
- qiskit/circuit/library/standard_gates/u1.py +6 -2
- qiskit/circuit/library/standard_gates/u2.py +4 -2
- qiskit/circuit/library/standard_gates/u3.py +9 -5
- qiskit/circuit/library/standard_gates/x.py +22 -11
- qiskit/circuit/library/standard_gates/xx_minus_yy.py +4 -3
- qiskit/circuit/library/standard_gates/xx_plus_yy.py +7 -5
- qiskit/circuit/library/standard_gates/z.py +1 -2
- qiskit/circuit/measure.py +4 -1
- qiskit/circuit/operation.py +13 -8
- qiskit/circuit/parameter.py +11 -6
- qiskit/circuit/quantumcircuit.py +1910 -260
- qiskit/circuit/quantumcircuitdata.py +2 -2
- qiskit/circuit/reset.py +5 -2
- qiskit/circuit/store.py +95 -0
- qiskit/compiler/assembler.py +22 -22
- qiskit/compiler/transpiler.py +63 -112
- qiskit/converters/__init__.py +17 -2
- qiskit/converters/circuit_to_dag.py +7 -0
- qiskit/converters/circuit_to_dagdependency_v2.py +47 -0
- qiskit/converters/circuit_to_gate.py +2 -0
- qiskit/converters/circuit_to_instruction.py +22 -0
- qiskit/converters/dag_to_circuit.py +4 -0
- qiskit/converters/dag_to_dagdependency_v2.py +44 -0
- qiskit/dagcircuit/collect_blocks.py +15 -10
- qiskit/dagcircuit/dagcircuit.py +434 -124
- qiskit/dagcircuit/dagdependency.py +19 -12
- qiskit/dagcircuit/dagdependency_v2.py +641 -0
- qiskit/dagcircuit/dagdepnode.py +19 -16
- qiskit/dagcircuit/dagnode.py +14 -4
- qiskit/passmanager/passmanager.py +11 -11
- qiskit/primitives/__init__.py +22 -12
- qiskit/primitives/backend_estimator.py +3 -5
- qiskit/primitives/backend_estimator_v2.py +410 -0
- qiskit/primitives/backend_sampler_v2.py +287 -0
- qiskit/primitives/base/base_estimator.py +4 -9
- qiskit/primitives/base/base_sampler.py +2 -2
- qiskit/primitives/containers/__init__.py +6 -4
- qiskit/primitives/containers/bit_array.py +293 -2
- qiskit/primitives/containers/data_bin.py +123 -50
- qiskit/primitives/containers/estimator_pub.py +10 -3
- qiskit/primitives/containers/observables_array.py +2 -2
- qiskit/primitives/containers/pub_result.py +1 -1
- qiskit/primitives/containers/sampler_pub.py +19 -3
- qiskit/primitives/containers/sampler_pub_result.py +74 -0
- qiskit/primitives/containers/shape.py +4 -4
- qiskit/primitives/statevector_estimator.py +4 -4
- qiskit/primitives/statevector_sampler.py +7 -12
- qiskit/providers/__init__.py +65 -34
- qiskit/providers/backend.py +2 -2
- qiskit/providers/backend_compat.py +8 -10
- qiskit/providers/basic_provider/__init__.py +2 -23
- qiskit/providers/basic_provider/basic_provider_tools.py +67 -31
- qiskit/providers/basic_provider/basic_simulator.py +81 -21
- qiskit/providers/fake_provider/__init__.py +1 -1
- qiskit/providers/fake_provider/fake_1q.py +1 -1
- qiskit/providers/fake_provider/fake_backend.py +3 -408
- qiskit/providers/fake_provider/generic_backend_v2.py +26 -14
- qiskit/providers/models/__init__.py +2 -2
- qiskit/providers/provider.py +16 -0
- qiskit/pulse/builder.py +4 -1
- qiskit/pulse/parameter_manager.py +60 -4
- qiskit/pulse/schedule.py +29 -13
- qiskit/pulse/utils.py +61 -20
- qiskit/qasm2/__init__.py +1 -5
- qiskit/qasm2/parse.py +1 -4
- qiskit/qasm3/__init__.py +42 -5
- qiskit/qasm3/ast.py +19 -0
- qiskit/qasm3/exporter.py +178 -106
- qiskit/qasm3/printer.py +27 -5
- qiskit/qobj/converters/pulse_instruction.py +6 -6
- qiskit/qpy/__init__.py +299 -67
- qiskit/qpy/binary_io/circuits.py +216 -47
- qiskit/qpy/binary_io/schedules.py +42 -36
- qiskit/qpy/binary_io/value.py +201 -22
- qiskit/qpy/common.py +1 -1
- qiskit/qpy/exceptions.py +20 -0
- qiskit/qpy/formats.py +29 -0
- qiskit/qpy/type_keys.py +21 -0
- qiskit/quantum_info/analysis/distance.py +3 -3
- qiskit/quantum_info/analysis/make_observable.py +2 -1
- qiskit/quantum_info/analysis/z2_symmetries.py +2 -1
- qiskit/quantum_info/operators/channel/chi.py +9 -8
- qiskit/quantum_info/operators/channel/choi.py +10 -9
- qiskit/quantum_info/operators/channel/kraus.py +2 -1
- qiskit/quantum_info/operators/channel/ptm.py +10 -9
- qiskit/quantum_info/operators/channel/quantum_channel.py +2 -1
- qiskit/quantum_info/operators/channel/stinespring.py +2 -1
- qiskit/quantum_info/operators/channel/superop.py +12 -11
- qiskit/quantum_info/operators/channel/transformations.py +12 -11
- qiskit/quantum_info/operators/dihedral/dihedral.py +5 -4
- qiskit/quantum_info/operators/operator.py +43 -30
- qiskit/quantum_info/operators/scalar_op.py +10 -9
- qiskit/quantum_info/operators/symplectic/base_pauli.py +70 -59
- qiskit/quantum_info/operators/symplectic/clifford.py +36 -9
- qiskit/quantum_info/operators/symplectic/pauli.py +53 -6
- qiskit/quantum_info/operators/symplectic/pauli_list.py +36 -14
- qiskit/quantum_info/operators/symplectic/random.py +3 -2
- qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py +61 -36
- qiskit/quantum_info/states/densitymatrix.py +13 -13
- qiskit/quantum_info/states/stabilizerstate.py +3 -3
- qiskit/quantum_info/states/statevector.py +14 -13
- qiskit/quantum_info/states/utils.py +5 -3
- qiskit/result/__init__.py +6 -0
- qiskit/result/mitigation/correlated_readout_mitigator.py +3 -2
- qiskit/result/mitigation/local_readout_mitigator.py +2 -1
- qiskit/result/mitigation/utils.py +3 -2
- qiskit/scheduler/__init__.py +10 -1
- qiskit/scheduler/methods/__init__.py +1 -8
- qiskit/synthesis/__init__.py +3 -6
- qiskit/synthesis/discrete_basis/commutator_decompose.py +2 -2
- qiskit/synthesis/evolution/lie_trotter.py +7 -14
- qiskit/synthesis/evolution/qdrift.py +3 -4
- qiskit/synthesis/linear/cnot_synth.py +1 -3
- qiskit/synthesis/linear/linear_circuits_utils.py +1 -1
- qiskit/synthesis/linear_phase/cz_depth_lnn.py +4 -18
- qiskit/synthesis/permutation/__init__.py +1 -0
- qiskit/synthesis/permutation/permutation_reverse_lnn.py +90 -0
- qiskit/synthesis/qft/qft_decompose_lnn.py +2 -6
- qiskit/synthesis/two_qubit/two_qubit_decompose.py +165 -954
- qiskit/synthesis/two_qubit/xx_decompose/circuits.py +13 -12
- qiskit/synthesis/two_qubit/xx_decompose/decomposer.py +7 -1
- qiskit/synthesis/unitary/aqc/__init__.py +1 -1
- qiskit/synthesis/unitary/aqc/cnot_structures.py +2 -1
- qiskit/synthesis/unitary/aqc/fast_gradient/fast_gradient.py +2 -1
- qiskit/synthesis/unitary/qsd.py +3 -2
- qiskit/transpiler/__init__.py +7 -3
- qiskit/transpiler/layout.py +140 -61
- qiskit/transpiler/passes/__init__.py +10 -2
- qiskit/transpiler/passes/basis/basis_translator.py +9 -4
- qiskit/transpiler/passes/basis/unroll_3q_or_more.py +1 -1
- qiskit/transpiler/passes/basis/unroll_custom_definitions.py +1 -1
- qiskit/transpiler/passes/calibration/rzx_builder.py +2 -1
- qiskit/transpiler/passes/layout/apply_layout.py +8 -3
- qiskit/transpiler/passes/layout/sabre_layout.py +15 -3
- qiskit/transpiler/passes/layout/set_layout.py +1 -1
- qiskit/transpiler/passes/optimization/__init__.py +2 -0
- qiskit/transpiler/passes/optimization/commutation_analysis.py +2 -2
- qiskit/transpiler/passes/optimization/commutative_cancellation.py +1 -1
- qiskit/transpiler/passes/optimization/consolidate_blocks.py +1 -1
- qiskit/transpiler/passes/optimization/cx_cancellation.py +10 -0
- qiskit/transpiler/passes/optimization/elide_permutations.py +114 -0
- qiskit/transpiler/passes/optimization/optimize_1q_decomposition.py +9 -3
- qiskit/transpiler/passes/optimization/optimize_annotated.py +248 -12
- qiskit/transpiler/passes/optimization/remove_final_reset.py +37 -0
- qiskit/transpiler/passes/optimization/template_matching/forward_match.py +1 -3
- qiskit/transpiler/passes/routing/__init__.py +1 -0
- qiskit/transpiler/passes/routing/basic_swap.py +13 -2
- qiskit/transpiler/passes/routing/commuting_2q_gate_routing/commuting_2q_gate_router.py +8 -1
- qiskit/transpiler/passes/routing/lookahead_swap.py +7 -1
- qiskit/transpiler/passes/routing/sabre_swap.py +10 -6
- qiskit/transpiler/passes/routing/star_prerouting.py +417 -0
- qiskit/transpiler/passes/routing/stochastic_swap.py +24 -8
- qiskit/transpiler/passes/scheduling/__init__.py +1 -1
- qiskit/transpiler/passes/scheduling/alap.py +1 -2
- qiskit/transpiler/passes/scheduling/alignments/align_measures.py +1 -2
- qiskit/transpiler/passes/scheduling/alignments/check_durations.py +9 -6
- qiskit/transpiler/passes/scheduling/alignments/pulse_gate_validation.py +8 -0
- qiskit/transpiler/passes/scheduling/alignments/reschedule.py +13 -4
- qiskit/transpiler/passes/scheduling/asap.py +1 -2
- qiskit/transpiler/passes/scheduling/base_scheduler.py +21 -2
- qiskit/transpiler/passes/scheduling/dynamical_decoupling.py +26 -4
- qiskit/transpiler/passes/scheduling/padding/dynamical_decoupling.py +24 -2
- qiskit/transpiler/passes/scheduling/time_unit_conversion.py +28 -4
- qiskit/transpiler/passes/synthesis/aqc_plugin.py +2 -2
- qiskit/transpiler/passes/synthesis/high_level_synthesis.py +120 -13
- qiskit/transpiler/passes/synthesis/unitary_synthesis.py +162 -55
- qiskit/transpiler/passes/utils/gates_basis.py +3 -3
- qiskit/transpiler/passmanager.py +44 -1
- qiskit/transpiler/preset_passmanagers/__init__.py +3 -3
- qiskit/transpiler/preset_passmanagers/builtin_plugins.py +34 -16
- qiskit/transpiler/preset_passmanagers/common.py +4 -6
- qiskit/transpiler/preset_passmanagers/plugin.py +9 -1
- qiskit/utils/__init__.py +3 -2
- qiskit/utils/optionals.py +6 -2
- qiskit/utils/parallel.py +24 -15
- qiskit/visualization/array.py +1 -1
- qiskit/visualization/bloch.py +2 -3
- qiskit/visualization/circuit/matplotlib.py +44 -14
- qiskit/visualization/circuit/text.py +38 -18
- qiskit/visualization/counts_visualization.py +3 -6
- qiskit/visualization/dag_visualization.py +6 -7
- qiskit/visualization/gate_map.py +9 -1
- qiskit/visualization/pulse_v2/interface.py +8 -3
- qiskit/visualization/state_visualization.py +3 -2
- qiskit/visualization/timeline/interface.py +18 -8
- {qiskit-1.0.2.dist-info → qiskit-1.1.0.dist-info}/METADATA +12 -8
- {qiskit-1.0.2.dist-info → qiskit-1.1.0.dist-info}/RECORD +261 -251
- {qiskit-1.0.2.dist-info → qiskit-1.1.0.dist-info}/WHEEL +1 -1
- qiskit/_qasm2.pyd +0 -0
- qiskit/_qasm3.pyd +0 -0
- {qiskit-1.0.2.dist-info → qiskit-1.1.0.dist-info}/LICENSE.txt +0 -0
- {qiskit-1.0.2.dist-info → qiskit-1.1.0.dist-info}/entry_points.txt +0 -0
- {qiskit-1.0.2.dist-info → qiskit-1.1.0.dist-info}/top_level.txt +0 -0
@@ -61,7 +61,7 @@ class UnrollCustomDefinitions(TransformationPass):
|
|
61
61
|
return dag
|
62
62
|
|
63
63
|
if self._target is None:
|
64
|
-
basic_insts = {"measure", "reset", "barrier", "snapshot", "delay"}
|
64
|
+
basic_insts = {"measure", "reset", "barrier", "snapshot", "delay", "store"}
|
65
65
|
device_insts = basic_insts | set(self._basis_gates)
|
66
66
|
|
67
67
|
for node in dag.op_nodes():
|
@@ -14,6 +14,7 @@
|
|
14
14
|
from __future__ import annotations
|
15
15
|
|
16
16
|
import enum
|
17
|
+
import math
|
17
18
|
import warnings
|
18
19
|
from collections.abc import Sequence
|
19
20
|
from math import pi, erf
|
@@ -135,7 +136,7 @@ class RZXCalibrationBuilder(CalibrationBuilder):
|
|
135
136
|
# The error function is used because the Gaussian may have chopped tails.
|
136
137
|
# Area is normalized by amplitude.
|
137
138
|
# This makes widths robust to the rounding error.
|
138
|
-
risefall_area = params["sigma"] *
|
139
|
+
risefall_area = params["sigma"] * math.sqrt(2 * pi) * erf(risefall_sigma_ratio)
|
139
140
|
full_area = params["width"] + risefall_area
|
140
141
|
|
141
142
|
# Get estimate of target area. Assume this is pi/2 controlled rotation.
|
@@ -56,11 +56,16 @@ class ApplyLayout(TransformationPass):
|
|
56
56
|
raise TranspilerError("The 'layout' must be full (with ancilla).")
|
57
57
|
|
58
58
|
post_layout = self.property_set["post_layout"]
|
59
|
-
|
60
59
|
q = QuantumRegister(len(layout), "q")
|
61
60
|
|
62
61
|
new_dag = DAGCircuit()
|
63
62
|
new_dag.add_qreg(q)
|
63
|
+
for var in dag.iter_input_vars():
|
64
|
+
new_dag.add_input_var(var)
|
65
|
+
for var in dag.iter_captured_vars():
|
66
|
+
new_dag.add_captured_var(var)
|
67
|
+
for var in dag.iter_declared_vars():
|
68
|
+
new_dag.add_declared_var(var)
|
64
69
|
new_dag.metadata = dag.metadata
|
65
70
|
new_dag.add_clbits(dag.clbits)
|
66
71
|
for creg in dag.cregs.values():
|
@@ -71,9 +76,9 @@ class ApplyLayout(TransformationPass):
|
|
71
76
|
}
|
72
77
|
for qreg in dag.qregs.values():
|
73
78
|
self.property_set["layout"].add_register(qreg)
|
74
|
-
|
79
|
+
virtual_physical_map = layout.get_virtual_bits()
|
75
80
|
for node in dag.topological_op_nodes():
|
76
|
-
qargs = [q[
|
81
|
+
qargs = [q[virtual_physical_map[qarg]] for qarg in node.qargs]
|
77
82
|
new_dag.apply_operation_back(node.op, qargs, node.cargs, check=False)
|
78
83
|
else:
|
79
84
|
# First build a new layout object going from:
|
@@ -35,8 +35,8 @@ from qiskit.transpiler.layout import Layout
|
|
35
35
|
from qiskit.transpiler.basepasses import TransformationPass
|
36
36
|
from qiskit.transpiler.exceptions import TranspilerError
|
37
37
|
from qiskit._accelerate.nlayout import NLayout
|
38
|
-
from qiskit._accelerate.
|
39
|
-
|
38
|
+
from qiskit._accelerate.sabre import (
|
39
|
+
sabre_layout_and_routing,
|
40
40
|
Heuristic,
|
41
41
|
NeighborTable,
|
42
42
|
)
|
@@ -308,11 +308,17 @@ class SabreLayout(TransformationPass):
|
|
308
308
|
mapped_dag.add_clbits(dag.clbits)
|
309
309
|
for creg in dag.cregs.values():
|
310
310
|
mapped_dag.add_creg(creg)
|
311
|
+
for var in dag.iter_input_vars():
|
312
|
+
mapped_dag.add_input_var(var)
|
313
|
+
for var in dag.iter_captured_vars():
|
314
|
+
mapped_dag.add_captured_var(var)
|
315
|
+
for var in dag.iter_declared_vars():
|
316
|
+
mapped_dag.add_declared_var(var)
|
311
317
|
mapped_dag._global_phase = dag._global_phase
|
312
318
|
self.property_set["original_qubit_indices"] = {
|
313
319
|
bit: index for index, bit in enumerate(dag.qubits)
|
314
320
|
}
|
315
|
-
|
321
|
+
final_layout = Layout(
|
316
322
|
{
|
317
323
|
mapped_dag.qubits[
|
318
324
|
component.coupling_map.graph[initial]
|
@@ -321,6 +327,12 @@ class SabreLayout(TransformationPass):
|
|
321
327
|
for initial, final in enumerate(component.final_permutation)
|
322
328
|
}
|
323
329
|
)
|
330
|
+
if self.property_set["final_layout"] is None:
|
331
|
+
self.property_set["final_layout"] = final_layout
|
332
|
+
else:
|
333
|
+
self.property_set["final_layout"] = final_layout.compose(
|
334
|
+
self.property_set["final_layout"], dag.qubits
|
335
|
+
)
|
324
336
|
for component in components:
|
325
337
|
# Sabre routing still returns all its swaps as on virtual qubits, so we need to expand
|
326
338
|
# each component DAG with the virtual ancillas that were allocated to it, so the layout
|
@@ -63,7 +63,7 @@ class SetLayout(AnalysisPass):
|
|
63
63
|
layout = None
|
64
64
|
else:
|
65
65
|
raise InvalidLayoutError(
|
66
|
-
f"SetLayout was
|
66
|
+
f"SetLayout was initialized with the layout type: {type(self.layout)}"
|
67
67
|
)
|
68
68
|
self.property_set["layout"] = layout
|
69
69
|
return dag
|
@@ -24,6 +24,7 @@ from .cx_cancellation import CXCancellation
|
|
24
24
|
from .optimize_1q_commutation import Optimize1qGatesSimpleCommutation
|
25
25
|
from .optimize_swap_before_measure import OptimizeSwapBeforeMeasure
|
26
26
|
from .remove_reset_in_zero_state import RemoveResetInZeroState
|
27
|
+
from .remove_final_reset import RemoveFinalReset
|
27
28
|
from .remove_diagonal_gates_before_measure import RemoveDiagonalGatesBeforeMeasure
|
28
29
|
from .hoare_opt import HoareOptimizer
|
29
30
|
from .template_optimization import TemplateOptimization
|
@@ -34,5 +35,6 @@ from .collect_linear_functions import CollectLinearFunctions
|
|
34
35
|
from .reset_after_measure_simplification import ResetAfterMeasureSimplification
|
35
36
|
from .optimize_cliffords import OptimizeCliffords
|
36
37
|
from .collect_cliffords import CollectCliffords
|
38
|
+
from .elide_permutations import ElidePermutations
|
37
39
|
from .normalize_rx_angle import NormalizeRXAngle
|
38
40
|
from .optimize_annotated import OptimizeAnnotated
|
@@ -47,7 +47,7 @@ class CommutationAnalysis(AnalysisPass):
|
|
47
47
|
# self.property_set['commutation_set'][wire][(node, wire)] will give the
|
48
48
|
# commutation set that contains node.
|
49
49
|
|
50
|
-
for wire in dag.
|
50
|
+
for wire in dag.qubits:
|
51
51
|
self.property_set["commutation_set"][wire] = []
|
52
52
|
|
53
53
|
# Add edges to the dictionary for each qubit
|
@@ -56,7 +56,7 @@ class CommutationAnalysis(AnalysisPass):
|
|
56
56
|
self.property_set["commutation_set"][(node, edge_wire)] = -1
|
57
57
|
|
58
58
|
# Construct the commutation set
|
59
|
-
for wire in dag.
|
59
|
+
for wire in dag.qubits:
|
60
60
|
|
61
61
|
for current_gate in dag.nodes_on_wire(wire):
|
62
62
|
|
@@ -99,7 +99,7 @@ class CommutativeCancellation(TransformationPass):
|
|
99
99
|
# - For 2qbit gates the key: (gate_type, first_qbit, sec_qbit, first commutation_set_id,
|
100
100
|
# sec_commutation_set_id), the value is the list gates that share the same gate type,
|
101
101
|
# qubits and commutation sets.
|
102
|
-
for wire in dag.
|
102
|
+
for wire in dag.qubits:
|
103
103
|
wire_commutation_set = self.property_set["commutation_set"][wire]
|
104
104
|
|
105
105
|
for com_set_idx, com_set in enumerate(wire_commutation_set):
|
@@ -131,7 +131,7 @@ class ConsolidateBlocks(TransformationPass):
|
|
131
131
|
if ( # pylint: disable=too-many-boolean-expressions
|
132
132
|
self.force_consolidate
|
133
133
|
or unitary.num_qubits > 2
|
134
|
-
or self.decomposer.num_basis_gates(
|
134
|
+
or self.decomposer.num_basis_gates(matrix) < basis_count
|
135
135
|
or len(block) > max_2q_depth
|
136
136
|
or ((self.basis_gates is not None) and outside_basis)
|
137
137
|
or ((self.target is not None) and outside_basis)
|
@@ -14,11 +14,21 @@
|
|
14
14
|
|
15
15
|
from qiskit.transpiler.basepasses import TransformationPass
|
16
16
|
from qiskit.transpiler.passes.utils import control_flow
|
17
|
+
from qiskit.utils.deprecation import deprecate_func
|
17
18
|
|
18
19
|
|
19
20
|
class CXCancellation(TransformationPass):
|
20
21
|
"""Cancel back-to-back ``cx`` gates in dag."""
|
21
22
|
|
23
|
+
@deprecate_func(
|
24
|
+
additional_msg=(
|
25
|
+
"Instead, use :class:`~.InverseCancellation`, which is a more generic pass."
|
26
|
+
),
|
27
|
+
since="1.1.0",
|
28
|
+
)
|
29
|
+
def __init__(self, *args, **kwargs):
|
30
|
+
super().__init__(*args, **kwargs)
|
31
|
+
|
22
32
|
@control_flow.trivial_recurse
|
23
33
|
def run(self, dag):
|
24
34
|
"""Run the CXCancellation pass on `dag`.
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# This code is part of Qiskit.
|
2
|
+
#
|
3
|
+
# (C) Copyright IBM 2023
|
4
|
+
#
|
5
|
+
# This code is licensed under the Apache License, Version 2.0. You may
|
6
|
+
# obtain a copy of this license in the LICENSE.txt file in the root directory
|
7
|
+
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
|
8
|
+
#
|
9
|
+
# Any modifications or derivative works of this code must retain this
|
10
|
+
# copyright notice, and modified files need to carry a notice indicating
|
11
|
+
# that they have been altered from the originals.
|
12
|
+
|
13
|
+
|
14
|
+
"""Remove any swap gates in the circuit by pushing it through into a qubit permutation."""
|
15
|
+
|
16
|
+
import logging
|
17
|
+
|
18
|
+
from qiskit.circuit.library.standard_gates import SwapGate
|
19
|
+
from qiskit.circuit.library.generalized_gates import PermutationGate
|
20
|
+
from qiskit.transpiler.basepasses import TransformationPass
|
21
|
+
from qiskit.transpiler.layout import Layout
|
22
|
+
|
23
|
+
logger = logging.getLogger(__name__)
|
24
|
+
|
25
|
+
|
26
|
+
class ElidePermutations(TransformationPass):
|
27
|
+
r"""Remove permutation operations from a pre-layout circuit
|
28
|
+
|
29
|
+
This pass is intended to be run before a layout (mapping virtual qubits
|
30
|
+
to physical qubits) is set during the transpilation pipeline. This
|
31
|
+
pass iterates over the :class:`~.DAGCircuit` and when a :class:`~.SwapGate`
|
32
|
+
or :class:`~.PermutationGate` are encountered it permutes the virtual qubits in
|
33
|
+
the circuit and removes the swap gate. This will effectively remove any
|
34
|
+
:class:`~SwapGate`\s or :class:`~PermutationGate` in the circuit prior to running
|
35
|
+
layout. If this pass is run after a layout has been set it will become a no-op
|
36
|
+
(and log a warning) as this optimization is not sound after physical qubits are
|
37
|
+
selected and there are connectivity constraints to adhere to.
|
38
|
+
|
39
|
+
For tracking purposes this pass sets 3 values in the property set if there
|
40
|
+
are any :class:`~.SwapGate` or :class:`~.PermutationGate` objects in the circuit
|
41
|
+
and the pass isn't a no-op.
|
42
|
+
|
43
|
+
* ``original_layout``: The trivial :class:`~.Layout` for the input to this pass being run
|
44
|
+
* ``original_qubit_indices``: The mapping of qubit objects to positional indices for the state
|
45
|
+
of the circuit as input to this pass.
|
46
|
+
* ``virtual_permutation_layout``: A :class:`~.Layout` object mapping input qubits to the output
|
47
|
+
state after eliding permutations.
|
48
|
+
|
49
|
+
These three properties are needed for the transpiler to track the permutations in the out
|
50
|
+
:attr:`.QuantumCircuit.layout` attribute. The elision of permutations is equivalent to a
|
51
|
+
``final_layout`` set by routing and all three of these attributes are needed in the case
|
52
|
+
"""
|
53
|
+
|
54
|
+
def run(self, dag):
|
55
|
+
"""Run the ElidePermutations pass on ``dag``.
|
56
|
+
|
57
|
+
Args:
|
58
|
+
dag (DAGCircuit): the DAG to be optimized.
|
59
|
+
|
60
|
+
Returns:
|
61
|
+
DAGCircuit: the optimized DAG.
|
62
|
+
"""
|
63
|
+
if self.property_set["layout"] is not None:
|
64
|
+
logger.warning(
|
65
|
+
"ElidePermutations is not valid after a layout has been set. This indicates "
|
66
|
+
"an invalid pass manager construction."
|
67
|
+
)
|
68
|
+
return dag
|
69
|
+
|
70
|
+
op_count = dag.count_ops(recurse=False)
|
71
|
+
if op_count.get("swap", 0) == 0 and op_count.get("permutation", 0) == 0:
|
72
|
+
return dag
|
73
|
+
|
74
|
+
new_dag = dag.copy_empty_like()
|
75
|
+
qubit_mapping = list(range(len(dag.qubits)))
|
76
|
+
|
77
|
+
def _apply_mapping(qargs):
|
78
|
+
return tuple(dag.qubits[qubit_mapping[dag.find_bit(qubit).index]] for qubit in qargs)
|
79
|
+
|
80
|
+
for node in dag.topological_op_nodes():
|
81
|
+
if not isinstance(node.op, (SwapGate, PermutationGate)):
|
82
|
+
new_dag.apply_operation_back(
|
83
|
+
node.op, _apply_mapping(node.qargs), node.cargs, check=False
|
84
|
+
)
|
85
|
+
elif getattr(node.op, "condition", None) is not None:
|
86
|
+
new_dag.apply_operation_back(
|
87
|
+
node.op, _apply_mapping(node.qargs), node.cargs, check=False
|
88
|
+
)
|
89
|
+
elif isinstance(node.op, SwapGate):
|
90
|
+
index_0 = dag.find_bit(node.qargs[0]).index
|
91
|
+
index_1 = dag.find_bit(node.qargs[1]).index
|
92
|
+
qubit_mapping[index_1], qubit_mapping[index_0] = (
|
93
|
+
qubit_mapping[index_0],
|
94
|
+
qubit_mapping[index_1],
|
95
|
+
)
|
96
|
+
elif isinstance(node.op, PermutationGate):
|
97
|
+
starting_indices = [qubit_mapping[dag.find_bit(qarg).index] for qarg in node.qargs]
|
98
|
+
pattern = node.op.params[0]
|
99
|
+
pattern_indices = [qubit_mapping[idx] for idx in pattern]
|
100
|
+
for i, j in zip(starting_indices, pattern_indices):
|
101
|
+
qubit_mapping[i] = j
|
102
|
+
input_qubit_mapping = {qubit: index for index, qubit in enumerate(dag.qubits)}
|
103
|
+
self.property_set["original_layout"] = Layout(input_qubit_mapping)
|
104
|
+
if self.property_set["original_qubit_indices"] is None:
|
105
|
+
self.property_set["original_qubit_indices"] = input_qubit_mapping
|
106
|
+
|
107
|
+
new_layout = Layout({dag.qubits[out]: idx for idx, out in enumerate(qubit_mapping)})
|
108
|
+
if current_layout := self.property_set["virtual_permutation_layout"]:
|
109
|
+
self.property_set["virtual_permutation_layout"] = new_layout.compose(
|
110
|
+
current_layout.inverse(dag.qubits, dag.qubits), dag.qubits
|
111
|
+
)
|
112
|
+
else:
|
113
|
+
self.property_set["virtual_permutation_layout"] = new_layout
|
114
|
+
return new_dag
|
@@ -34,6 +34,7 @@ from qiskit.circuit.library.standard_gates import (
|
|
34
34
|
)
|
35
35
|
from qiskit.circuit import Qubit
|
36
36
|
from qiskit.dagcircuit.dagcircuit import DAGCircuit
|
37
|
+
from qiskit.dagcircuit.dagnode import DAGOpNode
|
37
38
|
|
38
39
|
|
39
40
|
logger = logging.getLogger(__name__)
|
@@ -213,10 +214,15 @@ class Optimize1qGatesDecomposition(TransformationPass):
|
|
213
214
|
if best_circuit_sequence is not None and self._substitution_checks(
|
214
215
|
dag, run, best_circuit_sequence, basis, qubit
|
215
216
|
):
|
216
|
-
|
217
|
-
|
217
|
+
for gate_name, angles in best_circuit_sequence:
|
218
|
+
op = NAME_MAP[gate_name](*angles)
|
219
|
+
node = DAGOpNode(NAME_MAP[gate_name](*angles), run[0].qargs, dag=dag)
|
220
|
+
node._node_id = dag._multi_graph.add_node(node)
|
221
|
+
dag._increment_op(op)
|
222
|
+
dag._multi_graph.insert_node_on_in_edges(node._node_id, run[0]._node_id)
|
223
|
+
dag.global_phase += best_circuit_sequence.global_phase
|
218
224
|
# Delete the other nodes in the run
|
219
|
-
for current_node in run
|
225
|
+
for current_node in run:
|
220
226
|
dag.remove_op_node(current_node)
|
221
227
|
|
222
228
|
return dag
|
@@ -12,12 +12,19 @@
|
|
12
12
|
|
13
13
|
"""Optimize annotated operations on a circuit."""
|
14
14
|
|
15
|
-
from typing import Optional, List, Tuple
|
15
|
+
from typing import Optional, List, Tuple, Union
|
16
16
|
|
17
17
|
from qiskit.circuit.controlflow import CONTROL_FLOW_OP_NAMES
|
18
18
|
from qiskit.converters import circuit_to_dag, dag_to_circuit
|
19
19
|
from qiskit.circuit.annotated_operation import AnnotatedOperation, _canonicalize_modifiers
|
20
|
-
from qiskit.circuit import
|
20
|
+
from qiskit.circuit import (
|
21
|
+
QuantumCircuit,
|
22
|
+
Instruction,
|
23
|
+
EquivalenceLibrary,
|
24
|
+
ControlledGate,
|
25
|
+
Operation,
|
26
|
+
ControlFlowOp,
|
27
|
+
)
|
21
28
|
from qiskit.transpiler.basepasses import TransformationPass
|
22
29
|
from qiskit.transpiler.passes.utils import control_flow
|
23
30
|
from qiskit.transpiler.target import Target
|
@@ -43,6 +50,11 @@ class OptimizeAnnotated(TransformationPass):
|
|
43
50
|
``g2 = AnnotatedOperation(g1, ControlModifier(2))``, then ``g2`` can be replaced with
|
44
51
|
``AnnotatedOperation(SwapGate(), [InverseModifier(), ControlModifier(2)])``.
|
45
52
|
|
53
|
+
* Applies conjugate reduction to annotated operations. As an example,
|
54
|
+
``control - [P -- Q -- P^{-1}]`` can be rewritten as ``P -- control - [Q] -- P^{-1}``,
|
55
|
+
that is, only the middle part needs to be controlled. This also works for inverse
|
56
|
+
and power modifiers.
|
57
|
+
|
46
58
|
"""
|
47
59
|
|
48
60
|
def __init__(
|
@@ -51,6 +63,7 @@ class OptimizeAnnotated(TransformationPass):
|
|
51
63
|
equivalence_library: Optional[EquivalenceLibrary] = None,
|
52
64
|
basis_gates: Optional[List[str]] = None,
|
53
65
|
recurse: bool = True,
|
66
|
+
do_conjugate_reduction: bool = True,
|
54
67
|
):
|
55
68
|
"""
|
56
69
|
OptimizeAnnotated initializer.
|
@@ -67,17 +80,19 @@ class OptimizeAnnotated(TransformationPass):
|
|
67
80
|
not applied when neither is specified since such objects do not need to
|
68
81
|
be synthesized). Setting this value to ``False`` precludes the recursion in
|
69
82
|
every case.
|
83
|
+
do_conjugate_reduction: controls whether conjugate reduction should be performed.
|
70
84
|
"""
|
71
85
|
super().__init__()
|
72
86
|
|
73
87
|
self._target = target
|
74
88
|
self._equiv_lib = equivalence_library
|
75
89
|
self._basis_gates = basis_gates
|
90
|
+
self._do_conjugate_reduction = do_conjugate_reduction
|
76
91
|
|
77
92
|
self._top_level_only = not recurse or (self._basis_gates is None and self._target is None)
|
78
93
|
|
79
94
|
if not self._top_level_only and self._target is None:
|
80
|
-
basic_insts = {"measure", "reset", "barrier", "snapshot", "delay"}
|
95
|
+
basic_insts = {"measure", "reset", "barrier", "snapshot", "delay", "store"}
|
81
96
|
self._device_insts = basic_insts | set(self._basis_gates)
|
82
97
|
|
83
98
|
def run(self, dag: DAGCircuit):
|
@@ -122,7 +137,11 @@ class OptimizeAnnotated(TransformationPass):
|
|
122
137
|
# as they may remove annotated gates.
|
123
138
|
dag, opt2 = self._recurse(dag)
|
124
139
|
|
125
|
-
|
140
|
+
opt3 = False
|
141
|
+
if not self._top_level_only and self._do_conjugate_reduction:
|
142
|
+
dag, opt3 = self._conjugate_reduction(dag)
|
143
|
+
|
144
|
+
return dag, opt1 or opt2 or opt3
|
126
145
|
|
127
146
|
def _canonicalize(self, dag) -> Tuple[DAGCircuit, bool]:
|
128
147
|
"""
|
@@ -148,17 +167,219 @@ class OptimizeAnnotated(TransformationPass):
|
|
148
167
|
did_something = True
|
149
168
|
return dag, did_something
|
150
169
|
|
151
|
-
def
|
170
|
+
def _conjugate_decomposition(
|
171
|
+
self, dag: DAGCircuit
|
172
|
+
) -> Union[Tuple[DAGCircuit, DAGCircuit, DAGCircuit], None]:
|
152
173
|
"""
|
153
|
-
|
154
|
-
|
155
|
-
|
174
|
+
Decomposes a circuit ``A`` into 3 sub-circuits ``P``, ``Q``, ``R`` such that
|
175
|
+
``A = P -- Q -- R`` and ``R = P^{-1}``.
|
176
|
+
|
177
|
+
This is accomplished by iteratively finding inverse nodes at the front and at the back of the
|
178
|
+
circuit.
|
156
179
|
"""
|
157
180
|
|
158
|
-
#
|
159
|
-
|
160
|
-
|
181
|
+
front_block = [] # nodes collected from the front of the circuit (aka P)
|
182
|
+
back_block = [] # nodes collected from the back of the circuit (aka R)
|
183
|
+
|
184
|
+
# Stores in- and out- degree for each node. These degrees are computed at the start of this
|
185
|
+
# function and are updated when nodes are collected into front_block or into back_block.
|
186
|
+
in_degree = {}
|
187
|
+
out_degree = {}
|
188
|
+
|
189
|
+
# We use dicts to track for each qubit a DAG node at the front of the circuit that involves
|
190
|
+
# this qubit and a DAG node at the end of the circuit that involves this qubit (when exist).
|
191
|
+
# Note that for the DAGCircuit structure for each qubit there can be at most one such front
|
192
|
+
# and such back node.
|
193
|
+
# This allows for an efficient way to find an inverse pair of gates (one from the front and
|
194
|
+
# one from the back of the circuit).
|
195
|
+
# A qubit that was never examined does not appear in these dicts, and a qubit that was examined
|
196
|
+
# but currently is not involved at the front (resp. at the back) of the circuit has the value of
|
197
|
+
# None.
|
198
|
+
front_node_for_qubit = {}
|
199
|
+
back_node_for_qubit = {}
|
200
|
+
|
201
|
+
# Keep the set of nodes that have been moved either to front_block or to back_block
|
202
|
+
processed_nodes = set()
|
203
|
+
|
204
|
+
# Keep the set of qubits that are involved in nodes at the front or at the back of the circuit.
|
205
|
+
# When looking for inverse pairs of gates we will only iterate over these qubits.
|
206
|
+
active_qubits = set()
|
207
|
+
|
208
|
+
# Keep pairs of nodes for which the inverse check was performed and the nodes
|
209
|
+
# were found to be not inverse to each other (memoization).
|
210
|
+
checked_node_pairs = set()
|
211
|
+
|
212
|
+
# compute in- and out- degree for every node
|
213
|
+
# also update information for nodes at the start and at the end of the circuit
|
214
|
+
for node in dag.op_nodes():
|
215
|
+
preds = list(dag.op_predecessors(node))
|
216
|
+
in_degree[node] = len(preds)
|
217
|
+
if len(preds) == 0:
|
218
|
+
for q in node.qargs:
|
219
|
+
front_node_for_qubit[q] = node
|
220
|
+
active_qubits.add(q)
|
221
|
+
succs = list(dag.op_successors(node))
|
222
|
+
out_degree[node] = len(succs)
|
223
|
+
if len(succs) == 0:
|
224
|
+
for q in node.qargs:
|
225
|
+
back_node_for_qubit[q] = node
|
226
|
+
active_qubits.add(q)
|
227
|
+
|
228
|
+
# iterate while there is a possibility to find more inverse pairs
|
229
|
+
while len(active_qubits) > 0:
|
230
|
+
to_check = active_qubits.copy()
|
231
|
+
active_qubits.clear()
|
232
|
+
|
233
|
+
# For each qubit q, check whether the gate at the front of the circuit that involves q
|
234
|
+
# and the gate at the end of the circuit that involves q are inverse
|
235
|
+
for q in to_check:
|
236
|
+
|
237
|
+
if (front_node := front_node_for_qubit.get(q, None)) is None:
|
238
|
+
continue
|
239
|
+
if (back_node := back_node_for_qubit.get(q, None)) is None:
|
240
|
+
continue
|
241
|
+
|
242
|
+
# front_node or back_node could be already collected when considering other qubits
|
243
|
+
if front_node in processed_nodes or back_node in processed_nodes:
|
244
|
+
continue
|
245
|
+
|
246
|
+
# it is possible that the same node is both at the front and at the back,
|
247
|
+
# it should not be collected
|
248
|
+
if front_node == back_node:
|
249
|
+
continue
|
250
|
+
|
251
|
+
# have been checked before
|
252
|
+
if (front_node, back_node) in checked_node_pairs:
|
253
|
+
continue
|
254
|
+
|
255
|
+
# fast check based on the arguments
|
256
|
+
if front_node.qargs != back_node.qargs or front_node.cargs != back_node.cargs:
|
257
|
+
continue
|
258
|
+
|
259
|
+
# in the future we want to include a more precise check whether a pair
|
260
|
+
# of nodes are inverse
|
261
|
+
if front_node.op == back_node.op.inverse():
|
262
|
+
# update front_node_for_qubit and back_node_for_qubit
|
263
|
+
for q in front_node.qargs:
|
264
|
+
front_node_for_qubit[q] = None
|
265
|
+
for q in back_node.qargs:
|
266
|
+
back_node_for_qubit[q] = None
|
267
|
+
|
268
|
+
# see which other nodes become at the front and update information
|
269
|
+
for node in dag.op_successors(front_node):
|
270
|
+
if node not in processed_nodes:
|
271
|
+
in_degree[node] -= 1
|
272
|
+
if in_degree[node] == 0:
|
273
|
+
for q in node.qargs:
|
274
|
+
front_node_for_qubit[q] = node
|
275
|
+
active_qubits.add(q)
|
276
|
+
|
277
|
+
# see which other nodes become at the back and update information
|
278
|
+
for node in dag.op_predecessors(back_node):
|
279
|
+
if node not in processed_nodes:
|
280
|
+
out_degree[node] -= 1
|
281
|
+
if out_degree[node] == 0:
|
282
|
+
for q in node.qargs:
|
283
|
+
back_node_for_qubit[q] = node
|
284
|
+
active_qubits.add(q)
|
285
|
+
|
286
|
+
# collect and mark as processed
|
287
|
+
front_block.append(front_node)
|
288
|
+
back_block.append(back_node)
|
289
|
+
processed_nodes.add(front_node)
|
290
|
+
processed_nodes.add(back_node)
|
291
|
+
|
292
|
+
else:
|
293
|
+
checked_node_pairs.add((front_node, back_node))
|
294
|
+
|
295
|
+
# if nothing is found, return None
|
296
|
+
if len(front_block) == 0:
|
297
|
+
return None
|
298
|
+
|
299
|
+
# create the output DAGs
|
300
|
+
front_circuit = dag.copy_empty_like()
|
301
|
+
middle_circuit = dag.copy_empty_like()
|
302
|
+
back_circuit = dag.copy_empty_like()
|
303
|
+
front_circuit.global_phase = 0
|
304
|
+
back_circuit.global_phase = 0
|
305
|
+
|
306
|
+
for node in front_block:
|
307
|
+
front_circuit.apply_operation_back(node.op, node.qargs, node.cargs)
|
308
|
+
|
309
|
+
for node in back_block:
|
310
|
+
back_circuit.apply_operation_front(node.op, node.qargs, node.cargs)
|
311
|
+
|
312
|
+
for node in dag.op_nodes():
|
313
|
+
if node not in processed_nodes:
|
314
|
+
middle_circuit.apply_operation_back(node.op, node.qargs, node.cargs)
|
315
|
+
|
316
|
+
return front_circuit, middle_circuit, back_circuit
|
317
|
+
|
318
|
+
def _conjugate_reduce_op(
|
319
|
+
self, op: AnnotatedOperation, base_decomposition: Tuple[DAGCircuit, DAGCircuit, DAGCircuit]
|
320
|
+
) -> Operation:
|
321
|
+
"""
|
322
|
+
We are given an annotated-operation ``op = M [ B ]`` (where ``B`` is the base operation and
|
323
|
+
``M`` is the list of modifiers) and the "conjugate decomposition" of the definition of ``B``,
|
324
|
+
i.e. ``B = P * Q * R``, with ``R = P^{-1}`` (with ``P``, ``Q`` and ``R`` represented as
|
325
|
+
``DAGCircuit`` objects).
|
326
|
+
|
327
|
+
Let ``IQ`` denote a new custom instruction with definitions ``Q``.
|
328
|
+
|
329
|
+
We return the operation ``op_new`` which a new custom instruction with definition
|
330
|
+
``P * A * R``, where ``A`` is a new annotated-operation with modifiers ``M`` and
|
331
|
+
base gate ``IQ``.
|
332
|
+
"""
|
333
|
+
p_dag, q_dag, r_dag = base_decomposition
|
334
|
+
|
335
|
+
q_instr = Instruction(
|
336
|
+
name="iq", num_qubits=op.base_op.num_qubits, num_clbits=op.base_op.num_clbits, params=[]
|
337
|
+
)
|
338
|
+
q_instr.definition = dag_to_circuit(q_dag)
|
339
|
+
|
340
|
+
op_new = Instruction(
|
341
|
+
"optimized", num_qubits=op.num_qubits, num_clbits=op.num_clbits, params=[]
|
342
|
+
)
|
343
|
+
num_control_qubits = op.num_qubits - op.base_op.num_qubits
|
344
|
+
|
345
|
+
circ = QuantumCircuit(op.num_qubits, op.num_clbits)
|
346
|
+
qubits = circ.qubits
|
347
|
+
circ.compose(
|
348
|
+
dag_to_circuit(p_dag), qubits[num_control_qubits : op.num_qubits], inplace=True
|
349
|
+
)
|
350
|
+
circ.append(
|
351
|
+
AnnotatedOperation(base_op=q_instr, modifiers=op.modifiers), range(op.num_qubits)
|
352
|
+
)
|
353
|
+
circ.compose(
|
354
|
+
dag_to_circuit(r_dag), qubits[num_control_qubits : op.num_qubits], inplace=True
|
355
|
+
)
|
356
|
+
op_new.definition = circ
|
357
|
+
return op_new
|
358
|
+
|
359
|
+
def _conjugate_reduction(self, dag) -> Tuple[DAGCircuit, bool]:
|
360
|
+
"""
|
361
|
+
Looks for annotated operations whose base operation has a nontrivial conjugate decomposition.
|
362
|
+
In such cases, the modifiers of the annotated operation can be moved to the "middle" part of
|
363
|
+
the decomposition.
|
161
364
|
|
365
|
+
Returns the modified DAG and whether it did something.
|
366
|
+
"""
|
367
|
+
did_something = False
|
368
|
+
for node in dag.op_nodes(op=AnnotatedOperation):
|
369
|
+
base_op = node.op.base_op
|
370
|
+
if not self._skip_definition(base_op):
|
371
|
+
base_dag = circuit_to_dag(base_op.definition, copy_operations=False)
|
372
|
+
base_decomposition = self._conjugate_decomposition(base_dag)
|
373
|
+
if base_decomposition is not None:
|
374
|
+
new_op = self._conjugate_reduce_op(node.op, base_decomposition)
|
375
|
+
dag.substitute_node(node, new_op)
|
376
|
+
did_something = True
|
377
|
+
return dag, did_something
|
378
|
+
|
379
|
+
def _skip_definition(self, op: Operation) -> bool:
|
380
|
+
"""
|
381
|
+
Returns True if we should not recurse into a gate's definition.
|
382
|
+
"""
|
162
383
|
# Similar to HighLevelSynthesis transpiler pass, we do not recurse into a gate's
|
163
384
|
# `definition` for a gate that is supported by the target or in equivalence library.
|
164
385
|
|
@@ -170,7 +391,22 @@ class OptimizeAnnotated(TransformationPass):
|
|
170
391
|
else op.name in self._device_insts
|
171
392
|
)
|
172
393
|
if inst_supported or (self._equiv_lib is not None and self._equiv_lib.has_entry(op)):
|
173
|
-
return
|
394
|
+
return True
|
395
|
+
return False
|
396
|
+
|
397
|
+
def _recursively_process_definitions(self, op: Operation) -> bool:
|
398
|
+
"""
|
399
|
+
Recursively applies optimizations to op's definition (or to op.base_op's
|
400
|
+
definition if op is an annotated operation).
|
401
|
+
Returns True if did something.
|
402
|
+
"""
|
403
|
+
|
404
|
+
# If op is an annotated operation, we descend into its base_op
|
405
|
+
if isinstance(op, AnnotatedOperation):
|
406
|
+
return self._recursively_process_definitions(op.base_op)
|
407
|
+
|
408
|
+
if self._skip_definition(op):
|
409
|
+
return False
|
174
410
|
|
175
411
|
try:
|
176
412
|
# extract definition
|