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
qiskit/dagcircuit/dagnode.py
CHANGED
@@ -12,10 +12,13 @@
|
|
12
12
|
|
13
13
|
|
14
14
|
"""Objects to represent the information at a node in the DAGCircuit."""
|
15
|
+
from __future__ import annotations
|
15
16
|
|
16
17
|
import itertools
|
18
|
+
import typing
|
17
19
|
import uuid
|
18
|
-
from
|
20
|
+
from collections.abc import Iterable
|
21
|
+
|
19
22
|
|
20
23
|
from qiskit.circuit import (
|
21
24
|
Qubit,
|
@@ -27,11 +30,16 @@ from qiskit.circuit import (
|
|
27
30
|
SwitchCaseOp,
|
28
31
|
ForLoopOp,
|
29
32
|
Parameter,
|
33
|
+
Operation,
|
34
|
+
QuantumCircuit,
|
30
35
|
)
|
31
36
|
from qiskit.circuit.classical import expr
|
32
37
|
|
38
|
+
if typing.TYPE_CHECKING:
|
39
|
+
from qiskit.dagcircuit import DAGCircuit
|
40
|
+
|
33
41
|
|
34
|
-
def _legacy_condition_eq(cond1, cond2, bit_indices1, bit_indices2):
|
42
|
+
def _legacy_condition_eq(cond1, cond2, bit_indices1, bit_indices2) -> bool:
|
35
43
|
if cond1 is cond2 is None:
|
36
44
|
return True
|
37
45
|
elif None in (cond1, cond2):
|
@@ -49,7 +57,7 @@ def _legacy_condition_eq(cond1, cond2, bit_indices1, bit_indices2):
|
|
49
57
|
return False
|
50
58
|
|
51
59
|
|
52
|
-
def _circuit_to_dag(circuit, node_qargs, node_cargs, bit_indices):
|
60
|
+
def _circuit_to_dag(circuit: QuantumCircuit, node_qargs, node_cargs, bit_indices) -> DAGCircuit:
|
53
61
|
"""Get a :class:`.DAGCircuit` of the given :class:`.QuantumCircuit`. The bits in the output
|
54
62
|
will be ordered in a canonical order based on their indices in the outer DAG, as defined by the
|
55
63
|
``bit_indices`` mapping and the ``node_{q,c}args`` arguments."""
|
@@ -252,7 +260,9 @@ class DAGOpNode(DAGNode):
|
|
252
260
|
|
253
261
|
__slots__ = ["op", "qargs", "cargs", "sort_key"]
|
254
262
|
|
255
|
-
def __init__(
|
263
|
+
def __init__(
|
264
|
+
self, op: Operation, qargs: Iterable[Qubit] = (), cargs: Iterable[Clbit] = (), dag=None
|
265
|
+
):
|
256
266
|
"""Create an Instruction node"""
|
257
267
|
super().__init__()
|
258
268
|
self.op = op
|
@@ -21,7 +21,7 @@ from typing import Any
|
|
21
21
|
|
22
22
|
import dill
|
23
23
|
|
24
|
-
from qiskit.utils.parallel import parallel_map
|
24
|
+
from qiskit.utils.parallel import parallel_map, should_run_in_parallel
|
25
25
|
from .base_tasks import Task, PassManagerIR
|
26
26
|
from .exceptions import PassManagerError
|
27
27
|
from .flow_controllers import FlowControllerLinear
|
@@ -225,16 +225,16 @@ class BasePassManager(ABC):
|
|
225
225
|
in_programs = [in_programs]
|
226
226
|
is_list = False
|
227
227
|
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
callback=callback,
|
233
|
-
|
234
|
-
|
235
|
-
if is_list:
|
236
|
-
return [
|
237
|
-
return
|
228
|
+
# If we're not going to run in parallel, we want to avoid spending time `dill` serialising
|
229
|
+
# ourselves, since that can be quite expensive.
|
230
|
+
if len(in_programs) == 1 or not should_run_in_parallel(num_processes):
|
231
|
+
out = [
|
232
|
+
_run_workflow(program=program, pass_manager=self, callback=callback, **kwargs)
|
233
|
+
for program in in_programs
|
234
|
+
]
|
235
|
+
if len(in_programs) == 1 and not is_list:
|
236
|
+
return out[0]
|
237
|
+
return out
|
238
238
|
|
239
239
|
del callback
|
240
240
|
del kwargs
|
qiskit/primitives/__init__.py
CHANGED
@@ -51,7 +51,7 @@ define a computation unit of work for the estimator to complete:
|
|
51
51
|
* a collection parameter value sets to bind the circuit against, :math:`\theta_k`.
|
52
52
|
|
53
53
|
Running an estimator returns a :class:`~qiskit.primitives.BasePrimitiveJob` object, where calling
|
54
|
-
the method :meth:`~qiskit.primitives.BasePrimitiveJob.result` results in expectation value estimates
|
54
|
+
the method :meth:`~qiskit.primitives.BasePrimitiveJob.result` results in expectation value estimates
|
55
55
|
and metadata for each pub:
|
56
56
|
|
57
57
|
.. math::
|
@@ -86,31 +86,31 @@ Here is an example of how an estimator is used.
|
|
86
86
|
estimator = Estimator()
|
87
87
|
|
88
88
|
# calculate [ <psi1(theta1)|H1|psi1(theta1)> ]
|
89
|
-
job = estimator.run([(psi1,
|
89
|
+
job = estimator.run([(psi1, H1, [theta1])])
|
90
90
|
job_result = job.result() # It will block until the job finishes.
|
91
|
-
print(f"The primitive-job finished with result {job_result}")
|
91
|
+
print(f"The primitive-job finished with result {job_result}")
|
92
92
|
|
93
93
|
# calculate [ [<psi1(theta1)|H1|psi1(theta1)>,
|
94
94
|
# <psi1(theta3)|H3|psi1(theta3)>],
|
95
95
|
# [<psi2(theta2)|H2|psi2(theta2)>] ]
|
96
96
|
job2 = estimator.run(
|
97
97
|
[
|
98
|
-
(psi1, [
|
99
|
-
(psi2,
|
98
|
+
(psi1, [H1, H3], [theta1, theta3]),
|
99
|
+
(psi2, H2, theta2)
|
100
100
|
],
|
101
101
|
precision=0.01
|
102
102
|
)
|
103
103
|
job_result = job2.result()
|
104
104
|
print(f"The primitive-job finished with result {job_result}")
|
105
105
|
|
106
|
-
|
106
|
+
|
107
107
|
Overview of SamplerV2
|
108
108
|
=====================
|
109
109
|
|
110
110
|
:class:`~BaseSamplerV2` is a primitive that samples outputs of quantum circuits.
|
111
111
|
|
112
112
|
Following construction, a sampler is used by calling its :meth:`~.BaseSamplerV2.run` method
|
113
|
-
with a list of pubs (Primitive Unified
|
113
|
+
with a list of pubs (Primitive Unified Blocs). Each pub contains values that, together,
|
114
114
|
define a computational unit of work for the sampler to complete:
|
115
115
|
|
116
116
|
* A single :class:`~qiskit.circuit.QuantumCircuit`, possibly parameterized.
|
@@ -128,7 +128,7 @@ Here is an example of how a sampler is used.
|
|
128
128
|
|
129
129
|
.. code-block:: python
|
130
130
|
|
131
|
-
from qiskit.primitives
|
131
|
+
from qiskit.primitives import StatevectorSampler as Sampler
|
132
132
|
from qiskit import QuantumCircuit
|
133
133
|
from qiskit.circuit.library import RealAmplitudes
|
134
134
|
|
@@ -220,8 +220,8 @@ Here is an example of how the estimator is used.
|
|
220
220
|
# <psi2(theta2)|H2|psi2(theta2)>,
|
221
221
|
# <psi1(theta3)|H3|psi1(theta3)> ]
|
222
222
|
job2 = estimator.run(
|
223
|
-
[psi1, psi2, psi1],
|
224
|
-
[H1, H2, H3],
|
223
|
+
[psi1, psi2, psi1],
|
224
|
+
[H1, H2, H3],
|
225
225
|
[theta1, theta2, theta3]
|
226
226
|
)
|
227
227
|
job_result = job2.result()
|
@@ -387,14 +387,15 @@ level, however, here are some notable differences keep in mind when migrating fr
|
|
387
387
|
Primitives API
|
388
388
|
==============
|
389
389
|
|
390
|
-
|
391
|
-
|
390
|
+
Estimator V2
|
391
|
+
------------
|
392
392
|
|
393
393
|
.. autosummary::
|
394
394
|
:toctree: ../stubs/
|
395
395
|
|
396
396
|
BaseEstimatorV2
|
397
397
|
StatevectorEstimator
|
398
|
+
BackendEstimatorV2
|
398
399
|
|
399
400
|
Sampler V2
|
400
401
|
----------
|
@@ -404,6 +405,7 @@ Sampler V2
|
|
404
405
|
|
405
406
|
BaseSamplerV2
|
406
407
|
StatevectorSampler
|
408
|
+
BackendSamplerV2
|
407
409
|
|
408
410
|
Results V2
|
409
411
|
----------
|
@@ -415,8 +417,10 @@ Results V2
|
|
415
417
|
DataBin
|
416
418
|
PrimitiveResult
|
417
419
|
PubResult
|
420
|
+
SamplerPubResult
|
418
421
|
BasePrimitiveJob
|
419
422
|
PrimitiveJob
|
423
|
+
Shaped
|
420
424
|
|
421
425
|
Estimator V1
|
422
426
|
------------
|
@@ -462,14 +466,20 @@ from .containers import (
|
|
462
466
|
DataBin,
|
463
467
|
PrimitiveResult,
|
464
468
|
PubResult,
|
469
|
+
SamplerPubResult,
|
465
470
|
EstimatorPubLike,
|
466
471
|
SamplerPubLike,
|
467
472
|
BindingsArrayLike,
|
468
473
|
ObservableLike,
|
469
474
|
ObservablesArrayLike,
|
475
|
+
Shaped,
|
470
476
|
)
|
477
|
+
|
478
|
+
|
471
479
|
from .estimator import Estimator
|
472
480
|
from .primitive_job import BasePrimitiveJob, PrimitiveJob
|
473
481
|
from .sampler import Sampler
|
474
482
|
from .statevector_estimator import StatevectorEstimator
|
475
483
|
from .statevector_sampler import StatevectorSampler
|
484
|
+
from .backend_estimator_v2 import BackendEstimatorV2
|
485
|
+
from .backend_sampler_v2 import BackendSamplerV2
|
@@ -413,14 +413,12 @@ def _paulis2inds(paulis: PauliList) -> list[int]:
|
|
413
413
|
# Treat Z, X, Y the same
|
414
414
|
nonid = paulis.z | paulis.x
|
415
415
|
|
416
|
-
inds = [0] * paulis.size
|
417
416
|
# bits are packed into uint8 in little endian
|
418
417
|
# e.g., i-th bit corresponds to coefficient 2^i
|
419
418
|
packed_vals = np.packbits(nonid, axis=1, bitorder="little")
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
return inds
|
419
|
+
power_uint8 = 1 << (8 * np.arange(packed_vals.shape[1], dtype=object))
|
420
|
+
inds = packed_vals @ power_uint8
|
421
|
+
return inds.tolist()
|
424
422
|
|
425
423
|
|
426
424
|
def _parity(integer: int) -> int:
|
@@ -0,0 +1,410 @@
|
|
1
|
+
# This code is part of Qiskit.
|
2
|
+
#
|
3
|
+
# (C) Copyright IBM 2024.
|
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
|
+
"""Estimator V2 implementation for an arbitrary Backend object."""
|
14
|
+
|
15
|
+
from __future__ import annotations
|
16
|
+
|
17
|
+
import math
|
18
|
+
from collections import defaultdict
|
19
|
+
from collections.abc import Iterable
|
20
|
+
from dataclasses import dataclass
|
21
|
+
|
22
|
+
import numpy as np
|
23
|
+
|
24
|
+
from qiskit.circuit import ClassicalRegister, QuantumCircuit, QuantumRegister
|
25
|
+
from qiskit.exceptions import QiskitError
|
26
|
+
from qiskit.providers import BackendV1, BackendV2
|
27
|
+
from qiskit.quantum_info import Pauli, PauliList
|
28
|
+
from qiskit.result import Counts
|
29
|
+
from qiskit.transpiler import PassManager, PassManagerConfig
|
30
|
+
from qiskit.transpiler.passes import Optimize1qGatesDecomposition
|
31
|
+
|
32
|
+
from .backend_estimator import _pauli_expval_with_variance, _prepare_counts, _run_circuits
|
33
|
+
from .base import BaseEstimatorV2
|
34
|
+
from .containers import DataBin, EstimatorPubLike, PrimitiveResult, PubResult
|
35
|
+
from .containers.bindings_array import BindingsArray
|
36
|
+
from .containers.estimator_pub import EstimatorPub
|
37
|
+
from .primitive_job import PrimitiveJob
|
38
|
+
|
39
|
+
|
40
|
+
@dataclass
|
41
|
+
class Options:
|
42
|
+
"""Options for :class:`~.BackendEstimatorV2`."""
|
43
|
+
|
44
|
+
default_precision: float = 0.015625
|
45
|
+
"""The default precision to use if none are specified in :meth:`~run`.
|
46
|
+
Default: 0.015625 (1 / sqrt(4096)).
|
47
|
+
"""
|
48
|
+
|
49
|
+
abelian_grouping: bool = True
|
50
|
+
"""Whether the observables should be grouped into sets of qubit-wise commuting observables.
|
51
|
+
Default: True.
|
52
|
+
"""
|
53
|
+
|
54
|
+
seed_simulator: int | None = None
|
55
|
+
"""The seed to use in the simulator. If None, a random seed will be used.
|
56
|
+
Default: None.
|
57
|
+
"""
|
58
|
+
|
59
|
+
|
60
|
+
@dataclass
|
61
|
+
class _PreprocessedData:
|
62
|
+
"""Internal data structure to store the results of the preprocessing of a pub."""
|
63
|
+
|
64
|
+
circuits: list[QuantumCircuit]
|
65
|
+
"""The quantum circuits generated by binding parameters of the pub's circuit."""
|
66
|
+
|
67
|
+
parameter_indices: np.ndarray
|
68
|
+
"""The indices of the pub's bindings array broadcast to the shape of the pub."""
|
69
|
+
|
70
|
+
observables: np.ndarray
|
71
|
+
"""The pub's observable array broadcast to the shape of the pub."""
|
72
|
+
|
73
|
+
|
74
|
+
class BackendEstimatorV2(BaseEstimatorV2):
|
75
|
+
"""Evaluates expectation values for provided quantum circuit and observable combinations
|
76
|
+
|
77
|
+
The :class:`~.BackendEstimatorV2` class is a generic implementation of the
|
78
|
+
:class:`~.BaseEstimatorV2` interface that is used to wrap a :class:`~.BackendV2`
|
79
|
+
(or :class:`~.BackendV1`) object in the :class:`~.BaseEstimatorV2` API. It
|
80
|
+
facilitates using backends that do not provide a native
|
81
|
+
:class:`~.BaseEstimatorV2` implementation in places that work with
|
82
|
+
:class:`~.BaseEstimatorV2`. However,
|
83
|
+
if you're using a provider that has a native implementation of
|
84
|
+
:class:`~.BaseEstimatorV2`, it is a better choice to leverage that native
|
85
|
+
implementation as it will likely include additional optimizations and be
|
86
|
+
a more efficient implementation. The generic nature of this class
|
87
|
+
precludes doing any provider- or backend-specific optimizations.
|
88
|
+
|
89
|
+
This class does not perform any measurement or gate mitigation, and, presently, is only
|
90
|
+
compatible with Pauli-based observables.
|
91
|
+
|
92
|
+
Each tuple of ``(circuit, observables, <optional> parameter values, <optional> precision)``,
|
93
|
+
called an estimator primitive unified bloc (PUB), produces its own array-based result. The
|
94
|
+
:meth:`~.BackendEstimatorV2.run` method can be given a sequence of pubs to run in one call.
|
95
|
+
|
96
|
+
The options for :class:`~.BackendEstimatorV2` consist of the following items.
|
97
|
+
|
98
|
+
* ``default_precision``: The default precision to use if none are specified in :meth:`~run`.
|
99
|
+
Default: 0.015625 (1 / sqrt(4096)).
|
100
|
+
|
101
|
+
* ``abelian_grouping``: Whether the observables should be grouped into sets of qubit-wise
|
102
|
+
commuting observables.
|
103
|
+
Default: True.
|
104
|
+
|
105
|
+
* ``seed_simulator``: The seed to use in the simulator. If None, a random seed will be used.
|
106
|
+
Default: None.
|
107
|
+
"""
|
108
|
+
|
109
|
+
def __init__(
|
110
|
+
self,
|
111
|
+
*,
|
112
|
+
backend: BackendV1 | BackendV2,
|
113
|
+
options: dict | None = None,
|
114
|
+
):
|
115
|
+
"""
|
116
|
+
Args:
|
117
|
+
backend: The backend to run the primitive on.
|
118
|
+
options: The options to control the default precision (``default_precision``),
|
119
|
+
the operator grouping (``abelian_grouping``), and
|
120
|
+
the random seed for the simulator (``seed_simulator``).
|
121
|
+
"""
|
122
|
+
self._backend = backend
|
123
|
+
self._options = Options(**options) if options else Options()
|
124
|
+
|
125
|
+
basis = PassManagerConfig.from_backend(backend).basis_gates
|
126
|
+
if isinstance(backend, BackendV2):
|
127
|
+
opt1q = Optimize1qGatesDecomposition(basis=basis, target=backend.target)
|
128
|
+
else:
|
129
|
+
opt1q = Optimize1qGatesDecomposition(basis=basis)
|
130
|
+
self._passmanager = PassManager([opt1q])
|
131
|
+
|
132
|
+
@property
|
133
|
+
def options(self) -> Options:
|
134
|
+
"""Return the options"""
|
135
|
+
return self._options
|
136
|
+
|
137
|
+
@property
|
138
|
+
def backend(self) -> BackendV1 | BackendV2:
|
139
|
+
"""Returns the backend which this sampler object based on."""
|
140
|
+
return self._backend
|
141
|
+
|
142
|
+
def run(
|
143
|
+
self, pubs: Iterable[EstimatorPubLike], *, precision: float | None = None
|
144
|
+
) -> PrimitiveJob[PrimitiveResult[PubResult]]:
|
145
|
+
if precision is None:
|
146
|
+
precision = self._options.default_precision
|
147
|
+
coerced_pubs = [EstimatorPub.coerce(pub, precision) for pub in pubs]
|
148
|
+
self._validate_pubs(coerced_pubs)
|
149
|
+
job = PrimitiveJob(self._run, coerced_pubs)
|
150
|
+
job._submit()
|
151
|
+
return job
|
152
|
+
|
153
|
+
def _validate_pubs(self, pubs: list[EstimatorPub]):
|
154
|
+
for i, pub in enumerate(pubs):
|
155
|
+
if pub.precision <= 0.0:
|
156
|
+
raise ValueError(
|
157
|
+
f"The {i}-th pub has precision less than or equal to 0 ({pub.precision}). ",
|
158
|
+
"But precision should be larger than 0.",
|
159
|
+
)
|
160
|
+
|
161
|
+
def _run(self, pubs: list[EstimatorPub]) -> PrimitiveResult[PubResult]:
|
162
|
+
pub_dict = defaultdict(list)
|
163
|
+
# consolidate pubs with the same number of shots
|
164
|
+
for i, pub in enumerate(pubs):
|
165
|
+
shots = int(math.ceil(1.0 / pub.precision**2))
|
166
|
+
pub_dict[shots].append(i)
|
167
|
+
|
168
|
+
results = [None] * len(pubs)
|
169
|
+
for shots, lst in pub_dict.items():
|
170
|
+
# run pubs with the same number of shots at once
|
171
|
+
pub_results = self._run_pubs([pubs[i] for i in lst], shots)
|
172
|
+
# reconstruct the result of pubs
|
173
|
+
for i, pub_result in zip(lst, pub_results):
|
174
|
+
results[i] = pub_result
|
175
|
+
return PrimitiveResult(results)
|
176
|
+
|
177
|
+
def _run_pubs(self, pubs: list[EstimatorPub], shots: int) -> list[PubResult]:
|
178
|
+
"""Compute results for pubs that all require the same value of ``shots``."""
|
179
|
+
preprocessed_data = []
|
180
|
+
flat_circuits = []
|
181
|
+
for pub in pubs:
|
182
|
+
data = self._preprocess_pub(pub)
|
183
|
+
preprocessed_data.append(data)
|
184
|
+
flat_circuits.extend(data.circuits)
|
185
|
+
|
186
|
+
run_result, metadata = _run_circuits(
|
187
|
+
flat_circuits, self._backend, shots=shots, seed_simulator=self._options.seed_simulator
|
188
|
+
)
|
189
|
+
counts = _prepare_counts(run_result)
|
190
|
+
|
191
|
+
results = []
|
192
|
+
start = 0
|
193
|
+
for pub, data in zip(pubs, preprocessed_data):
|
194
|
+
end = start + len(data.circuits)
|
195
|
+
expval_map = self._calc_expval_map(counts[start:end], metadata[start:end])
|
196
|
+
start = end
|
197
|
+
results.append(self._postprocess_pub(pub, expval_map, data, shots))
|
198
|
+
return results
|
199
|
+
|
200
|
+
def _preprocess_pub(self, pub: EstimatorPub) -> _PreprocessedData:
|
201
|
+
"""Converts a pub into a list of bound circuits necessary to estimate all its observables.
|
202
|
+
|
203
|
+
The circuits contain metadata explaining which bindings array index they are with respect to,
|
204
|
+
and which measurement basis they are measuring.
|
205
|
+
|
206
|
+
Args:
|
207
|
+
pub: The pub to preprocess.
|
208
|
+
|
209
|
+
Returns:
|
210
|
+
The values ``(circuits, bc_param_ind, bc_obs)`` where ``circuits`` are the circuits to
|
211
|
+
execute on the backend, ``bc_param_ind`` are indices of the pub's bindings array and
|
212
|
+
``bc_obs`` is the observables array, both broadcast to the shape of the pub.
|
213
|
+
"""
|
214
|
+
circuit = pub.circuit
|
215
|
+
observables = pub.observables
|
216
|
+
parameter_values = pub.parameter_values
|
217
|
+
|
218
|
+
# calculate broadcasting of parameters and observables
|
219
|
+
param_shape = parameter_values.shape
|
220
|
+
param_indices = np.fromiter(np.ndindex(param_shape), dtype=object).reshape(param_shape)
|
221
|
+
bc_param_ind, bc_obs = np.broadcast_arrays(param_indices, observables)
|
222
|
+
|
223
|
+
# calculate expectation values for each pair of parameter value set and pauli
|
224
|
+
param_obs_map = defaultdict(set)
|
225
|
+
for index in np.ndindex(*bc_param_ind.shape):
|
226
|
+
param_index = bc_param_ind[index]
|
227
|
+
param_obs_map[param_index].update(bc_obs[index])
|
228
|
+
|
229
|
+
bound_circuits = self._bind_and_add_measurements(circuit, parameter_values, param_obs_map)
|
230
|
+
return _PreprocessedData(bound_circuits, bc_param_ind, bc_obs)
|
231
|
+
|
232
|
+
def _postprocess_pub(
|
233
|
+
self, pub: EstimatorPub, expval_map: dict, data: _PreprocessedData, shots: int
|
234
|
+
) -> PubResult:
|
235
|
+
"""Computes expectation values (evs) and standard errors (stds).
|
236
|
+
|
237
|
+
The values are stored in arrays broadcast to the shape of the pub.
|
238
|
+
|
239
|
+
Args:
|
240
|
+
pub: The pub to postprocess.
|
241
|
+
expval_map: The map
|
242
|
+
data: The result data of the preprocessing.
|
243
|
+
shots: The number of shots.
|
244
|
+
|
245
|
+
Returns:
|
246
|
+
The pub result.
|
247
|
+
"""
|
248
|
+
bc_param_ind = data.parameter_indices
|
249
|
+
bc_obs = data.observables
|
250
|
+
evs = np.zeros_like(bc_param_ind, dtype=float)
|
251
|
+
variances = np.zeros_like(bc_param_ind, dtype=float)
|
252
|
+
for index in np.ndindex(*bc_param_ind.shape):
|
253
|
+
param_index = bc_param_ind[index]
|
254
|
+
for pauli, coeff in bc_obs[index].items():
|
255
|
+
expval, variance = expval_map[param_index, pauli]
|
256
|
+
evs[index] += expval * coeff
|
257
|
+
variances[index] += variance * coeff**2
|
258
|
+
stds = np.sqrt(variances / shots)
|
259
|
+
data_bin = DataBin(evs=evs, stds=stds, shape=evs.shape)
|
260
|
+
return PubResult(data_bin, metadata={"target_precision": pub.precision})
|
261
|
+
|
262
|
+
def _bind_and_add_measurements(
|
263
|
+
self,
|
264
|
+
circuit: QuantumCircuit,
|
265
|
+
parameter_values: BindingsArray,
|
266
|
+
param_obs_map: dict[tuple[int, ...], set[str]],
|
267
|
+
) -> list[QuantumCircuit]:
|
268
|
+
"""Bind the given circuit against each parameter value set, and add necessary measurements
|
269
|
+
to each.
|
270
|
+
|
271
|
+
Args:
|
272
|
+
circuit: The (possibly parametric) circuit of interest.
|
273
|
+
parameter_values: An array of parameter value sets that can be applied to the circuit.
|
274
|
+
param_obs_map: A mapping from locations in ``parameter_values`` to a sets of
|
275
|
+
Pauli terms whose expectation values are required in those locations.
|
276
|
+
|
277
|
+
Returns:
|
278
|
+
A flat list of circuits sufficient to measure all Pauli terms in the ``param_obs_map``
|
279
|
+
values at the corresponding ``parameter_values`` location, where requisite
|
280
|
+
book-keeping is stored as circuit metadata.
|
281
|
+
"""
|
282
|
+
circuits = []
|
283
|
+
for param_index, pauli_strings in param_obs_map.items():
|
284
|
+
bound_circuit = parameter_values.bind(circuit, param_index)
|
285
|
+
# sort pauli_strings so that the order is deterministic
|
286
|
+
meas_paulis = PauliList(sorted(pauli_strings))
|
287
|
+
new_circuits = self._create_measurement_circuits(
|
288
|
+
bound_circuit, meas_paulis, param_index
|
289
|
+
)
|
290
|
+
circuits.extend(new_circuits)
|
291
|
+
return circuits
|
292
|
+
|
293
|
+
def _calc_expval_map(
|
294
|
+
self,
|
295
|
+
counts: list[Counts],
|
296
|
+
metadata: dict,
|
297
|
+
) -> dict[tuple[tuple[int, ...], str], tuple[float, float]]:
|
298
|
+
"""Computes the map of expectation values.
|
299
|
+
|
300
|
+
Args:
|
301
|
+
counts: The counts data.
|
302
|
+
metadata: The metadata.
|
303
|
+
|
304
|
+
Returns:
|
305
|
+
The map of expectation values takes a pair of an index of the bindings array and
|
306
|
+
a pauli string as a key and returns the expectation value of the pauli string
|
307
|
+
with the the pub's circuit bound against the parameter value set in the index of
|
308
|
+
the bindings array.
|
309
|
+
"""
|
310
|
+
expval_map: dict[tuple[tuple[int, ...], str], tuple[float, float]] = {}
|
311
|
+
for count, meta in zip(counts, metadata):
|
312
|
+
orig_paulis = meta["orig_paulis"]
|
313
|
+
meas_paulis = meta["meas_paulis"]
|
314
|
+
param_index = meta["param_index"]
|
315
|
+
expvals, variances = _pauli_expval_with_variance(count, meas_paulis)
|
316
|
+
for pauli, expval, variance in zip(orig_paulis, expvals, variances):
|
317
|
+
expval_map[param_index, pauli.to_label()] = (expval, variance)
|
318
|
+
return expval_map
|
319
|
+
|
320
|
+
def _create_measurement_circuits(
|
321
|
+
self, circuit: QuantumCircuit, observable: PauliList, param_index: tuple[int, ...]
|
322
|
+
) -> list[QuantumCircuit]:
|
323
|
+
"""Generate a list of circuits sufficient to estimate each of the given Paulis.
|
324
|
+
|
325
|
+
Paulis are divided into qubitwise-commuting subsets to reduce the total circuit count.
|
326
|
+
Metadata is attached to circuits in order to remember what each one measures, and
|
327
|
+
where it belongs in the output.
|
328
|
+
|
329
|
+
Args:
|
330
|
+
circuit: The circuit of interest.
|
331
|
+
observable: Which Pauli terms we would like to observe.
|
332
|
+
param_index: Where to put the data we estimate (only passed to metadata).
|
333
|
+
|
334
|
+
Returns:
|
335
|
+
A list of circuits sufficient to estimate each of the given Paulis.
|
336
|
+
"""
|
337
|
+
meas_circuits: list[QuantumCircuit] = []
|
338
|
+
if self._options.abelian_grouping:
|
339
|
+
for obs in observable.group_commuting(qubit_wise=True):
|
340
|
+
basis = Pauli((np.logical_or.reduce(obs.z), np.logical_or.reduce(obs.x)))
|
341
|
+
meas_circuit, indices = _measurement_circuit(circuit.num_qubits, basis)
|
342
|
+
paulis = PauliList.from_symplectic(
|
343
|
+
obs.z[:, indices],
|
344
|
+
obs.x[:, indices],
|
345
|
+
obs.phase,
|
346
|
+
)
|
347
|
+
meas_circuit.metadata = {
|
348
|
+
"orig_paulis": obs,
|
349
|
+
"meas_paulis": paulis,
|
350
|
+
"param_index": param_index,
|
351
|
+
}
|
352
|
+
meas_circuits.append(meas_circuit)
|
353
|
+
else:
|
354
|
+
for basis in observable:
|
355
|
+
meas_circuit, indices = _measurement_circuit(circuit.num_qubits, basis)
|
356
|
+
obs = PauliList(basis)
|
357
|
+
paulis = PauliList.from_symplectic(
|
358
|
+
obs.z[:, indices],
|
359
|
+
obs.x[:, indices],
|
360
|
+
obs.phase,
|
361
|
+
)
|
362
|
+
meas_circuit.metadata = {
|
363
|
+
"orig_paulis": obs,
|
364
|
+
"meas_paulis": paulis,
|
365
|
+
"param_index": param_index,
|
366
|
+
}
|
367
|
+
meas_circuits.append(meas_circuit)
|
368
|
+
|
369
|
+
# unroll basis gates
|
370
|
+
meas_circuits = self._passmanager.run(meas_circuits)
|
371
|
+
|
372
|
+
# combine measurement circuits
|
373
|
+
preprocessed_circuits = []
|
374
|
+
for meas_circuit in meas_circuits:
|
375
|
+
circuit_copy = circuit.copy()
|
376
|
+
# meas_circuit is supposed to have a classical register whose name is different from
|
377
|
+
# those of the transpiled_circuit
|
378
|
+
clbits = meas_circuit.cregs[0]
|
379
|
+
for creg in circuit_copy.cregs:
|
380
|
+
if clbits.name == creg.name:
|
381
|
+
raise QiskitError(
|
382
|
+
"Classical register for measurements conflict with those of the input "
|
383
|
+
f"circuit: {clbits}. "
|
384
|
+
"Recommended to avoid register names starting with '__'."
|
385
|
+
)
|
386
|
+
circuit_copy.add_register(clbits)
|
387
|
+
circuit_copy.compose(meas_circuit, clbits=clbits, inplace=True)
|
388
|
+
circuit_copy.metadata = meas_circuit.metadata
|
389
|
+
preprocessed_circuits.append(circuit_copy)
|
390
|
+
return preprocessed_circuits
|
391
|
+
|
392
|
+
|
393
|
+
def _measurement_circuit(num_qubits: int, pauli: Pauli):
|
394
|
+
# Note: if pauli is I for all qubits, this function generates a circuit to measure only
|
395
|
+
# the first qubit.
|
396
|
+
# Although such an operator can be optimized out by interpreting it as a constant (1),
|
397
|
+
# this optimization requires changes in various methods. So it is left as future work.
|
398
|
+
qubit_indices = np.arange(pauli.num_qubits)[pauli.z | pauli.x]
|
399
|
+
if not np.any(qubit_indices):
|
400
|
+
qubit_indices = [0]
|
401
|
+
meas_circuit = QuantumCircuit(
|
402
|
+
QuantumRegister(num_qubits, "q"), ClassicalRegister(len(qubit_indices), f"__c_{pauli}")
|
403
|
+
)
|
404
|
+
for clbit, i in enumerate(qubit_indices):
|
405
|
+
if pauli.x[i]:
|
406
|
+
if pauli.z[i]:
|
407
|
+
meas_circuit.sdg(i)
|
408
|
+
meas_circuit.h(i)
|
409
|
+
meas_circuit.measure(i, clbit)
|
410
|
+
return meas_circuit, qubit_indices
|