qiskit 1.0.2__cp38-abi3-macosx_10_9_universal2.whl → 1.1.0rc1__cp38-abi3-macosx_10_9_universal2.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.abi3.so +0 -0
- qiskit/_numpy_compat.py +73 -0
- qiskit/assembler/disassemble.py +5 -6
- qiskit/circuit/__init__.py +1131 -169
- qiskit/circuit/_classical_resource_map.py +7 -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/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 +8 -2
- 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 +864 -128
- 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/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/primitives/__init__.py +12 -8
- 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 +5 -4
- qiskit/primitives/containers/bit_array.py +292 -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 +1 -1
- qiskit/primitives/statevector_estimator.py +4 -4
- qiskit/primitives/statevector_sampler.py +7 -12
- qiskit/providers/__init__.py +17 -18
- qiskit/providers/backend.py +2 -2
- qiskit/providers/backend_compat.py +8 -10
- qiskit/providers/basic_provider/basic_provider_tools.py +67 -31
- qiskit/providers/basic_provider/basic_simulator.py +81 -21
- 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/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/qpy/__init__.py +247 -13
- 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 +48 -4
- 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 +54 -33
- 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/mitigation/correlated_readout_mitigator.py +3 -2
- qiskit/result/mitigation/local_readout_mitigator.py +2 -1
- qiskit/result/mitigation/utils.py +3 -2
- qiskit/synthesis/__init__.py +2 -0
- 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 +6 -0
- qiskit/transpiler/passes/basis/basis_translator.py +7 -2
- 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/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/optionals.py +6 -2
- 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/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.0rc1.dist-info}/METADATA +12 -8
- {qiskit-1.0.2.dist-info → qiskit-1.1.0rc1.dist-info}/RECORD +245 -235
- {qiskit-1.0.2.dist-info → qiskit-1.1.0rc1.dist-info}/WHEEL +1 -1
- qiskit/_qasm2.abi3.so +0 -0
- qiskit/_qasm3.abi3.so +0 -0
- {qiskit-1.0.2.dist-info → qiskit-1.1.0rc1.dist-info}/LICENSE.txt +0 -0
- {qiskit-1.0.2.dist-info → qiskit-1.1.0rc1.dist-info}/entry_points.txt +0 -0
- {qiskit-1.0.2.dist-info → qiskit-1.1.0rc1.dist-info}/top_level.txt +0 -0
@@ -37,17 +37,20 @@ class VariableMapper(expr.ExprVisitor[expr.Expr]):
|
|
37
37
|
``ValueError`` will be raised instead. The given ``add_register`` callable may choose to raise
|
38
38
|
its own exception."""
|
39
39
|
|
40
|
-
__slots__ = ("target_cregs", "register_map", "bit_map", "add_register")
|
40
|
+
__slots__ = ("target_cregs", "register_map", "bit_map", "var_map", "add_register")
|
41
41
|
|
42
42
|
def __init__(
|
43
43
|
self,
|
44
44
|
target_cregs: typing.Iterable[ClassicalRegister],
|
45
45
|
bit_map: typing.Mapping[Bit, Bit],
|
46
|
+
var_map: typing.Mapping[expr.Var, expr.Var] | None = None,
|
47
|
+
*,
|
46
48
|
add_register: typing.Callable[[ClassicalRegister], None] | None = None,
|
47
49
|
):
|
48
50
|
self.target_cregs = tuple(target_cregs)
|
49
51
|
self.register_map = {}
|
50
52
|
self.bit_map = bit_map
|
53
|
+
self.var_map = var_map or {}
|
51
54
|
self.add_register = add_register
|
52
55
|
|
53
56
|
def _map_register(self, theirs: ClassicalRegister) -> ClassicalRegister:
|
@@ -110,8 +113,8 @@ class VariableMapper(expr.ExprVisitor[expr.Expr]):
|
|
110
113
|
return (mapped_theirs, mapped_value)
|
111
114
|
|
112
115
|
def map_target(self, target, /):
|
113
|
-
"""Map the
|
114
|
-
as defined in the ``circuit`` argument of the initialiser of this class."""
|
116
|
+
"""Map the real-time variables in a ``target`` of a :class:`.SwitchCaseOp` to the new
|
117
|
+
circuit, as defined in the ``circuit`` argument of the initialiser of this class."""
|
115
118
|
if isinstance(target, Clbit):
|
116
119
|
return self.bit_map[target]
|
117
120
|
if isinstance(target, ClassicalRegister):
|
@@ -127,9 +130,7 @@ class VariableMapper(expr.ExprVisitor[expr.Expr]):
|
|
127
130
|
return expr.Var(self.bit_map[node.var], node.type)
|
128
131
|
if isinstance(node.var, ClassicalRegister):
|
129
132
|
return expr.Var(self._map_register(node.var), node.type)
|
130
|
-
|
131
|
-
# wrong thing (which would be `return node` without mapping, right now).
|
132
|
-
raise RuntimeError(f"unhandled variable in 'compose': {node}") # pragma: no cover
|
133
|
+
return self.var_map.get(node, node)
|
133
134
|
|
134
135
|
def visit_value(self, node, /):
|
135
136
|
return expr.Value(node.value, node.type)
|
qiskit/circuit/_utils.py
CHANGED
@@ -13,7 +13,10 @@
|
|
13
13
|
This module contains utility functions for circuits.
|
14
14
|
"""
|
15
15
|
|
16
|
+
import math
|
16
17
|
import numpy
|
18
|
+
|
19
|
+
from qiskit import _numpy_compat
|
17
20
|
from qiskit.exceptions import QiskitError
|
18
21
|
from qiskit.circuit.exceptions import CircuitError
|
19
22
|
from .parametervector import ParameterVectorElement
|
@@ -56,7 +59,7 @@ def _compute_control_matrix(base_mat, num_ctrl_qubits, ctrl_state=None):
|
|
56
59
|
Raises:
|
57
60
|
QiskitError: unrecognized mode or invalid ctrl_state
|
58
61
|
"""
|
59
|
-
num_target = int(
|
62
|
+
num_target = int(math.log2(base_mat.shape[0]))
|
60
63
|
ctrl_dim = 2**num_ctrl_qubits
|
61
64
|
ctrl_grnd = numpy.repeat([[1], [0]], [1, ctrl_dim - 1])
|
62
65
|
if ctrl_state is None:
|
@@ -116,8 +119,9 @@ def with_gate_array(base_array):
|
|
116
119
|
nonwritable = numpy.array(base_array, dtype=numpy.complex128)
|
117
120
|
nonwritable.setflags(write=False)
|
118
121
|
|
119
|
-
def __array__(_self, dtype=None):
|
120
|
-
|
122
|
+
def __array__(_self, dtype=None, copy=_numpy_compat.COPY_ONLY_IF_NEEDED):
|
123
|
+
dtype = nonwritable.dtype if dtype is None else dtype
|
124
|
+
return numpy.array(nonwritable, dtype=dtype, copy=copy)
|
121
125
|
|
122
126
|
def decorator(cls):
|
123
127
|
if hasattr(cls, "__array__"):
|
@@ -148,15 +152,21 @@ def with_controlled_gate_array(base_array, num_ctrl_qubits, cached_states=None):
|
|
148
152
|
if cached_states is None:
|
149
153
|
nonwritables = [matrix_for_control_state(state) for state in range(2**num_ctrl_qubits)]
|
150
154
|
|
151
|
-
def __array__(self, dtype=None):
|
152
|
-
|
155
|
+
def __array__(self, dtype=None, copy=_numpy_compat.COPY_ONLY_IF_NEEDED):
|
156
|
+
arr = nonwritables[self.ctrl_state]
|
157
|
+
dtype = arr.dtype if dtype is None else dtype
|
158
|
+
return numpy.array(arr, dtype=dtype, copy=copy)
|
153
159
|
|
154
160
|
else:
|
155
161
|
nonwritables = {state: matrix_for_control_state(state) for state in cached_states}
|
156
162
|
|
157
|
-
def __array__(self, dtype=None):
|
158
|
-
if (
|
159
|
-
|
163
|
+
def __array__(self, dtype=None, copy=_numpy_compat.COPY_ONLY_IF_NEEDED):
|
164
|
+
if (arr := nonwritables.get(self.ctrl_state)) is not None:
|
165
|
+
dtype = arr.dtype if dtype is None else dtype
|
166
|
+
return numpy.array(arr, dtype=dtype, copy=copy)
|
167
|
+
|
168
|
+
if copy is False and copy is not _numpy_compat.COPY_ONLY_IF_NEEDED:
|
169
|
+
raise ValueError("could not produce matrix without calculation")
|
160
170
|
return numpy.asarray(
|
161
171
|
_compute_control_matrix(base, num_ctrl_qubits, self.ctrl_state), dtype=dtype
|
162
172
|
)
|
@@ -97,7 +97,10 @@ class AnnotatedOperation(Operation):
|
|
97
97
|
inverted and then controlled by 2 qubits.
|
98
98
|
"""
|
99
99
|
self.base_op = base_op
|
100
|
+
"""The base operation that the modifiers in this annotated operation applies to."""
|
100
101
|
self.modifiers = modifiers if isinstance(modifiers, List) else [modifiers]
|
102
|
+
"""Ordered sequence of the modifiers to apply to :attr:`base_op`. The modifiers are applied
|
103
|
+
in order from lowest index to highest index."""
|
101
104
|
|
102
105
|
@property
|
103
106
|
def name(self):
|
@@ -198,6 +201,24 @@ class AnnotatedOperation(Operation):
|
|
198
201
|
extended_modifiers.append(InverseModifier())
|
199
202
|
return AnnotatedOperation(self.base_op, extended_modifiers)
|
200
203
|
|
204
|
+
def power(self, exponent: float, annotated: bool = False):
|
205
|
+
"""
|
206
|
+
Raise this gate to the power of ``exponent``.
|
207
|
+
|
208
|
+
Implemented as an annotated operation, see :class:`.AnnotatedOperation`.
|
209
|
+
|
210
|
+
Args:
|
211
|
+
exponent: the power to raise the gate to
|
212
|
+
annotated: ignored (used for consistency with other power methods)
|
213
|
+
|
214
|
+
Returns:
|
215
|
+
An operation implementing ``gate^exponent``
|
216
|
+
"""
|
217
|
+
# pylint: disable=unused-argument
|
218
|
+
extended_modifiers = self.modifiers.copy()
|
219
|
+
extended_modifiers.append(PowerModifier(exponent))
|
220
|
+
return AnnotatedOperation(self.base_op, extended_modifiers)
|
221
|
+
|
201
222
|
|
202
223
|
def _canonicalize_modifiers(modifiers):
|
203
224
|
"""
|
qiskit/circuit/barrier.py
CHANGED
@@ -16,29 +16,26 @@ Can be applied to a :class:`~qiskit.circuit.QuantumCircuit`
|
|
16
16
|
with the :meth:`~qiskit.circuit.QuantumCircuit.barrier` method.
|
17
17
|
"""
|
18
18
|
|
19
|
+
from __future__ import annotations
|
20
|
+
|
19
21
|
from qiskit.exceptions import QiskitError
|
20
22
|
from .instruction import Instruction
|
21
23
|
|
22
24
|
|
23
25
|
class Barrier(Instruction):
|
24
|
-
"""
|
26
|
+
"""A directive for circuit compilation to separate pieces of a circuit so that any optimizations
|
27
|
+
or re-writes are constrained to only act between barriers.
|
25
28
|
|
26
|
-
|
27
|
-
|
28
|
-
of a circuit so that any optimizations or re-writes are constrained
|
29
|
-
to only act between barriers."""
|
29
|
+
This will also appear in visualizations as a visual marker.
|
30
|
+
"""
|
30
31
|
|
31
32
|
_directive = True
|
32
33
|
|
33
|
-
def __init__(self, num_qubits, label=None):
|
34
|
-
"""
|
35
|
-
|
34
|
+
def __init__(self, num_qubits: int, label: str | None = None):
|
35
|
+
"""
|
36
36
|
Args:
|
37
|
-
num_qubits
|
38
|
-
label
|
39
|
-
|
40
|
-
Raises:
|
41
|
-
TypeError: if barrier label is invalid.
|
37
|
+
num_qubits: the number of qubits for the barrier.
|
38
|
+
label: the optional label of this barrier.
|
42
39
|
"""
|
43
40
|
self._label = label
|
44
41
|
super().__init__("barrier", num_qubits, 0, [], label=label)
|
qiskit/circuit/bit.py
CHANGED
@@ -15,8 +15,8 @@
|
|
15
15
|
Classical expressions (:mod:`qiskit.circuit.classical`)
|
16
16
|
=======================================================
|
17
17
|
|
18
|
-
This module contains an exploratory representation of
|
19
|
-
circuit execution.
|
18
|
+
This module contains an exploratory representation of real-time operations on classical values
|
19
|
+
during circuit execution.
|
20
20
|
|
21
21
|
Currently, only simple expressions on bits and registers that result in a Boolean value are
|
22
22
|
supported, and these are only valid for use in the conditions of :meth:`.QuantumCircuit.if_test`
|
@@ -39,12 +39,13 @@ The expression system is based on tree representation. All nodes in the tree ar
|
|
39
39
|
|
40
40
|
These objects are mutable and should not be reused in a different location without a copy.
|
41
41
|
|
42
|
-
The
|
43
|
-
|
42
|
+
The base for dynamic variables is the :class:`Var`, which can be either an arbitrarily typed
|
43
|
+
real-time variable, or a wrapper around a :class:`.Clbit` or :class:`.ClassicalRegister`.
|
44
44
|
|
45
45
|
.. autoclass:: Var
|
46
|
+
:members: var, name
|
46
47
|
|
47
|
-
Similarly, literals used in
|
48
|
+
Similarly, literals used in expressions (such as integers) should be lifted to :class:`Value` nodes
|
48
49
|
with associated types.
|
49
50
|
|
50
51
|
.. autoclass:: Value
|
@@ -61,6 +62,12 @@ and :class:`Binary.Op` respectively.
|
|
61
62
|
:members: Op
|
62
63
|
:member-order: bysource
|
63
64
|
|
65
|
+
Bit-like types (unsigned integers) can be indexed by integer types, represented by :class:`Index`.
|
66
|
+
The result is a single bit. The resulting expression has an associated memory location (and so can
|
67
|
+
be used as an lvalue for :class:`.Store`, etc) if the target is also an lvalue.
|
68
|
+
|
69
|
+
.. autoclass:: Index
|
70
|
+
|
64
71
|
When constructing expressions, one must ensure that the types are valid for the operation.
|
65
72
|
Attempts to construct expressions with invalid types will raise a regular Python ``TypeError``.
|
66
73
|
|
@@ -91,6 +98,13 @@ some scalar value as an :class:`Expr` node, you can manually :func:`lift` it you
|
|
91
98
|
|
92
99
|
.. autofunction:: lift
|
93
100
|
|
101
|
+
Typically you should create memory-owning :class:`Var` instances by using the
|
102
|
+
:meth:`.QuantumCircuit.add_var` method to declare them in some circuit context, since a
|
103
|
+
:class:`.QuantumCircuit` will not accept an :class:`Expr` that contains variables that are not
|
104
|
+
already declared in it, since it needs to know how to allocate the storage and how the variable will
|
105
|
+
be initialized. However, should you want to do this manually, you should use the low-level
|
106
|
+
:meth:`Var.new` call to safely generate a named variable for usage.
|
107
|
+
|
94
108
|
You can manually specify casts in cases where the cast is allowed in explicit form, but may be
|
95
109
|
lossy (such as the cast of a higher precision :class:`~.types.Uint` to a lower precision one).
|
96
110
|
|
@@ -114,6 +128,13 @@ Similarly, the binary operations and relations have helper functions defined.
|
|
114
128
|
.. autofunction:: less_equal
|
115
129
|
.. autofunction:: greater
|
116
130
|
.. autofunction:: greater_equal
|
131
|
+
.. autofunction:: shift_left
|
132
|
+
.. autofunction:: shift_right
|
133
|
+
|
134
|
+
You can index into unsigned integers and bit-likes using another unsigned integer of any width.
|
135
|
+
This includes in storing operations, if the target of the index is writeable.
|
136
|
+
|
137
|
+
.. autofunction:: index
|
117
138
|
|
118
139
|
Qiskit's legacy method for specifying equality conditions for use in conditionals is to use a
|
119
140
|
two-tuple of a :class:`.Clbit` or :class:`.ClassicalRegister` and an integer. This represents an
|
@@ -152,6 +173,11 @@ between two different circuits. In this case, one can use :func:`structurally_e
|
|
152
173
|
suitable "key" functions to do the comparison.
|
153
174
|
|
154
175
|
.. autofunction:: structurally_equivalent
|
176
|
+
|
177
|
+
Some expressions have associated memory locations, and others may be purely temporary.
|
178
|
+
You can use :func:`is_lvalue` to determine whether an expression has an associated memory location.
|
179
|
+
|
180
|
+
.. autofunction:: is_lvalue
|
155
181
|
"""
|
156
182
|
|
157
183
|
__all__ = [
|
@@ -161,9 +187,11 @@ __all__ = [
|
|
161
187
|
"Cast",
|
162
188
|
"Unary",
|
163
189
|
"Binary",
|
190
|
+
"Index",
|
164
191
|
"ExprVisitor",
|
165
192
|
"iter_vars",
|
166
193
|
"structurally_equivalent",
|
194
|
+
"is_lvalue",
|
167
195
|
"lift",
|
168
196
|
"cast",
|
169
197
|
"bit_not",
|
@@ -171,6 +199,8 @@ __all__ = [
|
|
171
199
|
"bit_and",
|
172
200
|
"bit_or",
|
173
201
|
"bit_xor",
|
202
|
+
"shift_left",
|
203
|
+
"shift_right",
|
174
204
|
"logic_and",
|
175
205
|
"logic_or",
|
176
206
|
"equal",
|
@@ -179,11 +209,12 @@ __all__ = [
|
|
179
209
|
"less_equal",
|
180
210
|
"greater",
|
181
211
|
"greater_equal",
|
212
|
+
"index",
|
182
213
|
"lift_legacy_condition",
|
183
214
|
]
|
184
215
|
|
185
|
-
from .expr import Expr, Var, Value, Cast, Unary, Binary
|
186
|
-
from .visitors import ExprVisitor, iter_vars, structurally_equivalent
|
216
|
+
from .expr import Expr, Var, Value, Cast, Unary, Binary, Index
|
217
|
+
from .visitors import ExprVisitor, iter_vars, structurally_equivalent, is_lvalue
|
187
218
|
from .constructors import (
|
188
219
|
lift,
|
189
220
|
cast,
|
@@ -200,5 +231,8 @@ from .constructors import (
|
|
200
231
|
less_equal,
|
201
232
|
greater,
|
202
233
|
greater_equal,
|
234
|
+
shift_left,
|
235
|
+
shift_right,
|
236
|
+
index,
|
203
237
|
lift_legacy_condition,
|
204
238
|
)
|
@@ -37,7 +37,7 @@ __all__ = [
|
|
37
37
|
|
38
38
|
import typing
|
39
39
|
|
40
|
-
from .expr import Expr, Var, Value, Unary, Binary, Cast
|
40
|
+
from .expr import Expr, Var, Value, Unary, Binary, Cast, Index
|
41
41
|
from ..types import CastKind, cast_kind
|
42
42
|
from .. import types
|
43
43
|
|
@@ -471,3 +471,86 @@ Var(ClassicalRegister(3, "b"), Uint(3)), \
|
|
471
471
|
Uint(3))
|
472
472
|
"""
|
473
473
|
return _binary_relation(Binary.Op.GREATER_EQUAL, left, right)
|
474
|
+
|
475
|
+
|
476
|
+
def _shift_like(
|
477
|
+
op: Binary.Op, left: typing.Any, right: typing.Any, type: types.Type | None
|
478
|
+
) -> Expr:
|
479
|
+
if type is not None and type.kind is not types.Uint:
|
480
|
+
raise TypeError(f"type '{type}' is not a valid bitshift operand type")
|
481
|
+
if isinstance(left, Expr):
|
482
|
+
left = _coerce_lossless(left, type) if type is not None else left
|
483
|
+
else:
|
484
|
+
left = lift(left, type)
|
485
|
+
right = lift(right)
|
486
|
+
if left.type.kind != types.Uint or right.type.kind != types.Uint:
|
487
|
+
raise TypeError(f"invalid types for '{op}': '{left.type}' and '{right.type}'")
|
488
|
+
return Binary(op, left, right, left.type)
|
489
|
+
|
490
|
+
|
491
|
+
def shift_left(left: typing.Any, right: typing.Any, /, type: types.Type | None = None) -> Expr:
|
492
|
+
"""Create a 'bitshift left' expression node from the given two values, resolving any implicit
|
493
|
+
casts and lifting the values into :class:`Value` nodes if required.
|
494
|
+
|
495
|
+
If ``type`` is given, the ``left`` operand will be coerced to it (if possible).
|
496
|
+
|
497
|
+
Examples:
|
498
|
+
Shift the value of a standalone variable left by some amount::
|
499
|
+
|
500
|
+
>>> from qiskit.circuit.classical import expr, types
|
501
|
+
>>> a = expr.Var.new("a", types.Uint(8))
|
502
|
+
>>> expr.shift_left(a, 4)
|
503
|
+
Binary(Binary.Op.SHIFT_LEFT, \
|
504
|
+
Var(<UUID>, Uint(8), name='a'), \
|
505
|
+
Value(4, Uint(3)), \
|
506
|
+
Uint(8))
|
507
|
+
|
508
|
+
Shift an integer literal by a variable amount, coercing the type of the literal::
|
509
|
+
|
510
|
+
>>> expr.shift_left(3, a, types.Uint(16))
|
511
|
+
Binary(Binary.Op.SHIFT_LEFT, \
|
512
|
+
Value(3, Uint(16)), \
|
513
|
+
Var(<UUID>, Uint(8), name='a'), \
|
514
|
+
Uint(16))
|
515
|
+
"""
|
516
|
+
return _shift_like(Binary.Op.SHIFT_LEFT, left, right, type)
|
517
|
+
|
518
|
+
|
519
|
+
def shift_right(left: typing.Any, right: typing.Any, /, type: types.Type | None = None) -> Expr:
|
520
|
+
"""Create a 'bitshift right' expression node from the given values, resolving any implicit casts
|
521
|
+
and lifting the values into :class:`Value` nodes if required.
|
522
|
+
|
523
|
+
If ``type`` is given, the ``left`` operand will be coerced to it (if possible).
|
524
|
+
|
525
|
+
Examples:
|
526
|
+
Shift the value of a classical register right by some amount::
|
527
|
+
|
528
|
+
>>> from qiskit.circuit import ClassicalRegister
|
529
|
+
>>> from qiskit.circuit.classical import expr
|
530
|
+
>>> expr.shift_right(ClassicalRegister(8, "a"), 4)
|
531
|
+
Binary(Binary.Op.SHIFT_RIGHT, \
|
532
|
+
Var(ClassicalRegister(8, "a"), Uint(8)), \
|
533
|
+
Value(4, Uint(3)), \
|
534
|
+
Uint(8))
|
535
|
+
"""
|
536
|
+
return _shift_like(Binary.Op.SHIFT_RIGHT, left, right, type)
|
537
|
+
|
538
|
+
|
539
|
+
def index(target: typing.Any, index: typing.Any, /) -> Expr:
|
540
|
+
"""Index into the ``target`` with the given integer ``index``, lifting the values into
|
541
|
+
:class:`Value` nodes if required.
|
542
|
+
|
543
|
+
This can be used as the target of a :class:`.Store`, if the ``target`` is itself an lvalue.
|
544
|
+
|
545
|
+
Examples:
|
546
|
+
Index into a classical register with a literal::
|
547
|
+
|
548
|
+
>>> from qiskit.circuit import ClassicalRegister
|
549
|
+
>>> from qiskit.circuit.classical import expr
|
550
|
+
>>> expr.index(ClassicalRegister(8, "a"), 3)
|
551
|
+
Index(Var(ClassicalRegister(8, "a"), Uint(8)), Value(3, Uint(2)), Bool())
|
552
|
+
"""
|
553
|
+
target, index = lift(target), lift(index)
|
554
|
+
if target.type.kind is not types.Uint or index.type.kind is not types.Uint:
|
555
|
+
raise TypeError(f"invalid types for indexing: '{target.type}' and '{index.type}'")
|
556
|
+
return Index(target, index, types.Bool())
|
@@ -31,6 +31,7 @@ __all__ = [
|
|
31
31
|
import abc
|
32
32
|
import enum
|
33
33
|
import typing
|
34
|
+
import uuid
|
34
35
|
|
35
36
|
from .. import types
|
36
37
|
|
@@ -110,29 +111,49 @@ class Cast(Expr):
|
|
110
111
|
class Var(Expr):
|
111
112
|
"""A classical variable.
|
112
113
|
|
114
|
+
These variables take two forms: a new-style variable that owns its storage location and has an
|
115
|
+
associated name; and an old-style variable that wraps a :class:`.Clbit` or
|
116
|
+
:class:`.ClassicalRegister` instance that is owned by some containing circuit. In general,
|
117
|
+
construction of variables for use in programs should use :meth:`Var.new` or
|
118
|
+
:meth:`.QuantumCircuit.add_var`.
|
119
|
+
|
113
120
|
Variables are immutable after construction, so they can be used as dictionary keys."""
|
114
121
|
|
115
|
-
__slots__ = ("var",)
|
122
|
+
__slots__ = ("var", "name")
|
116
123
|
|
117
|
-
var: qiskit.circuit.Clbit | qiskit.circuit.ClassicalRegister
|
124
|
+
var: qiskit.circuit.Clbit | qiskit.circuit.ClassicalRegister | uuid.UUID
|
118
125
|
"""A reference to the backing data storage of the :class:`Var` instance. When lifting
|
119
126
|
old-style :class:`.Clbit` or :class:`.ClassicalRegister` instances into a :class:`Var`,
|
120
|
-
this is exactly the :class:`.Clbit` or :class:`.ClassicalRegister`.
|
127
|
+
this is exactly the :class:`.Clbit` or :class:`.ClassicalRegister`. If the variable is a
|
128
|
+
new-style classical variable (one that owns its own storage separate to the old
|
129
|
+
:class:`.Clbit`/:class:`.ClassicalRegister` model), this field will be a :class:`~uuid.UUID`
|
130
|
+
to uniquely identify it."""
|
131
|
+
name: str | None
|
132
|
+
"""The name of the variable. This is required to exist if the backing :attr:`var` attribute
|
133
|
+
is a :class:`~uuid.UUID`, i.e. if it is a new-style variable, and must be ``None`` if it is
|
134
|
+
an old-style variable."""
|
121
135
|
|
122
136
|
def __init__(
|
123
137
|
self,
|
124
|
-
var: qiskit.circuit.Clbit | qiskit.circuit.ClassicalRegister,
|
138
|
+
var: qiskit.circuit.Clbit | qiskit.circuit.ClassicalRegister | uuid.UUID,
|
125
139
|
type: types.Type,
|
140
|
+
*,
|
141
|
+
name: str | None = None,
|
126
142
|
):
|
127
143
|
super().__setattr__("type", type)
|
128
144
|
super().__setattr__("var", var)
|
145
|
+
super().__setattr__("name", name)
|
146
|
+
|
147
|
+
@classmethod
|
148
|
+
def new(cls, name: str, type: types.Type) -> typing.Self:
|
149
|
+
"""Generate a new named variable that owns its own backing storage."""
|
150
|
+
return cls(uuid.uuid4(), type, name=name)
|
129
151
|
|
130
152
|
@property
|
131
153
|
def standalone(self) -> bool:
|
132
|
-
"""Whether this :class:`Var` is a standalone variable that owns its storage location.
|
133
|
-
|
134
|
-
|
135
|
-
return False
|
154
|
+
"""Whether this :class:`Var` is a standalone variable that owns its storage location. If
|
155
|
+
false, this is a wrapper :class:`Var` around a pre-existing circuit object."""
|
156
|
+
return isinstance(self.var, uuid.UUID)
|
136
157
|
|
137
158
|
def accept(self, visitor, /):
|
138
159
|
return visitor.visit_var(self)
|
@@ -143,21 +164,29 @@ class Var(Expr):
|
|
143
164
|
raise AttributeError(f"'Var' object has no attribute '{key}'")
|
144
165
|
|
145
166
|
def __hash__(self):
|
146
|
-
return hash((self.type, self.var))
|
167
|
+
return hash((self.type, self.var, self.name))
|
147
168
|
|
148
169
|
def __eq__(self, other):
|
149
|
-
return
|
170
|
+
return (
|
171
|
+
isinstance(other, Var)
|
172
|
+
and self.type == other.type
|
173
|
+
and self.var == other.var
|
174
|
+
and self.name == other.name
|
175
|
+
)
|
150
176
|
|
151
177
|
def __repr__(self):
|
152
|
-
|
178
|
+
if self.name is None:
|
179
|
+
return f"Var({self.var}, {self.type})"
|
180
|
+
return f"Var({self.var}, {self.type}, name='{self.name}')"
|
153
181
|
|
154
182
|
def __getstate__(self):
|
155
|
-
return (self.var, self.type)
|
183
|
+
return (self.var, self.type, self.name)
|
156
184
|
|
157
185
|
def __setstate__(self, state):
|
158
|
-
var, type = state
|
186
|
+
var, type, name = state
|
159
187
|
super().__setattr__("type", type)
|
160
188
|
super().__setattr__("var", var)
|
189
|
+
super().__setattr__("name", name)
|
161
190
|
|
162
191
|
def __copy__(self):
|
163
192
|
# I am immutable...
|
@@ -271,6 +300,11 @@ class Binary(Expr):
|
|
271
300
|
The binary mathematical relations :data:`EQUAL`, :data:`NOT_EQUAL`, :data:`LESS`,
|
272
301
|
:data:`LESS_EQUAL`, :data:`GREATER` and :data:`GREATER_EQUAL` take unsigned integers
|
273
302
|
(with an implicit cast to make them the same width), and return a Boolean.
|
303
|
+
|
304
|
+
The bitshift operations :data:`SHIFT_LEFT` and :data:`SHIFT_RIGHT` can take bit-like
|
305
|
+
container types (e.g. unsigned integers) as the left operand, and any integer type as the
|
306
|
+
right-hand operand. In all cases, the output bit width is the same as the input, and zeros
|
307
|
+
fill in the "exposed" spaces.
|
274
308
|
"""
|
275
309
|
|
276
310
|
# If adding opcodes, remember to add helper constructor functions in `constructors.py`
|
@@ -298,6 +332,10 @@ class Binary(Expr):
|
|
298
332
|
"""Numeric greater than. ``lhs > rhs``."""
|
299
333
|
GREATER_EQUAL = 11
|
300
334
|
"""Numeric greater than or equal to. ``lhs >= rhs``."""
|
335
|
+
SHIFT_LEFT = 12
|
336
|
+
"""Zero-padding bitshift to the left. ``lhs << rhs``."""
|
337
|
+
SHIFT_RIGHT = 13
|
338
|
+
"""Zero-padding bitshift to the right. ``lhs >> rhs``."""
|
301
339
|
|
302
340
|
def __str__(self):
|
303
341
|
return f"Binary.{super().__str__()}"
|
@@ -325,3 +363,35 @@ class Binary(Expr):
|
|
325
363
|
|
326
364
|
def __repr__(self):
|
327
365
|
return f"Binary({self.op}, {self.left}, {self.right}, {self.type})"
|
366
|
+
|
367
|
+
|
368
|
+
@typing.final
|
369
|
+
class Index(Expr):
|
370
|
+
"""An indexing expression.
|
371
|
+
|
372
|
+
Args:
|
373
|
+
target: The object being indexed.
|
374
|
+
index: The expression doing the indexing.
|
375
|
+
type: The resolved type of the result.
|
376
|
+
"""
|
377
|
+
|
378
|
+
__slots__ = ("target", "index")
|
379
|
+
|
380
|
+
def __init__(self, target: Expr, index: Expr, type: types.Type):
|
381
|
+
self.target = target
|
382
|
+
self.index = index
|
383
|
+
self.type = type
|
384
|
+
|
385
|
+
def accept(self, visitor, /):
|
386
|
+
return visitor.visit_index(self)
|
387
|
+
|
388
|
+
def __eq__(self, other):
|
389
|
+
return (
|
390
|
+
isinstance(other, Index)
|
391
|
+
and self.type == other.type
|
392
|
+
and self.target == other.target
|
393
|
+
and self.index == other.index
|
394
|
+
)
|
395
|
+
|
396
|
+
def __repr__(self):
|
397
|
+
return f"Index({self.target}, {self.index}, {self.type})"
|
@@ -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)
|