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
@@ -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 typing import Iterable
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__(self, op, qargs: Iterable[Qubit] = (), cargs: Iterable[Clbit] = (), dag=None):
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
- if len(in_programs) == 1:
229
- out_program = _run_workflow(
230
- program=in_programs[0],
231
- pass_manager=self,
232
- callback=callback,
233
- **kwargs,
234
- )
235
- if is_list:
236
- return [out_program]
237
- return out_program
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
@@ -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, hamiltonian1, [theta1])])
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, [hamiltonian1, hamiltonian3], [theta1, theta3]),
99
- (psi2, hamiltonian2, theta2)
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 Blocks). Each pub contains values that, together,
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.statevector_sampler import Sampler
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
- Primitives V2
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
- for i, vals in enumerate(packed_vals):
421
- for j, val in enumerate(vals):
422
- inds[i] += val.item() * (1 << (8 * j))
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