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.
Files changed (263) hide show
  1. qiskit/VERSION.txt +1 -1
  2. qiskit/__init__.py +27 -16
  3. qiskit/_accelerate.pyd +0 -0
  4. qiskit/_numpy_compat.py +73 -0
  5. qiskit/assembler/__init__.py +5 -10
  6. qiskit/assembler/disassemble.py +5 -6
  7. qiskit/circuit/__init__.py +1061 -232
  8. qiskit/circuit/_classical_resource_map.py +10 -6
  9. qiskit/circuit/_utils.py +18 -8
  10. qiskit/circuit/annotated_operation.py +21 -0
  11. qiskit/circuit/barrier.py +10 -13
  12. qiskit/circuit/bit.py +0 -1
  13. qiskit/circuit/classical/__init__.py +2 -2
  14. qiskit/circuit/classical/expr/__init__.py +39 -5
  15. qiskit/circuit/classical/expr/constructors.py +84 -1
  16. qiskit/circuit/classical/expr/expr.py +83 -13
  17. qiskit/circuit/classical/expr/visitors.py +83 -0
  18. qiskit/circuit/classical/types/__init__.py +5 -4
  19. qiskit/circuit/classicalfunction/__init__.py +1 -0
  20. qiskit/circuit/commutation_checker.py +86 -51
  21. qiskit/circuit/controlflow/_builder_utils.py +9 -1
  22. qiskit/circuit/controlflow/break_loop.py +8 -22
  23. qiskit/circuit/controlflow/builder.py +116 -1
  24. qiskit/circuit/controlflow/continue_loop.py +8 -22
  25. qiskit/circuit/controlflow/control_flow.py +47 -8
  26. qiskit/circuit/controlflow/for_loop.py +8 -23
  27. qiskit/circuit/controlflow/if_else.py +13 -27
  28. qiskit/circuit/controlflow/switch_case.py +14 -21
  29. qiskit/circuit/controlflow/while_loop.py +9 -23
  30. qiskit/circuit/controlledgate.py +2 -2
  31. qiskit/circuit/delay.py +7 -5
  32. qiskit/circuit/gate.py +20 -7
  33. qiskit/circuit/instruction.py +31 -30
  34. qiskit/circuit/instructionset.py +9 -22
  35. qiskit/circuit/library/__init__.py +3 -13
  36. qiskit/circuit/library/arithmetic/integer_comparator.py +2 -2
  37. qiskit/circuit/library/arithmetic/quadratic_form.py +3 -2
  38. qiskit/circuit/library/blueprintcircuit.py +29 -7
  39. qiskit/circuit/library/data_preparation/state_preparation.py +6 -5
  40. qiskit/circuit/library/generalized_gates/diagonal.py +5 -4
  41. qiskit/circuit/library/generalized_gates/isometry.py +51 -254
  42. qiskit/circuit/library/generalized_gates/pauli.py +2 -2
  43. qiskit/circuit/library/generalized_gates/permutation.py +4 -1
  44. qiskit/circuit/library/generalized_gates/rv.py +15 -11
  45. qiskit/circuit/library/generalized_gates/uc.py +2 -98
  46. qiskit/circuit/library/generalized_gates/unitary.py +9 -4
  47. qiskit/circuit/library/hamiltonian_gate.py +11 -5
  48. qiskit/circuit/library/n_local/efficient_su2.py +5 -5
  49. qiskit/circuit/library/n_local/n_local.py +100 -49
  50. qiskit/circuit/library/n_local/two_local.py +3 -59
  51. qiskit/circuit/library/overlap.py +3 -3
  52. qiskit/circuit/library/phase_oracle.py +1 -1
  53. qiskit/circuit/library/quantum_volume.py +39 -38
  54. qiskit/circuit/library/standard_gates/equivalence_library.py +50 -0
  55. qiskit/circuit/library/standard_gates/global_phase.py +4 -2
  56. qiskit/circuit/library/standard_gates/i.py +1 -2
  57. qiskit/circuit/library/standard_gates/iswap.py +1 -2
  58. qiskit/circuit/library/standard_gates/multi_control_rotation_gates.py +11 -5
  59. qiskit/circuit/library/standard_gates/p.py +31 -15
  60. qiskit/circuit/library/standard_gates/r.py +4 -3
  61. qiskit/circuit/library/standard_gates/rx.py +7 -4
  62. qiskit/circuit/library/standard_gates/rxx.py +4 -3
  63. qiskit/circuit/library/standard_gates/ry.py +7 -4
  64. qiskit/circuit/library/standard_gates/ryy.py +4 -3
  65. qiskit/circuit/library/standard_gates/rz.py +7 -4
  66. qiskit/circuit/library/standard_gates/rzx.py +4 -3
  67. qiskit/circuit/library/standard_gates/rzz.py +4 -3
  68. qiskit/circuit/library/standard_gates/s.py +4 -8
  69. qiskit/circuit/library/standard_gates/t.py +2 -4
  70. qiskit/circuit/library/standard_gates/u.py +16 -11
  71. qiskit/circuit/library/standard_gates/u1.py +6 -2
  72. qiskit/circuit/library/standard_gates/u2.py +4 -2
  73. qiskit/circuit/library/standard_gates/u3.py +9 -5
  74. qiskit/circuit/library/standard_gates/x.py +22 -11
  75. qiskit/circuit/library/standard_gates/xx_minus_yy.py +4 -3
  76. qiskit/circuit/library/standard_gates/xx_plus_yy.py +7 -5
  77. qiskit/circuit/library/standard_gates/z.py +1 -2
  78. qiskit/circuit/measure.py +4 -1
  79. qiskit/circuit/operation.py +13 -8
  80. qiskit/circuit/parameter.py +11 -6
  81. qiskit/circuit/quantumcircuit.py +1910 -260
  82. qiskit/circuit/quantumcircuitdata.py +2 -2
  83. qiskit/circuit/reset.py +5 -2
  84. qiskit/circuit/store.py +95 -0
  85. qiskit/compiler/assembler.py +22 -22
  86. qiskit/compiler/transpiler.py +63 -112
  87. qiskit/converters/__init__.py +17 -2
  88. qiskit/converters/circuit_to_dag.py +7 -0
  89. qiskit/converters/circuit_to_dagdependency_v2.py +47 -0
  90. qiskit/converters/circuit_to_gate.py +2 -0
  91. qiskit/converters/circuit_to_instruction.py +22 -0
  92. qiskit/converters/dag_to_circuit.py +4 -0
  93. qiskit/converters/dag_to_dagdependency_v2.py +44 -0
  94. qiskit/dagcircuit/collect_blocks.py +15 -10
  95. qiskit/dagcircuit/dagcircuit.py +434 -124
  96. qiskit/dagcircuit/dagdependency.py +19 -12
  97. qiskit/dagcircuit/dagdependency_v2.py +641 -0
  98. qiskit/dagcircuit/dagdepnode.py +19 -16
  99. qiskit/dagcircuit/dagnode.py +14 -4
  100. qiskit/passmanager/passmanager.py +11 -11
  101. qiskit/primitives/__init__.py +22 -12
  102. qiskit/primitives/backend_estimator.py +3 -5
  103. qiskit/primitives/backend_estimator_v2.py +410 -0
  104. qiskit/primitives/backend_sampler_v2.py +287 -0
  105. qiskit/primitives/base/base_estimator.py +4 -9
  106. qiskit/primitives/base/base_sampler.py +2 -2
  107. qiskit/primitives/containers/__init__.py +6 -4
  108. qiskit/primitives/containers/bit_array.py +293 -2
  109. qiskit/primitives/containers/data_bin.py +123 -50
  110. qiskit/primitives/containers/estimator_pub.py +10 -3
  111. qiskit/primitives/containers/observables_array.py +2 -2
  112. qiskit/primitives/containers/pub_result.py +1 -1
  113. qiskit/primitives/containers/sampler_pub.py +19 -3
  114. qiskit/primitives/containers/sampler_pub_result.py +74 -0
  115. qiskit/primitives/containers/shape.py +4 -4
  116. qiskit/primitives/statevector_estimator.py +4 -4
  117. qiskit/primitives/statevector_sampler.py +7 -12
  118. qiskit/providers/__init__.py +65 -34
  119. qiskit/providers/backend.py +2 -2
  120. qiskit/providers/backend_compat.py +8 -10
  121. qiskit/providers/basic_provider/__init__.py +2 -23
  122. qiskit/providers/basic_provider/basic_provider_tools.py +67 -31
  123. qiskit/providers/basic_provider/basic_simulator.py +81 -21
  124. qiskit/providers/fake_provider/__init__.py +1 -1
  125. qiskit/providers/fake_provider/fake_1q.py +1 -1
  126. qiskit/providers/fake_provider/fake_backend.py +3 -408
  127. qiskit/providers/fake_provider/generic_backend_v2.py +26 -14
  128. qiskit/providers/models/__init__.py +2 -2
  129. qiskit/providers/provider.py +16 -0
  130. qiskit/pulse/builder.py +4 -1
  131. qiskit/pulse/parameter_manager.py +60 -4
  132. qiskit/pulse/schedule.py +29 -13
  133. qiskit/pulse/utils.py +61 -20
  134. qiskit/qasm2/__init__.py +1 -5
  135. qiskit/qasm2/parse.py +1 -4
  136. qiskit/qasm3/__init__.py +42 -5
  137. qiskit/qasm3/ast.py +19 -0
  138. qiskit/qasm3/exporter.py +178 -106
  139. qiskit/qasm3/printer.py +27 -5
  140. qiskit/qobj/converters/pulse_instruction.py +6 -6
  141. qiskit/qpy/__init__.py +299 -67
  142. qiskit/qpy/binary_io/circuits.py +216 -47
  143. qiskit/qpy/binary_io/schedules.py +42 -36
  144. qiskit/qpy/binary_io/value.py +201 -22
  145. qiskit/qpy/common.py +1 -1
  146. qiskit/qpy/exceptions.py +20 -0
  147. qiskit/qpy/formats.py +29 -0
  148. qiskit/qpy/type_keys.py +21 -0
  149. qiskit/quantum_info/analysis/distance.py +3 -3
  150. qiskit/quantum_info/analysis/make_observable.py +2 -1
  151. qiskit/quantum_info/analysis/z2_symmetries.py +2 -1
  152. qiskit/quantum_info/operators/channel/chi.py +9 -8
  153. qiskit/quantum_info/operators/channel/choi.py +10 -9
  154. qiskit/quantum_info/operators/channel/kraus.py +2 -1
  155. qiskit/quantum_info/operators/channel/ptm.py +10 -9
  156. qiskit/quantum_info/operators/channel/quantum_channel.py +2 -1
  157. qiskit/quantum_info/operators/channel/stinespring.py +2 -1
  158. qiskit/quantum_info/operators/channel/superop.py +12 -11
  159. qiskit/quantum_info/operators/channel/transformations.py +12 -11
  160. qiskit/quantum_info/operators/dihedral/dihedral.py +5 -4
  161. qiskit/quantum_info/operators/operator.py +43 -30
  162. qiskit/quantum_info/operators/scalar_op.py +10 -9
  163. qiskit/quantum_info/operators/symplectic/base_pauli.py +70 -59
  164. qiskit/quantum_info/operators/symplectic/clifford.py +36 -9
  165. qiskit/quantum_info/operators/symplectic/pauli.py +53 -6
  166. qiskit/quantum_info/operators/symplectic/pauli_list.py +36 -14
  167. qiskit/quantum_info/operators/symplectic/random.py +3 -2
  168. qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py +61 -36
  169. qiskit/quantum_info/states/densitymatrix.py +13 -13
  170. qiskit/quantum_info/states/stabilizerstate.py +3 -3
  171. qiskit/quantum_info/states/statevector.py +14 -13
  172. qiskit/quantum_info/states/utils.py +5 -3
  173. qiskit/result/__init__.py +6 -0
  174. qiskit/result/mitigation/correlated_readout_mitigator.py +3 -2
  175. qiskit/result/mitigation/local_readout_mitigator.py +2 -1
  176. qiskit/result/mitigation/utils.py +3 -2
  177. qiskit/scheduler/__init__.py +10 -1
  178. qiskit/scheduler/methods/__init__.py +1 -8
  179. qiskit/synthesis/__init__.py +3 -6
  180. qiskit/synthesis/discrete_basis/commutator_decompose.py +2 -2
  181. qiskit/synthesis/evolution/lie_trotter.py +7 -14
  182. qiskit/synthesis/evolution/qdrift.py +3 -4
  183. qiskit/synthesis/linear/cnot_synth.py +1 -3
  184. qiskit/synthesis/linear/linear_circuits_utils.py +1 -1
  185. qiskit/synthesis/linear_phase/cz_depth_lnn.py +4 -18
  186. qiskit/synthesis/permutation/__init__.py +1 -0
  187. qiskit/synthesis/permutation/permutation_reverse_lnn.py +90 -0
  188. qiskit/synthesis/qft/qft_decompose_lnn.py +2 -6
  189. qiskit/synthesis/two_qubit/two_qubit_decompose.py +165 -954
  190. qiskit/synthesis/two_qubit/xx_decompose/circuits.py +13 -12
  191. qiskit/synthesis/two_qubit/xx_decompose/decomposer.py +7 -1
  192. qiskit/synthesis/unitary/aqc/__init__.py +1 -1
  193. qiskit/synthesis/unitary/aqc/cnot_structures.py +2 -1
  194. qiskit/synthesis/unitary/aqc/fast_gradient/fast_gradient.py +2 -1
  195. qiskit/synthesis/unitary/qsd.py +3 -2
  196. qiskit/transpiler/__init__.py +7 -3
  197. qiskit/transpiler/layout.py +140 -61
  198. qiskit/transpiler/passes/__init__.py +10 -2
  199. qiskit/transpiler/passes/basis/basis_translator.py +9 -4
  200. qiskit/transpiler/passes/basis/unroll_3q_or_more.py +1 -1
  201. qiskit/transpiler/passes/basis/unroll_custom_definitions.py +1 -1
  202. qiskit/transpiler/passes/calibration/rzx_builder.py +2 -1
  203. qiskit/transpiler/passes/layout/apply_layout.py +8 -3
  204. qiskit/transpiler/passes/layout/sabre_layout.py +15 -3
  205. qiskit/transpiler/passes/layout/set_layout.py +1 -1
  206. qiskit/transpiler/passes/optimization/__init__.py +2 -0
  207. qiskit/transpiler/passes/optimization/commutation_analysis.py +2 -2
  208. qiskit/transpiler/passes/optimization/commutative_cancellation.py +1 -1
  209. qiskit/transpiler/passes/optimization/consolidate_blocks.py +1 -1
  210. qiskit/transpiler/passes/optimization/cx_cancellation.py +10 -0
  211. qiskit/transpiler/passes/optimization/elide_permutations.py +114 -0
  212. qiskit/transpiler/passes/optimization/optimize_1q_decomposition.py +9 -3
  213. qiskit/transpiler/passes/optimization/optimize_annotated.py +248 -12
  214. qiskit/transpiler/passes/optimization/remove_final_reset.py +37 -0
  215. qiskit/transpiler/passes/optimization/template_matching/forward_match.py +1 -3
  216. qiskit/transpiler/passes/routing/__init__.py +1 -0
  217. qiskit/transpiler/passes/routing/basic_swap.py +13 -2
  218. qiskit/transpiler/passes/routing/commuting_2q_gate_routing/commuting_2q_gate_router.py +8 -1
  219. qiskit/transpiler/passes/routing/lookahead_swap.py +7 -1
  220. qiskit/transpiler/passes/routing/sabre_swap.py +10 -6
  221. qiskit/transpiler/passes/routing/star_prerouting.py +417 -0
  222. qiskit/transpiler/passes/routing/stochastic_swap.py +24 -8
  223. qiskit/transpiler/passes/scheduling/__init__.py +1 -1
  224. qiskit/transpiler/passes/scheduling/alap.py +1 -2
  225. qiskit/transpiler/passes/scheduling/alignments/align_measures.py +1 -2
  226. qiskit/transpiler/passes/scheduling/alignments/check_durations.py +9 -6
  227. qiskit/transpiler/passes/scheduling/alignments/pulse_gate_validation.py +8 -0
  228. qiskit/transpiler/passes/scheduling/alignments/reschedule.py +13 -4
  229. qiskit/transpiler/passes/scheduling/asap.py +1 -2
  230. qiskit/transpiler/passes/scheduling/base_scheduler.py +21 -2
  231. qiskit/transpiler/passes/scheduling/dynamical_decoupling.py +26 -4
  232. qiskit/transpiler/passes/scheduling/padding/dynamical_decoupling.py +24 -2
  233. qiskit/transpiler/passes/scheduling/time_unit_conversion.py +28 -4
  234. qiskit/transpiler/passes/synthesis/aqc_plugin.py +2 -2
  235. qiskit/transpiler/passes/synthesis/high_level_synthesis.py +120 -13
  236. qiskit/transpiler/passes/synthesis/unitary_synthesis.py +162 -55
  237. qiskit/transpiler/passes/utils/gates_basis.py +3 -3
  238. qiskit/transpiler/passmanager.py +44 -1
  239. qiskit/transpiler/preset_passmanagers/__init__.py +3 -3
  240. qiskit/transpiler/preset_passmanagers/builtin_plugins.py +34 -16
  241. qiskit/transpiler/preset_passmanagers/common.py +4 -6
  242. qiskit/transpiler/preset_passmanagers/plugin.py +9 -1
  243. qiskit/utils/__init__.py +3 -2
  244. qiskit/utils/optionals.py +6 -2
  245. qiskit/utils/parallel.py +24 -15
  246. qiskit/visualization/array.py +1 -1
  247. qiskit/visualization/bloch.py +2 -3
  248. qiskit/visualization/circuit/matplotlib.py +44 -14
  249. qiskit/visualization/circuit/text.py +38 -18
  250. qiskit/visualization/counts_visualization.py +3 -6
  251. qiskit/visualization/dag_visualization.py +6 -7
  252. qiskit/visualization/gate_map.py +9 -1
  253. qiskit/visualization/pulse_v2/interface.py +8 -3
  254. qiskit/visualization/state_visualization.py +3 -2
  255. qiskit/visualization/timeline/interface.py +18 -8
  256. {qiskit-1.0.2.dist-info → qiskit-1.1.0.dist-info}/METADATA +12 -8
  257. {qiskit-1.0.2.dist-info → qiskit-1.1.0.dist-info}/RECORD +261 -251
  258. {qiskit-1.0.2.dist-info → qiskit-1.1.0.dist-info}/WHEEL +1 -1
  259. qiskit/_qasm2.pyd +0 -0
  260. qiskit/_qasm3.pyd +0 -0
  261. {qiskit-1.0.2.dist-info → qiskit-1.1.0.dist-info}/LICENSE.txt +0 -0
  262. {qiskit-1.0.2.dist-info → qiskit-1.1.0.dist-info}/entry_points.txt +0 -0
  263. {qiskit-1.0.2.dist-info → qiskit-1.1.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,287 @@
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
+ """Sampler V2 implementation for an arbitrary Backend object."""
14
+
15
+ from __future__ import annotations
16
+
17
+ import warnings
18
+ from collections import defaultdict
19
+ from dataclasses import dataclass
20
+ from typing import Iterable
21
+
22
+ import numpy as np
23
+ from numpy.typing import NDArray
24
+
25
+ from qiskit.circuit import QuantumCircuit
26
+ from qiskit.primitives.backend_estimator import _run_circuits
27
+ from qiskit.primitives.base import BaseSamplerV2
28
+ from qiskit.primitives.containers import (
29
+ BitArray,
30
+ DataBin,
31
+ PrimitiveResult,
32
+ SamplerPubLike,
33
+ SamplerPubResult,
34
+ )
35
+ from qiskit.primitives.containers.bit_array import _min_num_bytes
36
+ from qiskit.primitives.containers.sampler_pub import SamplerPub
37
+ from qiskit.primitives.primitive_job import PrimitiveJob
38
+ from qiskit.providers.backend import BackendV1, BackendV2
39
+ from qiskit.result import Result
40
+
41
+
42
+ @dataclass
43
+ class Options:
44
+ """Options for :class:`~.BackendSamplerV2`"""
45
+
46
+ default_shots: int = 1024
47
+ """The default shots to use if none are specified in :meth:`~.run`.
48
+ Default: 1024.
49
+ """
50
+
51
+ seed_simulator: int | None = None
52
+ """The seed to use in the simulator. If None, a random seed will be used.
53
+ Default: None.
54
+ """
55
+
56
+
57
+ @dataclass
58
+ class _MeasureInfo:
59
+ creg_name: str
60
+ num_bits: int
61
+ num_bytes: int
62
+ start: int
63
+
64
+
65
+ class BackendSamplerV2(BaseSamplerV2):
66
+ """Evaluates bitstrings for provided quantum circuits
67
+
68
+ The :class:`~.BackendSamplerV2` class is a generic implementation of the
69
+ :class:`~.BaseSamplerV2` interface that is used to wrap a :class:`~.BackendV2`
70
+ (or :class:`~.BackendV1`) object in the class :class:`~.BaseSamplerV2` API. It
71
+ facilitates using backends that do not provide a native
72
+ :class:`~.BaseSamplerV2` implementation in places that work with
73
+ :class:`~.BaseSamplerV2`. However,
74
+ if you're using a provider that has a native implementation of
75
+ :class:`~.BaseSamplerV2`, it is a better choice to leverage that native
76
+ implementation as it will likely include additional optimizations and be
77
+ a more efficient implementation. The generic nature of this class
78
+ precludes doing any provider- or backend-specific optimizations.
79
+
80
+ This class does not perform any measurement or gate mitigation.
81
+
82
+ Each tuple of ``(circuit, <optional> parameter values, <optional> shots)``, called a sampler
83
+ primitive unified bloc (PUB), produces its own array-valued result. The :meth:`~run` method can
84
+ be given many pubs at once.
85
+
86
+ The options for :class:`~.BackendSamplerV2` consist of the following items.
87
+
88
+ * ``default_shots``: The default shots to use if none are specified in :meth:`~run`.
89
+ Default: 1024.
90
+
91
+ * ``seed_simulator``: The seed to use in the simulator. If None, a random seed will be used.
92
+ Default: None.
93
+
94
+ .. note::
95
+
96
+ This class requires a backend that supports ``memory`` option.
97
+
98
+ """
99
+
100
+ def __init__(
101
+ self,
102
+ *,
103
+ backend: BackendV1 | BackendV2,
104
+ options: dict | None = None,
105
+ ):
106
+ """
107
+ Args:
108
+ backend: The backend to run the primitive on.
109
+ options: The options to control the default shots (``default_shots``) and
110
+ the random seed for the simulator (``seed_simulator``).
111
+ """
112
+ self._backend = backend
113
+ self._options = Options(**options) if options else Options()
114
+
115
+ @property
116
+ def backend(self) -> BackendV1 | BackendV2:
117
+ """Returns the backend which this sampler object based on."""
118
+ return self._backend
119
+
120
+ @property
121
+ def options(self) -> Options:
122
+ """Return the options"""
123
+ return self._options
124
+
125
+ def run(
126
+ self, pubs: Iterable[SamplerPubLike], *, shots: int | None = None
127
+ ) -> PrimitiveJob[PrimitiveResult[SamplerPubResult]]:
128
+ if shots is None:
129
+ shots = self._options.default_shots
130
+ coerced_pubs = [SamplerPub.coerce(pub, shots) for pub in pubs]
131
+ self._validate_pubs(coerced_pubs)
132
+ job = PrimitiveJob(self._run, coerced_pubs)
133
+ job._submit()
134
+ return job
135
+
136
+ def _validate_pubs(self, pubs: list[SamplerPub]):
137
+ for i, pub in enumerate(pubs):
138
+ if len(pub.circuit.cregs) == 0:
139
+ warnings.warn(
140
+ f"The {i}-th pub's circuit has no output classical registers and so the result "
141
+ "will be empty. Did you mean to add measurement instructions?",
142
+ UserWarning,
143
+ )
144
+
145
+ def _run(self, pubs: list[SamplerPub]) -> PrimitiveResult[SamplerPubResult]:
146
+ pub_dict = defaultdict(list)
147
+ # consolidate pubs with the same number of shots
148
+ for i, pub in enumerate(pubs):
149
+ pub_dict[pub.shots].append(i)
150
+
151
+ results = [None] * len(pubs)
152
+ for shots, lst in pub_dict.items():
153
+ # run pubs with the same number of shots at once
154
+ pub_results = self._run_pubs([pubs[i] for i in lst], shots)
155
+ # reconstruct the result of pubs
156
+ for i, pub_result in zip(lst, pub_results):
157
+ results[i] = pub_result
158
+ return PrimitiveResult(results)
159
+
160
+ def _run_pubs(self, pubs: list[SamplerPub], shots: int) -> list[SamplerPubResult]:
161
+ """Compute results for pubs that all require the same value of ``shots``."""
162
+ # prepare circuits
163
+ bound_circuits = [pub.parameter_values.bind_all(pub.circuit) for pub in pubs]
164
+ flatten_circuits = []
165
+ for circuits in bound_circuits:
166
+ flatten_circuits.extend(np.ravel(circuits).tolist())
167
+
168
+ # run circuits
169
+ results, _ = _run_circuits(
170
+ flatten_circuits,
171
+ self._backend,
172
+ memory=True,
173
+ shots=shots,
174
+ seed_simulator=self._options.seed_simulator,
175
+ )
176
+ result_memory = _prepare_memory(results)
177
+
178
+ # pack memory to an ndarray of uint8
179
+ results = []
180
+ start = 0
181
+ for pub, bound in zip(pubs, bound_circuits):
182
+ meas_info, max_num_bytes = _analyze_circuit(pub.circuit)
183
+ end = start + bound.size
184
+ results.append(
185
+ self._postprocess_pub(
186
+ result_memory[start:end], shots, bound.shape, meas_info, max_num_bytes
187
+ )
188
+ )
189
+ start = end
190
+
191
+ return results
192
+
193
+ def _postprocess_pub(
194
+ self,
195
+ result_memory: list[list[str]],
196
+ shots: int,
197
+ shape: tuple[int, ...],
198
+ meas_info: list[_MeasureInfo],
199
+ max_num_bytes: int,
200
+ ) -> SamplerPubResult:
201
+ """Converts the memory data into an array of bit arrays with the shape of the pub."""
202
+ arrays = {
203
+ item.creg_name: np.zeros(shape + (shots, item.num_bytes), dtype=np.uint8)
204
+ for item in meas_info
205
+ }
206
+ memory_array = _memory_array(result_memory, max_num_bytes)
207
+
208
+ for samples, index in zip(memory_array, np.ndindex(*shape)):
209
+ for item in meas_info:
210
+ ary = _samples_to_packed_array(samples, item.num_bits, item.start)
211
+ arrays[item.creg_name][index] = ary
212
+
213
+ meas = {
214
+ item.creg_name: BitArray(arrays[item.creg_name], item.num_bits) for item in meas_info
215
+ }
216
+ return SamplerPubResult(DataBin(**meas, shape=shape), metadata={})
217
+
218
+
219
+ def _analyze_circuit(circuit: QuantumCircuit) -> tuple[list[_MeasureInfo], int]:
220
+ """Analyzes the information for each creg in a circuit."""
221
+ meas_info = []
222
+ max_num_bits = 0
223
+ for creg in circuit.cregs:
224
+ name = creg.name
225
+ num_bits = creg.size
226
+ if num_bits != 0:
227
+ start = circuit.find_bit(creg[0]).index
228
+ else:
229
+ start = 0
230
+ meas_info.append(
231
+ _MeasureInfo(
232
+ creg_name=name,
233
+ num_bits=num_bits,
234
+ num_bytes=_min_num_bytes(num_bits),
235
+ start=start,
236
+ )
237
+ )
238
+ max_num_bits = max(max_num_bits, start + num_bits)
239
+ return meas_info, _min_num_bytes(max_num_bits)
240
+
241
+
242
+ def _prepare_memory(results: list[Result]) -> list[list[str]]:
243
+ """Joins splitted results if exceeding max_experiments"""
244
+ lst = []
245
+ for res in results:
246
+ for exp in res.results:
247
+ if hasattr(exp.data, "memory") and exp.data.memory:
248
+ lst.append(exp.data.memory)
249
+ else:
250
+ # no measure in a circuit
251
+ lst.append(["0x0"] * exp.shots)
252
+ return lst
253
+
254
+
255
+ def _memory_array(results: list[list[str]], num_bytes: int) -> NDArray[np.uint8]:
256
+ """Converts the memory data into an array in an unpacked way."""
257
+ lst = []
258
+ for memory in results:
259
+ if num_bytes > 0:
260
+ data = b"".join(int(i, 16).to_bytes(num_bytes, "big") for i in memory)
261
+ data = np.frombuffer(data, dtype=np.uint8).reshape(-1, num_bytes)
262
+ else:
263
+ # no measure in a circuit
264
+ data = np.zeros((len(memory), num_bytes), dtype=np.uint8)
265
+ lst.append(data)
266
+ ary = np.asarray(lst)
267
+ return np.unpackbits(ary, axis=-1, bitorder="big")
268
+
269
+
270
+ def _samples_to_packed_array(
271
+ samples: NDArray[np.uint8], num_bits: int, start: int
272
+ ) -> NDArray[np.uint8]:
273
+ """Converts an unpacked array of the memory data into a packed array."""
274
+ # samples of `Backend.run(memory=True)` will be the order of
275
+ # clbit_last, ..., clbit_1, clbit_0
276
+ # place samples in the order of clbit_start+num_bits-1, ..., clbit_start+1, clbit_start
277
+ if start == 0:
278
+ ary = samples[:, -start - num_bits :]
279
+ else:
280
+ ary = samples[:, -start - num_bits : -start]
281
+ # pad 0 in the left to align the number to be mod 8
282
+ # since np.packbits(bitorder='big') pads 0 to the right.
283
+ pad_size = -num_bits % 8
284
+ ary = np.pad(ary, ((0, 0), (pad_size, 0)), constant_values=0)
285
+ # pack bits in big endian order
286
+ ary = np.packbits(ary, axis=-1, bitorder="big")
287
+ return ary
@@ -18,8 +18,6 @@ from abc import abstractmethod, ABC
18
18
  from collections.abc import Iterable, Sequence
19
19
  from copy import copy
20
20
  from typing import Generic, TypeVar
21
- import numpy as np
22
- from numpy.typing import NDArray
23
21
 
24
22
  from qiskit.circuit import QuantumCircuit
25
23
  from qiskit.providers import JobV1 as Job
@@ -27,7 +25,6 @@ from qiskit.quantum_info.operators import SparsePauliOp
27
25
  from qiskit.quantum_info.operators.base_operator import BaseOperator
28
26
 
29
27
  from ..containers import (
30
- make_data_bin,
31
28
  DataBin,
32
29
  EstimatorPubLike,
33
30
  PrimitiveResult,
@@ -205,12 +202,10 @@ class BaseEstimatorV2(ABC):
205
202
  """
206
203
 
207
204
  @staticmethod
208
- def _make_data_bin(pub: EstimatorPub) -> DataBin:
209
- # provide a standard way to construct estimator databins to ensure that names match
210
- # across implementations
211
- return make_data_bin(
212
- (("evs", NDArray[np.float64]), ("stds", NDArray[np.float64])), pub.shape
213
- )
205
+ def _make_data_bin(_: EstimatorPub) -> type[DataBin]:
206
+ # this method is present for backwards compat. new primitive implementatinos
207
+ # should avoid it.
208
+ return DataBin
214
209
 
215
210
  @abstractmethod
216
211
  def run(
@@ -23,8 +23,8 @@ from qiskit.circuit import QuantumCircuit
23
23
  from qiskit.providers import JobV1 as Job
24
24
 
25
25
  from ..containers.primitive_result import PrimitiveResult
26
- from ..containers.pub_result import PubResult
27
26
  from ..containers.sampler_pub import SamplerPubLike
27
+ from ..containers.sampler_pub_result import SamplerPubResult
28
28
  from . import validation
29
29
  from .base_primitive import BasePrimitive
30
30
  from .base_primitive_job import BasePrimitiveJob
@@ -165,7 +165,7 @@ class BaseSamplerV2(ABC):
165
165
  @abstractmethod
166
166
  def run(
167
167
  self, pubs: Iterable[SamplerPubLike], *, shots: int | None = None
168
- ) -> BasePrimitiveJob[PrimitiveResult[PubResult]]:
168
+ ) -> BasePrimitiveJob[PrimitiveResult[SamplerPubResult]]:
169
169
  """Run and collect samples from each pub.
170
170
 
171
171
  Args:
@@ -15,11 +15,13 @@ Data containers for primitives.
15
15
  """
16
16
 
17
17
 
18
+ from .bindings_array import BindingsArrayLike
18
19
  from .bit_array import BitArray
19
- from .data_bin import make_data_bin, DataBin
20
+ from .data_bin import DataBin, make_data_bin
21
+ from .estimator_pub import EstimatorPubLike
22
+ from .observables_array import ObservableLike, ObservablesArrayLike
20
23
  from .primitive_result import PrimitiveResult
21
24
  from .pub_result import PubResult
22
- from .estimator_pub import EstimatorPubLike
23
25
  from .sampler_pub import SamplerPubLike
24
- from .bindings_array import BindingsArrayLike
25
- from .observables_array import ObservableLike, ObservablesArrayLike
26
+ from .sampler_pub_result import SamplerPubResult
27
+ from .shape import Shaped
@@ -19,13 +19,15 @@ from __future__ import annotations
19
19
  from collections import defaultdict
20
20
  from functools import partial
21
21
  from itertools import chain, repeat
22
- from typing import Callable, Iterable, Literal, Mapping
22
+ from typing import Callable, Iterable, Literal, Mapping, Sequence
23
23
 
24
24
  import numpy as np
25
25
  from numpy.typing import NDArray
26
26
 
27
- from qiskit.result import Counts
27
+ from qiskit.exceptions import QiskitError
28
+ from qiskit.result import Counts, sampled_expectation_value
28
29
 
30
+ from .observables_array import ObservablesArray, ObservablesArrayLike
29
31
  from .shape import ShapedMixin, ShapeInput, shape_tuple
30
32
 
31
33
  # this lookup table tells you how many bits are 1 in each uint8 value
@@ -37,6 +39,23 @@ def _min_num_bytes(num_bits: int) -> int:
37
39
  return num_bits // 8 + (num_bits % 8 > 0)
38
40
 
39
41
 
42
+ def _unpack(bit_array: BitArray) -> NDArray[np.uint8]:
43
+ arr = np.unpackbits(bit_array.array, axis=-1, bitorder="big")
44
+ arr = arr[..., -1 : -bit_array.num_bits - 1 : -1]
45
+ return arr
46
+
47
+
48
+ def _pack(arr: NDArray[np.uint8]) -> tuple[NDArray[np.uint8], int]:
49
+ arr = arr[..., ::-1]
50
+ num_bits = arr.shape[-1]
51
+ pad_size = -num_bits % 8
52
+ if pad_size > 0:
53
+ pad_width = [(0, 0)] * (arr.ndim - 1) + [(pad_size, 0)]
54
+ arr = np.pad(arr, pad_width, constant_values=0)
55
+ arr = np.packbits(arr, axis=-1, bitorder="big")
56
+ return arr, num_bits
57
+
58
+
40
59
  class BitArray(ShapedMixin):
41
60
  """Stores an array of bit values.
42
61
 
@@ -110,6 +129,14 @@ class BitArray(ShapedMixin):
110
129
  desc = f"<shape={self.shape}, num_shots={self.num_shots}, num_bits={self.num_bits}>"
111
130
  return f"BitArray({desc})"
112
131
 
132
+ def __getitem__(self, indices):
133
+ """Slices the array along an existing axis of the array."""
134
+ if isinstance(indices, tuple) and len(indices) >= self.ndim + 2:
135
+ raise ValueError(
136
+ "BitArrays cannot be sliced along the bits axis, see slice_bits() instead."
137
+ )
138
+ return BitArray(self._array[indices], self.num_bits)
139
+
113
140
  @property
114
141
  def array(self) -> NDArray[np.uint8]:
115
142
  """The raw NumPy array of data."""
@@ -347,3 +374,267 @@ class BitArray(ShapedMixin):
347
374
  else:
348
375
  raise ValueError("Cannot change the size of the array.")
349
376
  return BitArray(self._array.reshape(shape), self.num_bits)
377
+
378
+ def transpose(self, *axes) -> "BitArray":
379
+ """Return a bit array with axes transposed.
380
+
381
+ Args:
382
+ axes: None, tuple of ints or n ints. See `ndarray.transpose
383
+ <https://numpy.org/doc/stable/reference/generated/
384
+ numpy.ndarray.transpose.html#numpy.ndarray.transpose>`_
385
+ for the details.
386
+
387
+ Returns:
388
+ BitArray: A bit array with axes permuted.
389
+
390
+ Raises:
391
+ ValueError: If ``axes`` don't match this bit array.
392
+ ValueError: If ``axes`` includes any indices that are out of bounds.
393
+ """
394
+ if len(axes) == 0:
395
+ axes = tuple(reversed(range(self.ndim)))
396
+ if len(axes) == 1 and isinstance(axes[0], Sequence):
397
+ axes = axes[0]
398
+ if len(axes) != self.ndim:
399
+ raise ValueError("axes don't match bit array")
400
+ for i in axes:
401
+ if i >= self.ndim or self.ndim + i < 0:
402
+ raise ValueError(
403
+ f"axis {i} is out of bounds for bit array of dimension {self.ndim}."
404
+ )
405
+ axes = tuple(i if i >= 0 else self.ndim + i for i in axes) + (-2, -1)
406
+ return BitArray(self._array.transpose(axes), self.num_bits)
407
+
408
+ def slice_bits(self, indices: int | Sequence[int]) -> "BitArray":
409
+ """Return a bit array sliced along the bit axis of some indices of interest.
410
+
411
+ .. note::
412
+
413
+ The convention used by this method is that the index ``0`` corresponds to
414
+ the least-significant bit in the :attr:`~array`, or equivalently
415
+ the right-most bitstring entry as returned by
416
+ :meth:`~get_counts` or :meth:`~get_bitstrings`, etc.
417
+
418
+ If this bit array was produced by a sampler, then an index ``i`` corresponds to the
419
+ :class:`~.ClassicalRegister` location ``creg[i]``.
420
+
421
+ Args:
422
+ indices: The bit positions of interest to slice along.
423
+
424
+ Returns:
425
+ A bit array sliced along the bit axis.
426
+
427
+ Raises:
428
+ ValueError: If there are any invalid indices of the bit axis.
429
+ """
430
+ if isinstance(indices, int):
431
+ indices = (indices,)
432
+ for index in indices:
433
+ if index < 0 or index >= self.num_bits:
434
+ raise ValueError(
435
+ f"index {index} is out of bounds for the number of bits {self.num_bits}."
436
+ )
437
+ # This implementation introduces a temporary 8x memory overhead due to bit
438
+ # unpacking. This could be fixed using bitwise functions, at the expense of a
439
+ # more complicated implementation.
440
+ arr = _unpack(self)
441
+ arr = arr[..., indices]
442
+ arr, num_bits = _pack(arr)
443
+ return BitArray(arr, num_bits)
444
+
445
+ def slice_shots(self, indices: int | Sequence[int]) -> "BitArray":
446
+ """Return a bit array sliced along the shots axis of some indices of interest.
447
+
448
+ Args:
449
+ indices: The shots positions of interest to slice along.
450
+
451
+ Returns:
452
+ A bit array sliced along the shots axis.
453
+
454
+ Raises:
455
+ ValueError: If there are any invalid indices of the shots axis.
456
+ """
457
+ if isinstance(indices, int):
458
+ indices = (indices,)
459
+ for index in indices:
460
+ if index < 0 or index >= self.num_shots:
461
+ raise ValueError(
462
+ f"index {index} is out of bounds for the number of shots {self.num_shots}."
463
+ )
464
+ arr = self._array
465
+ arr = arr[..., indices, :]
466
+ return BitArray(arr, self.num_bits)
467
+
468
+ def expectation_values(self, observables: ObservablesArrayLike) -> NDArray[np.float64]:
469
+ """Compute the expectation values of the provided observables, broadcasted against
470
+ this bit array.
471
+
472
+ .. note::
473
+
474
+ This method returns the real part of the expectation value even if
475
+ the operator has complex coefficients due to the specification of
476
+ :func:`~.sampled_expectation_value`.
477
+
478
+ Args:
479
+ observables: The observable(s) to take the expectation value of.
480
+ Must have a shape broadcastable with with this bit array and
481
+ the same number of qubits as the number of bits of this bit array.
482
+ The observables must be diagonal (I, Z, 0 or 1) too.
483
+
484
+ Returns:
485
+ An array of expectation values whose shape is the broadcast shape of ``observables``
486
+ and this bit array.
487
+
488
+ Raises:
489
+ ValueError: If the provided observables does not have a shape broadcastable with
490
+ this bit array.
491
+ ValueError: If the provided observables does not have the same number of qubits as
492
+ the number of bits of this bit array.
493
+ ValueError: If the provided observables are not diagonal.
494
+ """
495
+ observables = ObservablesArray.coerce(observables)
496
+ arr_indices = np.fromiter(np.ndindex(self.shape), dtype=object).reshape(self.shape)
497
+ bc_indices, bc_obs = np.broadcast_arrays(arr_indices, observables)
498
+ counts = {}
499
+ arr = np.zeros_like(bc_indices, dtype=float)
500
+ for index in np.ndindex(bc_indices.shape):
501
+ loc = bc_indices[index]
502
+ for pauli, coeff in bc_obs[index].items():
503
+ if loc not in counts:
504
+ counts[loc] = self.get_counts(loc)
505
+ try:
506
+ expval = sampled_expectation_value(counts[loc], pauli)
507
+ except QiskitError as ex:
508
+ raise ValueError(ex.message) from ex
509
+ arr[index] += expval * coeff
510
+ return arr
511
+
512
+ @staticmethod
513
+ def concatenate(bit_arrays: Sequence[BitArray], axis: int = 0) -> BitArray:
514
+ """Join a sequence of bit arrays along an existing axis.
515
+
516
+ Args:
517
+ bit_arrays: The bit arrays must have (1) the same number of bits,
518
+ (2) the same number of shots, and
519
+ (3) the same shape, except in the dimension corresponding to axis
520
+ (the first, by default).
521
+ axis: The axis along which the arrays will be joined. Default is 0.
522
+
523
+ Returns:
524
+ The concatenated bit array.
525
+
526
+ Raises:
527
+ ValueError: If the sequence of bit arrays is empty.
528
+ ValueError: If any bit arrays has a different number of bits.
529
+ ValueError: If any bit arrays has a different number of shots.
530
+ ValueError: If any bit arrays has a different number of dimensions.
531
+ """
532
+ if len(bit_arrays) == 0:
533
+ raise ValueError("Need at least one bit array to concatenate")
534
+ num_bits = bit_arrays[0].num_bits
535
+ num_shots = bit_arrays[0].num_shots
536
+ ndim = bit_arrays[0].ndim
537
+ if ndim == 0:
538
+ raise ValueError("Zero-dimensional bit arrays cannot be concatenated")
539
+ for i, ba in enumerate(bit_arrays):
540
+ if ba.num_bits != num_bits:
541
+ raise ValueError(
542
+ "All bit arrays must have same number of bits, "
543
+ f"but the bit array at index 0 has {num_bits} bits "
544
+ f"and the bit array at index {i} has {ba.num_bits} bits."
545
+ )
546
+ if ba.num_shots != num_shots:
547
+ raise ValueError(
548
+ "All bit arrays must have same number of shots, "
549
+ f"but the bit array at index 0 has {num_shots} shots "
550
+ f"and the bit array at index {i} has {ba.num_shots} shots."
551
+ )
552
+ if ba.ndim != ndim:
553
+ raise ValueError(
554
+ "All bit arrays must have same number of dimensions, "
555
+ f"but the bit array at index 0 has {ndim} dimension(s) "
556
+ f"and the bit array at index {i} has {ba.ndim} dimension(s)."
557
+ )
558
+ if axis < 0 or axis >= ndim:
559
+ raise ValueError(f"axis {axis} is out of bounds for bit array of dimension {ndim}.")
560
+ data = np.concatenate([ba.array for ba in bit_arrays], axis=axis)
561
+ return BitArray(data, num_bits)
562
+
563
+ @staticmethod
564
+ def concatenate_shots(bit_arrays: Sequence[BitArray]) -> BitArray:
565
+ """Join a sequence of bit arrays along the shots axis.
566
+
567
+ Args:
568
+ bit_arrays: The bit arrays must have (1) the same number of bits,
569
+ and (2) the same shape.
570
+
571
+ Returns:
572
+ The stacked bit array.
573
+
574
+ Raises:
575
+ ValueError: If the sequence of bit arrays is empty.
576
+ ValueError: If any bit arrays has a different number of bits.
577
+ ValueError: If any bit arrays has a different shape.
578
+ """
579
+ if len(bit_arrays) == 0:
580
+ raise ValueError("Need at least one bit array to stack")
581
+ num_bits = bit_arrays[0].num_bits
582
+ shape = bit_arrays[0].shape
583
+ for i, ba in enumerate(bit_arrays):
584
+ if ba.num_bits != num_bits:
585
+ raise ValueError(
586
+ "All bit arrays must have same number of bits, "
587
+ f"but the bit array at index 0 has {num_bits} bits "
588
+ f"and the bit array at index {i} has {ba.num_bits} bits."
589
+ )
590
+ if ba.shape != shape:
591
+ raise ValueError(
592
+ "All bit arrays must have same shape, "
593
+ f"but the bit array at index 0 has shape {shape} "
594
+ f"and the bit array at index {i} has shape {ba.shape}."
595
+ )
596
+ data = np.concatenate([ba.array for ba in bit_arrays], axis=-2)
597
+ return BitArray(data, num_bits)
598
+
599
+ @staticmethod
600
+ def concatenate_bits(bit_arrays: Sequence[BitArray]) -> BitArray:
601
+ """Join a sequence of bit arrays along the bits axis.
602
+
603
+ .. note::
604
+ This method is equivalent to per-shot bitstring concatenation.
605
+
606
+ Args:
607
+ bit_arrays: Bit arrays that have (1) the same number of shots,
608
+ and (2) the same shape.
609
+
610
+ Returns:
611
+ The stacked bit array.
612
+
613
+ Raises:
614
+ ValueError: If the sequence of bit arrays is empty.
615
+ ValueError: If any bit arrays has a different number of shots.
616
+ ValueError: If any bit arrays has a different shape.
617
+ """
618
+ if len(bit_arrays) == 0:
619
+ raise ValueError("Need at least one bit array to stack")
620
+ num_shots = bit_arrays[0].num_shots
621
+ shape = bit_arrays[0].shape
622
+ for i, ba in enumerate(bit_arrays):
623
+ if ba.num_shots != num_shots:
624
+ raise ValueError(
625
+ "All bit arrays must have same number of shots, "
626
+ f"but the bit array at index 0 has {num_shots} shots "
627
+ f"and the bit array at index {i} has {ba.num_shots} shots."
628
+ )
629
+ if ba.shape != shape:
630
+ raise ValueError(
631
+ "All bit arrays must have same shape, "
632
+ f"but the bit array at index 0 has shape {shape} "
633
+ f"and the bit array at index {i} has shape {ba.shape}."
634
+ )
635
+ # This implementation introduces a temporary 8x memory overhead due to bit
636
+ # unpacking. This could be fixed using bitwise functions, at the expense of a
637
+ # more complicated implementation.
638
+ data = np.concatenate([_unpack(ba) for ba in bit_arrays], axis=-1)
639
+ data, num_bits = _pack(data)
640
+ return BitArray(data, num_bits)