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
@@ -55,6 +55,9 @@ class ExprVisitor(typing.Generic[_T_co]):
|
|
55
55
|
def visit_cast(self, node: expr.Cast, /) -> _T_co: # pragma: no cover
|
56
56
|
return self.visit_generic(node)
|
57
57
|
|
58
|
+
def visit_index(self, node: expr.Index, /) -> _T_co: # pragma: no cover
|
59
|
+
return self.visit_generic(node)
|
60
|
+
|
58
61
|
|
59
62
|
class _VarWalkerImpl(ExprVisitor[typing.Iterable[expr.Var]]):
|
60
63
|
__slots__ = ()
|
@@ -75,6 +78,10 @@ class _VarWalkerImpl(ExprVisitor[typing.Iterable[expr.Var]]):
|
|
75
78
|
def visit_cast(self, node, /):
|
76
79
|
yield from node.operand.accept(self)
|
77
80
|
|
81
|
+
def visit_index(self, node, /):
|
82
|
+
yield from node.target.accept(self)
|
83
|
+
yield from node.index.accept(self)
|
84
|
+
|
78
85
|
|
79
86
|
_VAR_WALKER = _VarWalkerImpl()
|
80
87
|
|
@@ -164,6 +171,16 @@ class _StructuralEquivalenceImpl(ExprVisitor[bool]):
|
|
164
171
|
self.other = self.other.operand
|
165
172
|
return node.operand.accept(self)
|
166
173
|
|
174
|
+
def visit_index(self, node, /):
|
175
|
+
if self.other.__class__ is not node.__class__ or self.other.type != node.type:
|
176
|
+
return False
|
177
|
+
other = self.other
|
178
|
+
self.other = other.target
|
179
|
+
if not node.target.accept(self):
|
180
|
+
return False
|
181
|
+
self.other = other.index
|
182
|
+
return node.index.accept(self)
|
183
|
+
|
167
184
|
|
168
185
|
def structurally_equivalent(
|
169
186
|
left: expr.Expr,
|
@@ -215,3 +232,69 @@ def structurally_equivalent(
|
|
215
232
|
True
|
216
233
|
"""
|
217
234
|
return left.accept(_StructuralEquivalenceImpl(right, left_var_key, right_var_key))
|
235
|
+
|
236
|
+
|
237
|
+
class _IsLValueImpl(ExprVisitor[bool]):
|
238
|
+
__slots__ = ()
|
239
|
+
|
240
|
+
def visit_var(self, node, /):
|
241
|
+
return True
|
242
|
+
|
243
|
+
def visit_value(self, node, /):
|
244
|
+
return False
|
245
|
+
|
246
|
+
def visit_unary(self, node, /):
|
247
|
+
return False
|
248
|
+
|
249
|
+
def visit_binary(self, node, /):
|
250
|
+
return False
|
251
|
+
|
252
|
+
def visit_cast(self, node, /):
|
253
|
+
return False
|
254
|
+
|
255
|
+
def visit_index(self, node, /):
|
256
|
+
return node.target.accept(self)
|
257
|
+
|
258
|
+
|
259
|
+
_IS_LVALUE = _IsLValueImpl()
|
260
|
+
|
261
|
+
|
262
|
+
def is_lvalue(node: expr.Expr, /) -> bool:
|
263
|
+
"""Return whether this expression can be used in l-value positions, that is, whether it has a
|
264
|
+
well-defined location in memory, such as one that might be writeable.
|
265
|
+
|
266
|
+
Being an l-value is a necessary but not sufficient for this location to be writeable; it is
|
267
|
+
permissible that a larger object containing this memory location may not allow writing from
|
268
|
+
the scope that attempts to write to it. This would be an access property of the containing
|
269
|
+
program, however, and not an inherent property of the expression system.
|
270
|
+
|
271
|
+
Examples:
|
272
|
+
Literal values are never l-values; there's no memory location associated with (for example)
|
273
|
+
the constant ``1``::
|
274
|
+
|
275
|
+
>>> from qiskit.circuit.classical import expr
|
276
|
+
>>> expr.is_lvalue(expr.lift(2))
|
277
|
+
False
|
278
|
+
|
279
|
+
:class:`~.expr.Var` nodes are always l-values, because they always have some associated
|
280
|
+
memory location::
|
281
|
+
|
282
|
+
>>> from qiskit.circuit.classical import types
|
283
|
+
>>> from qiskit.circuit import Clbit
|
284
|
+
>>> expr.is_lvalue(expr.Var.new("a", types.Bool()))
|
285
|
+
True
|
286
|
+
>>> expr.is_lvalue(expr.lift(Clbit()))
|
287
|
+
True
|
288
|
+
|
289
|
+
Currently there are no unary or binary operations on variables that can produce an l-value
|
290
|
+
expression, but it is likely in the future that some sort of "indexing" operation will be
|
291
|
+
added, which could produce l-values::
|
292
|
+
|
293
|
+
>>> a = expr.Var.new("a", types.Uint(8))
|
294
|
+
>>> b = expr.Var.new("b", types.Uint(8))
|
295
|
+
>>> expr.is_lvalue(a) and expr.is_lvalue(b)
|
296
|
+
True
|
297
|
+
>>> expr.is_lvalue(expr.bit_and(a, b))
|
298
|
+
False
|
299
|
+
"""
|
300
|
+
return node.accept(_IS_LVALUE)
|
@@ -47,13 +47,14 @@ type, which may be slightly different to the 'classical' programming languages y
|
|
47
47
|
Working with types
|
48
48
|
==================
|
49
49
|
|
50
|
-
There are some functions on these types
|
51
|
-
only in manipulations of the expression tree;
|
50
|
+
There are some additional functions on these types documented in the subsequent sections.
|
51
|
+
These are mostly expected to be used only in manipulations of the expression tree;
|
52
|
+
users who are building expressions using the
|
52
53
|
:ref:`user-facing construction interface <circuit-classical-expressions-expr-construction>` should
|
53
54
|
not need to use these.
|
54
55
|
|
55
56
|
Partial ordering of types
|
56
|
-
|
57
|
+
=========================
|
57
58
|
|
58
59
|
The type system is equipped with a partial ordering, where :math:`a < b` is interpreted as
|
59
60
|
":math:`a` is a strict subtype of :math:`b`". Note that the partial ordering is a subset of the
|
@@ -78,7 +79,7 @@ Some helper methods are then defined in terms of this low-level :func:`order` pr
|
|
78
79
|
|
79
80
|
|
80
81
|
Casting between types
|
81
|
-
|
82
|
+
=====================
|
82
83
|
|
83
84
|
It is common to need to cast values of one type to another type. The casting rules for this are
|
84
85
|
embedded into the :mod:`types` module. You can query the casting kinds using :func:`cast_kind`:
|
@@ -16,11 +16,15 @@ from functools import lru_cache
|
|
16
16
|
from typing import List, Union
|
17
17
|
import numpy as np
|
18
18
|
|
19
|
+
from qiskit import QiskitError
|
19
20
|
from qiskit.circuit import Qubit
|
20
21
|
from qiskit.circuit.operation import Operation
|
21
|
-
from qiskit.circuit.controlflow import
|
22
|
+
from qiskit.circuit.controlflow import CONTROL_FLOW_OP_NAMES
|
22
23
|
from qiskit.quantum_info.operators import Operator
|
23
24
|
|
25
|
+
_skipped_op_names = {"measure", "reset", "delay", "initialize"}
|
26
|
+
_no_cache_op_names = {"annotated"}
|
27
|
+
|
24
28
|
|
25
29
|
@lru_cache(maxsize=None)
|
26
30
|
def _identity_op(num_qubits):
|
@@ -94,8 +98,11 @@ class CommutationChecker:
|
|
94
98
|
)
|
95
99
|
first_op, first_qargs, _ = first_op_tuple
|
96
100
|
second_op, second_qargs, _ = second_op_tuple
|
97
|
-
|
98
|
-
|
101
|
+
|
102
|
+
skip_cache = first_op.name in _no_cache_op_names or second_op.name in _no_cache_op_names
|
103
|
+
|
104
|
+
if skip_cache:
|
105
|
+
return _commute_matmul(first_op, first_qargs, second_op, second_qargs)
|
99
106
|
|
100
107
|
commutation_lookup = self.check_commutation_entries(
|
101
108
|
first_op, first_qargs, second_op, second_qargs
|
@@ -113,6 +120,8 @@ class CommutationChecker:
|
|
113
120
|
if self._current_cache_entries >= self._cache_max_entries:
|
114
121
|
self.clear_cached_commutations()
|
115
122
|
|
123
|
+
first_params = getattr(first_op, "params", [])
|
124
|
+
second_params = getattr(second_op, "params", [])
|
116
125
|
if len(first_params) > 0 or len(second_params) > 0:
|
117
126
|
self._cached_commutations.setdefault((first_op.name, second_op.name), {}).setdefault(
|
118
127
|
_get_relative_placement(first_qargs, second_qargs), {}
|
@@ -184,7 +193,11 @@ class CommutationChecker:
|
|
184
193
|
|
185
194
|
|
186
195
|
def _hashable_parameters(params):
|
187
|
-
"""Convert the parameters of a gate into a hashable format for lookup in a dictionary.
|
196
|
+
"""Convert the parameters of a gate into a hashable format for lookup in a dictionary.
|
197
|
+
|
198
|
+
This aims to be fast in common cases, and is not intended to work outside of the lifetime of a
|
199
|
+
single commutation pass; it does not handle mutable state correctly if the state is actually
|
200
|
+
changed."""
|
188
201
|
try:
|
189
202
|
hash(params)
|
190
203
|
return params
|
@@ -201,7 +214,53 @@ def _hashable_parameters(params):
|
|
201
214
|
return ("fallback", str(params))
|
202
215
|
|
203
216
|
|
204
|
-
|
217
|
+
def is_commutation_supported(op):
|
218
|
+
"""
|
219
|
+
Filter operations whose commutation is not supported due to bugs in transpiler passes invoking
|
220
|
+
commutation analysis.
|
221
|
+
Args:
|
222
|
+
op (Operation): operation to be checked for commutation relation
|
223
|
+
Return:
|
224
|
+
True if determining the commutation of op is currently supported
|
225
|
+
"""
|
226
|
+
# Bug in CommutativeCancellation, e.g. see gh-8553
|
227
|
+
if getattr(op, "condition", False):
|
228
|
+
return False
|
229
|
+
|
230
|
+
# Commutation of ControlFlow gates also not supported yet. This may be pending a control flow graph.
|
231
|
+
if op.name in CONTROL_FLOW_OP_NAMES:
|
232
|
+
return False
|
233
|
+
|
234
|
+
return True
|
235
|
+
|
236
|
+
|
237
|
+
def is_commutation_skipped(op, qargs, max_num_qubits):
|
238
|
+
"""
|
239
|
+
Filter operations whose commutation will not be determined.
|
240
|
+
Args:
|
241
|
+
op (Operation): operation to be checked for commutation relation
|
242
|
+
qargs (List): operation qubits
|
243
|
+
max_num_qubits (int): the maximum number of qubits to consider, the check may be skipped if
|
244
|
+
the number of qubits for either operation exceeds this amount.
|
245
|
+
Return:
|
246
|
+
True if determining the commutation of op is currently not supported
|
247
|
+
"""
|
248
|
+
if (
|
249
|
+
len(qargs) > max_num_qubits
|
250
|
+
or getattr(op, "_directive", False)
|
251
|
+
or op.name in _skipped_op_names
|
252
|
+
):
|
253
|
+
return True
|
254
|
+
|
255
|
+
if getattr(op, "is_parameterized", False) and op.is_parameterized():
|
256
|
+
return True
|
257
|
+
|
258
|
+
# we can proceed if op has defined: to_operator, to_matrix and __array__, or if its definition can be
|
259
|
+
# recursively resolved by operations that have a matrix. We check this by constructing an Operator.
|
260
|
+
if (hasattr(op, "to_matrix") and hasattr(op, "__array__")) or hasattr(op, "to_operator"):
|
261
|
+
return False
|
262
|
+
|
263
|
+
return False
|
205
264
|
|
206
265
|
|
207
266
|
def _commutation_precheck(
|
@@ -213,43 +272,14 @@ def _commutation_precheck(
|
|
213
272
|
cargs2: List,
|
214
273
|
max_num_qubits,
|
215
274
|
):
|
216
|
-
|
217
|
-
|
218
|
-
# We don't support commutation of conditional gates for now due to bugs in
|
219
|
-
# CommutativeCancellation. See gh-8553.
|
220
|
-
if getattr(op1, "condition", None) is not None or getattr(op2, "condition", None) is not None:
|
275
|
+
if not is_commutation_supported(op1) or not is_commutation_supported(op2):
|
221
276
|
return False
|
222
277
|
|
223
|
-
|
224
|
-
# pending a control flow graph.
|
225
|
-
if isinstance(op1, ControlFlowOp) or isinstance(op2, ControlFlowOp):
|
226
|
-
return False
|
227
|
-
|
228
|
-
# These lines are adapted from dag_dependency and say that two gates over
|
229
|
-
# different quantum and classical bits necessarily commute. This is more
|
230
|
-
# permissive that the check from commutation_analysis, as for example it
|
231
|
-
# allows to commute X(1) and Measure(0, 0).
|
232
|
-
# Presumably this check was not present in commutation_analysis as
|
233
|
-
# it was only called on pairs of connected nodes from DagCircuit.
|
234
|
-
intersection_q = set(qargs1).intersection(set(qargs2))
|
235
|
-
intersection_c = set(cargs1).intersection(set(cargs2))
|
236
|
-
if not (intersection_q or intersection_c):
|
278
|
+
if set(qargs1).isdisjoint(qargs2) and set(cargs1).isdisjoint(cargs2):
|
237
279
|
return True
|
238
280
|
|
239
|
-
|
240
|
-
|
241
|
-
return False
|
242
|
-
|
243
|
-
# These lines are adapted from commutation_analysis, which is more restrictive than the
|
244
|
-
# check from dag_dependency when considering nodes with "_directive". It would be nice to
|
245
|
-
# think which optimizations from dag_dependency can indeed be used.
|
246
|
-
if op1.name in _skipped_op_names or op2.name in _skipped_op_names:
|
247
|
-
return False
|
248
|
-
|
249
|
-
if getattr(op1, "_directive", False) or getattr(op2, "_directive", False):
|
250
|
-
return False
|
251
|
-
if (getattr(op1, "is_parameterized", False) and op1.is_parameterized()) or (
|
252
|
-
getattr(op2, "is_parameterized", False) and op2.is_parameterized()
|
281
|
+
if is_commutation_skipped(op1, qargs1, max_num_qubits) or is_commutation_skipped(
|
282
|
+
op2, qargs2, max_num_qubits
|
253
283
|
):
|
254
284
|
return False
|
255
285
|
|
@@ -264,13 +294,11 @@ def _get_relative_placement(first_qargs: List[Qubit], second_qargs: List[Qubit])
|
|
264
294
|
second_qargs (DAGOpNode): second gate
|
265
295
|
|
266
296
|
Return:
|
267
|
-
A tuple that describes the relative qubit placement.
|
268
|
-
gate qubit arrangements as q2^{-1}[q1[i]] where q1[i] is the ith qubit of the first gate and
|
269
|
-
q2^{-1}[q] returns the qubit index of qubit q in the second gate (possibly 'None'). E.g.
|
297
|
+
A tuple that describes the relative qubit placement: E.g.
|
270
298
|
_get_relative_placement(CX(0, 1), CX(1, 2)) would return (None, 0) as there is no overlap on
|
271
299
|
the first qubit of the first gate but there is an overlap on the second qubit of the first gate,
|
272
|
-
i.e. qubit 0 of the second gate. Likewise,
|
273
|
-
return (1, None)
|
300
|
+
i.e. qubit 0 of the second gate. Likewise,
|
301
|
+
_get_relative_placement(CX(1, 2), CX(0, 1)) would return (1, None)
|
274
302
|
"""
|
275
303
|
qubits_g2 = {q_g1: i_g1 for i_g1, q_g1 in enumerate(second_qargs)}
|
276
304
|
return tuple(qubits_g2.get(q_g0, None) for q_g0 in first_qargs)
|
@@ -285,7 +313,7 @@ def _persistent_id(op_name: str) -> int:
|
|
285
313
|
Return:
|
286
314
|
The integer id of the input string.
|
287
315
|
"""
|
288
|
-
return int.from_bytes(bytes(op_name, encoding="
|
316
|
+
return int.from_bytes(bytes(op_name, encoding="utf-8"), byteorder="big", signed=True)
|
289
317
|
|
290
318
|
|
291
319
|
def _order_operations(
|
@@ -355,8 +383,10 @@ def _query_commutation(
|
|
355
383
|
# if we have another dict in commutation_after_placement, commutation depends on params
|
356
384
|
if isinstance(commutation_after_placement, dict):
|
357
385
|
# Param commutation entry exists and must be a dict
|
386
|
+
first_params = getattr(first_op, "params", [])
|
387
|
+
second_params = getattr(second_op, "params", [])
|
358
388
|
return commutation_after_placement.get(
|
359
|
-
(_hashable_parameters(
|
389
|
+
(_hashable_parameters(first_params), _hashable_parameters(second_params)),
|
360
390
|
None,
|
361
391
|
)
|
362
392
|
else:
|
@@ -379,12 +409,17 @@ def _commute_matmul(
|
|
379
409
|
first_qarg = tuple(qarg[q] for q in first_qargs)
|
380
410
|
second_qarg = tuple(qarg[q] for q in second_qargs)
|
381
411
|
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
412
|
+
# try to generate an Operator out of op, if this succeeds we can determine commutativity, otherwise
|
413
|
+
# return false
|
414
|
+
try:
|
415
|
+
operator_1 = Operator(
|
416
|
+
first_ops, input_dims=(2,) * len(first_qarg), output_dims=(2,) * len(first_qarg)
|
417
|
+
)
|
418
|
+
operator_2 = Operator(
|
419
|
+
second_op, input_dims=(2,) * len(second_qarg), output_dims=(2,) * len(second_qarg)
|
420
|
+
)
|
421
|
+
except QiskitError:
|
422
|
+
return False
|
388
423
|
|
389
424
|
if first_qarg == second_qarg:
|
390
425
|
# Use full composition if possible to get the fastest matmul paths.
|
@@ -174,8 +174,16 @@ def _unify_circuit_resources_rebuild( # pylint: disable=invalid-name # (it's t
|
|
174
174
|
out_circuits = []
|
175
175
|
for circuit in circuits:
|
176
176
|
out = QuantumCircuit(
|
177
|
-
qubits,
|
177
|
+
qubits,
|
178
|
+
clbits,
|
179
|
+
*circuit.qregs,
|
180
|
+
*circuit.cregs,
|
181
|
+
global_phase=circuit.global_phase,
|
182
|
+
inputs=circuit.iter_input_vars(),
|
183
|
+
captures=circuit.iter_captured_vars(),
|
178
184
|
)
|
185
|
+
for var in circuit.iter_declared_vars():
|
186
|
+
out.add_uninitialized_var(var)
|
179
187
|
for instruction in circuit.data:
|
180
188
|
out._append(instruction)
|
181
189
|
out_circuits.append(out)
|
@@ -19,31 +19,17 @@ from .builder import InstructionPlaceholder, InstructionResources
|
|
19
19
|
|
20
20
|
|
21
21
|
class BreakLoopOp(Instruction):
|
22
|
-
"""A circuit operation which, when encountered, jumps to the end of
|
23
|
-
|
24
|
-
|
25
|
-
.. note:
|
26
|
-
|
27
|
-
Can be inserted only within the body of a loop op, and must span
|
28
|
-
the full width of that block.
|
29
|
-
|
30
|
-
**Circuit symbol:**
|
31
|
-
|
32
|
-
.. parsed-literal::
|
33
|
-
|
34
|
-
┌──────────────┐
|
35
|
-
q_0: ┤0 ├
|
36
|
-
│ │
|
37
|
-
q_1: ┤1 ├
|
38
|
-
│ break_loop │
|
39
|
-
q_2: ┤2 ├
|
40
|
-
│ │
|
41
|
-
c_0: ╡0 ╞
|
42
|
-
└──────────────┘
|
43
|
-
|
22
|
+
"""A circuit operation which, when encountered, jumps to the end of the nearest enclosing loop.
|
23
|
+
Can only be used inside loops.
|
44
24
|
"""
|
45
25
|
|
46
26
|
def __init__(self, num_qubits: int, num_clbits: int, label: Optional[str] = None):
|
27
|
+
"""
|
28
|
+
Args:
|
29
|
+
num_qubits: the number of qubits this affects.
|
30
|
+
num_clbits: the number of qubits this affects.
|
31
|
+
label: an optional string label for the instruction.
|
32
|
+
"""
|
47
33
|
super().__init__("break_loop", num_qubits, num_clbits, [], label=label)
|
48
34
|
|
49
35
|
|
@@ -24,7 +24,8 @@ import itertools
|
|
24
24
|
import typing
|
25
25
|
from typing import Collection, Iterable, FrozenSet, Tuple, Union, Optional, Sequence
|
26
26
|
|
27
|
-
from qiskit._accelerate.
|
27
|
+
from qiskit._accelerate.circuit import CircuitData
|
28
|
+
from qiskit.circuit.classical import expr
|
28
29
|
from qiskit.circuit.classicalregister import Clbit, ClassicalRegister
|
29
30
|
from qiskit.circuit.exceptions import CircuitError
|
30
31
|
from qiskit.circuit.instruction import Instruction
|
@@ -103,6 +104,66 @@ class CircuitScopeInterface(abc.ABC):
|
|
103
104
|
or a :class:`.Clbit` that isn't actually in the circuit.
|
104
105
|
"""
|
105
106
|
|
107
|
+
@abc.abstractmethod
|
108
|
+
def add_uninitialized_var(self, var: expr.Var):
|
109
|
+
"""Add an uninitialized variable to the circuit scope.
|
110
|
+
|
111
|
+
The general circuit context is responsible for ensuring the variable is initialized. These
|
112
|
+
uninitialized variables are guaranteed to be standalone.
|
113
|
+
|
114
|
+
Args:
|
115
|
+
var: the variable to add, if valid.
|
116
|
+
|
117
|
+
Raises:
|
118
|
+
CircuitError: if the variable cannot be added, such as because it invalidly shadows or
|
119
|
+
redefines an existing name.
|
120
|
+
"""
|
121
|
+
|
122
|
+
@abc.abstractmethod
|
123
|
+
def remove_var(self, var: expr.Var):
|
124
|
+
"""Remove a variable from the locals of this scope.
|
125
|
+
|
126
|
+
This is only called in the case that an exception occurred while initializing the variable,
|
127
|
+
and is not exposed to users.
|
128
|
+
|
129
|
+
Args:
|
130
|
+
var: the variable to remove. It can be assumed that this was already the subject of an
|
131
|
+
:meth:`add_uninitialized_var` call.
|
132
|
+
"""
|
133
|
+
|
134
|
+
@abc.abstractmethod
|
135
|
+
def use_var(self, var: expr.Var):
|
136
|
+
"""Called for every standalone classical real-time variable being used by some circuit
|
137
|
+
instruction.
|
138
|
+
|
139
|
+
The given variable is guaranteed to be a stand-alone variable; bit-like resource-wrapping
|
140
|
+
variables will have been filtered out and their resources given to
|
141
|
+
:meth:`resolve_classical_resource`.
|
142
|
+
|
143
|
+
Args:
|
144
|
+
var: the variable to validate.
|
145
|
+
|
146
|
+
Returns:
|
147
|
+
the same variable.
|
148
|
+
|
149
|
+
Raises:
|
150
|
+
CircuitError: if the variable is not valid for this scope.
|
151
|
+
"""
|
152
|
+
|
153
|
+
@abc.abstractmethod
|
154
|
+
def get_var(self, name: str) -> Optional[expr.Var]:
|
155
|
+
"""Get the variable (if any) in scope with the given name.
|
156
|
+
|
157
|
+
This should call up to the parent scope if in a control-flow builder scope, in case the
|
158
|
+
variable exists in an outer scope.
|
159
|
+
|
160
|
+
Args:
|
161
|
+
name: the name of the symbol to lookup.
|
162
|
+
|
163
|
+
Returns:
|
164
|
+
the variable if it is found, otherwise ``None``.
|
165
|
+
"""
|
166
|
+
|
106
167
|
|
107
168
|
class InstructionResources(typing.NamedTuple):
|
108
169
|
"""The quantum and classical resources used within a particular instruction.
|
@@ -271,6 +332,8 @@ class ControlFlowBuilderBlock(CircuitScopeInterface):
|
|
271
332
|
"_parent",
|
272
333
|
"_built",
|
273
334
|
"_forbidden_message",
|
335
|
+
"_vars_local",
|
336
|
+
"_vars_capture",
|
274
337
|
)
|
275
338
|
|
276
339
|
def __init__(
|
@@ -311,6 +374,8 @@ class ControlFlowBuilderBlock(CircuitScopeInterface):
|
|
311
374
|
self._instructions = CircuitData(qubits, clbits)
|
312
375
|
self.registers = set(registers)
|
313
376
|
self.global_phase = 0.0
|
377
|
+
self._vars_local = {}
|
378
|
+
self._vars_capture = {}
|
314
379
|
self._allow_jumps = allow_jumps
|
315
380
|
self._parent = parent
|
316
381
|
self._built = False
|
@@ -393,6 +458,49 @@ class ControlFlowBuilderBlock(CircuitScopeInterface):
|
|
393
458
|
self.add_register(resource)
|
394
459
|
return resource
|
395
460
|
|
461
|
+
def add_uninitialized_var(self, var: expr.Var):
|
462
|
+
if self._built:
|
463
|
+
raise CircuitError("Cannot add resources after the scope has been built.")
|
464
|
+
# We can shadow a name if it was declared in an outer scope, but only if we haven't already
|
465
|
+
# captured it ourselves yet.
|
466
|
+
if (previous := self._vars_local.get(var.name)) is not None:
|
467
|
+
if previous == var:
|
468
|
+
raise CircuitError(f"'{var}' is already present in the scope")
|
469
|
+
raise CircuitError(f"cannot add '{var}' as its name shadows the existing '{previous}'")
|
470
|
+
if var.name in self._vars_capture:
|
471
|
+
raise CircuitError(f"cannot add '{var}' as its name shadows the existing '{previous}'")
|
472
|
+
self._vars_local[var.name] = var
|
473
|
+
|
474
|
+
def remove_var(self, var: expr.Var):
|
475
|
+
if self._built:
|
476
|
+
raise RuntimeError("exception handler 'remove_var' called after scope built")
|
477
|
+
self._vars_local.pop(var.name)
|
478
|
+
|
479
|
+
def get_var(self, name: str):
|
480
|
+
if (out := self._vars_local.get(name)) is not None:
|
481
|
+
return out
|
482
|
+
return self._parent.get_var(name)
|
483
|
+
|
484
|
+
def use_var(self, var: expr.Var):
|
485
|
+
if (local := self._vars_local.get(var.name)) is not None:
|
486
|
+
if local == var:
|
487
|
+
return
|
488
|
+
raise CircuitError(f"cannot use '{var}' which is shadowed by the local '{local}'")
|
489
|
+
if self._vars_capture.get(var.name) == var:
|
490
|
+
return
|
491
|
+
if self._parent.get_var(var.name) != var:
|
492
|
+
raise CircuitError(f"cannot close over '{var}', which is not in scope")
|
493
|
+
self._parent.use_var(var)
|
494
|
+
self._vars_capture[var.name] = var
|
495
|
+
|
496
|
+
def iter_local_vars(self):
|
497
|
+
"""Iterator over the variables currently declared in this scope."""
|
498
|
+
return self._vars_local.values()
|
499
|
+
|
500
|
+
def iter_captured_vars(self):
|
501
|
+
"""Iterator over the variables currently captured in this scope."""
|
502
|
+
return self._vars_capture.values()
|
503
|
+
|
396
504
|
def peek(self) -> CircuitInstruction:
|
397
505
|
"""Get the value of the most recent instruction tuple in this scope."""
|
398
506
|
if not self._instructions:
|
@@ -493,7 +601,12 @@ class ControlFlowBuilderBlock(CircuitScopeInterface):
|
|
493
601
|
self._instructions.clbits,
|
494
602
|
*self.registers,
|
495
603
|
global_phase=self.global_phase,
|
604
|
+
captures=self._vars_capture.values(),
|
496
605
|
)
|
606
|
+
for var in self._vars_local.values():
|
607
|
+
# The requisite `Store` instruction to initialise the variable will have been appended
|
608
|
+
# into the instructions.
|
609
|
+
out.add_uninitialized_var(var)
|
497
610
|
|
498
611
|
# Maps placeholder index to the newly concrete instruction.
|
499
612
|
placeholder_to_concrete = {}
|
@@ -566,6 +679,8 @@ class ControlFlowBuilderBlock(CircuitScopeInterface):
|
|
566
679
|
out._instructions = self._instructions.copy()
|
567
680
|
out.registers = self.registers.copy()
|
568
681
|
out.global_phase = self.global_phase
|
682
|
+
out._vars_local = self._vars_local.copy()
|
683
|
+
out._vars_capture = self._vars_capture.copy()
|
569
684
|
out._parent = self._parent
|
570
685
|
out._allow_jumps = self._allow_jumps
|
571
686
|
out._forbidden_message = self._forbidden_message
|
@@ -19,31 +19,17 @@ from .builder import InstructionPlaceholder, InstructionResources
|
|
19
19
|
|
20
20
|
|
21
21
|
class ContinueLoopOp(Instruction):
|
22
|
-
"""A circuit operation which, when encountered, moves to the next iteration of
|
23
|
-
|
24
|
-
|
25
|
-
.. note::
|
26
|
-
|
27
|
-
Can be inserted only within the body of a loop op, and must span the full
|
28
|
-
width of that block.
|
29
|
-
|
30
|
-
**Circuit symbol:**
|
31
|
-
|
32
|
-
.. parsed-literal::
|
33
|
-
|
34
|
-
┌─────────────────┐
|
35
|
-
q_0: ┤0 ├
|
36
|
-
│ │
|
37
|
-
q_1: ┤1 ├
|
38
|
-
│ continue_loop │
|
39
|
-
q_2: ┤2 ├
|
40
|
-
│ │
|
41
|
-
c_0: ╡0 ╞
|
42
|
-
└─────────────────┘
|
43
|
-
|
22
|
+
"""A circuit operation which, when encountered, moves to the next iteration of the nearest
|
23
|
+
enclosing loop. Can only be used inside loops.
|
44
24
|
"""
|
45
25
|
|
46
26
|
def __init__(self, num_qubits: int, num_clbits: int, label: Optional[str] = None):
|
27
|
+
"""
|
28
|
+
Args:
|
29
|
+
num_qubits: the number of qubits this affects.
|
30
|
+
num_clbits: the number of qubits this affects.
|
31
|
+
label: an optional string label for the instruction.
|
32
|
+
"""
|
47
33
|
super().__init__("continue_loop", num_qubits, num_clbits, [], label=label)
|
48
34
|
|
49
35
|
|