qiskit 1.0.2__cp38-abi3-win32.whl → 1.1.0rc1__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 (247) 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/disassemble.py +5 -6
  6. qiskit/circuit/__init__.py +1131 -169
  7. qiskit/circuit/_classical_resource_map.py +7 -6
  8. qiskit/circuit/_utils.py +18 -8
  9. qiskit/circuit/annotated_operation.py +21 -0
  10. qiskit/circuit/barrier.py +10 -13
  11. qiskit/circuit/bit.py +0 -1
  12. qiskit/circuit/classical/__init__.py +2 -2
  13. qiskit/circuit/classical/expr/__init__.py +39 -5
  14. qiskit/circuit/classical/expr/constructors.py +84 -1
  15. qiskit/circuit/classical/expr/expr.py +83 -13
  16. qiskit/circuit/classical/expr/visitors.py +83 -0
  17. qiskit/circuit/commutation_checker.py +86 -51
  18. qiskit/circuit/controlflow/_builder_utils.py +9 -1
  19. qiskit/circuit/controlflow/break_loop.py +8 -22
  20. qiskit/circuit/controlflow/builder.py +116 -1
  21. qiskit/circuit/controlflow/continue_loop.py +8 -22
  22. qiskit/circuit/controlflow/control_flow.py +47 -8
  23. qiskit/circuit/controlflow/for_loop.py +8 -23
  24. qiskit/circuit/controlflow/if_else.py +13 -27
  25. qiskit/circuit/controlflow/switch_case.py +14 -21
  26. qiskit/circuit/controlflow/while_loop.py +9 -23
  27. qiskit/circuit/controlledgate.py +2 -2
  28. qiskit/circuit/delay.py +7 -5
  29. qiskit/circuit/gate.py +20 -7
  30. qiskit/circuit/instruction.py +31 -30
  31. qiskit/circuit/instructionset.py +9 -22
  32. qiskit/circuit/library/__init__.py +8 -2
  33. qiskit/circuit/library/arithmetic/integer_comparator.py +2 -2
  34. qiskit/circuit/library/arithmetic/quadratic_form.py +3 -2
  35. qiskit/circuit/library/blueprintcircuit.py +29 -7
  36. qiskit/circuit/library/data_preparation/state_preparation.py +6 -5
  37. qiskit/circuit/library/generalized_gates/diagonal.py +5 -4
  38. qiskit/circuit/library/generalized_gates/isometry.py +51 -254
  39. qiskit/circuit/library/generalized_gates/pauli.py +2 -2
  40. qiskit/circuit/library/generalized_gates/permutation.py +4 -1
  41. qiskit/circuit/library/generalized_gates/rv.py +15 -11
  42. qiskit/circuit/library/generalized_gates/uc.py +2 -98
  43. qiskit/circuit/library/generalized_gates/unitary.py +9 -4
  44. qiskit/circuit/library/hamiltonian_gate.py +11 -5
  45. qiskit/circuit/library/n_local/efficient_su2.py +5 -5
  46. qiskit/circuit/library/n_local/n_local.py +100 -49
  47. qiskit/circuit/library/n_local/two_local.py +3 -59
  48. qiskit/circuit/library/overlap.py +3 -3
  49. qiskit/circuit/library/phase_oracle.py +1 -1
  50. qiskit/circuit/library/quantum_volume.py +39 -38
  51. qiskit/circuit/library/standard_gates/equivalence_library.py +50 -0
  52. qiskit/circuit/library/standard_gates/global_phase.py +4 -2
  53. qiskit/circuit/library/standard_gates/i.py +1 -2
  54. qiskit/circuit/library/standard_gates/iswap.py +1 -2
  55. qiskit/circuit/library/standard_gates/multi_control_rotation_gates.py +11 -5
  56. qiskit/circuit/library/standard_gates/p.py +31 -15
  57. qiskit/circuit/library/standard_gates/r.py +4 -3
  58. qiskit/circuit/library/standard_gates/rx.py +7 -4
  59. qiskit/circuit/library/standard_gates/rxx.py +4 -3
  60. qiskit/circuit/library/standard_gates/ry.py +7 -4
  61. qiskit/circuit/library/standard_gates/ryy.py +4 -3
  62. qiskit/circuit/library/standard_gates/rz.py +7 -4
  63. qiskit/circuit/library/standard_gates/rzx.py +4 -3
  64. qiskit/circuit/library/standard_gates/rzz.py +4 -3
  65. qiskit/circuit/library/standard_gates/s.py +4 -8
  66. qiskit/circuit/library/standard_gates/t.py +2 -4
  67. qiskit/circuit/library/standard_gates/u.py +16 -11
  68. qiskit/circuit/library/standard_gates/u1.py +6 -2
  69. qiskit/circuit/library/standard_gates/u2.py +4 -2
  70. qiskit/circuit/library/standard_gates/u3.py +9 -5
  71. qiskit/circuit/library/standard_gates/x.py +22 -11
  72. qiskit/circuit/library/standard_gates/xx_minus_yy.py +4 -3
  73. qiskit/circuit/library/standard_gates/xx_plus_yy.py +7 -5
  74. qiskit/circuit/library/standard_gates/z.py +1 -2
  75. qiskit/circuit/measure.py +4 -1
  76. qiskit/circuit/operation.py +13 -8
  77. qiskit/circuit/parameter.py +11 -6
  78. qiskit/circuit/quantumcircuit.py +864 -128
  79. qiskit/circuit/quantumcircuitdata.py +2 -2
  80. qiskit/circuit/reset.py +5 -2
  81. qiskit/circuit/store.py +95 -0
  82. qiskit/compiler/assembler.py +22 -22
  83. qiskit/compiler/transpiler.py +63 -112
  84. qiskit/converters/circuit_to_dag.py +7 -0
  85. qiskit/converters/circuit_to_dagdependency_v2.py +47 -0
  86. qiskit/converters/circuit_to_gate.py +2 -0
  87. qiskit/converters/circuit_to_instruction.py +22 -0
  88. qiskit/converters/dag_to_circuit.py +4 -0
  89. qiskit/converters/dag_to_dagdependency_v2.py +44 -0
  90. qiskit/dagcircuit/collect_blocks.py +15 -10
  91. qiskit/dagcircuit/dagcircuit.py +434 -124
  92. qiskit/dagcircuit/dagdependency.py +19 -12
  93. qiskit/dagcircuit/dagdependency_v2.py +641 -0
  94. qiskit/dagcircuit/dagdepnode.py +19 -16
  95. qiskit/dagcircuit/dagnode.py +14 -4
  96. qiskit/primitives/__init__.py +12 -8
  97. qiskit/primitives/backend_estimator.py +3 -5
  98. qiskit/primitives/backend_estimator_v2.py +410 -0
  99. qiskit/primitives/backend_sampler_v2.py +287 -0
  100. qiskit/primitives/base/base_estimator.py +4 -9
  101. qiskit/primitives/base/base_sampler.py +2 -2
  102. qiskit/primitives/containers/__init__.py +5 -4
  103. qiskit/primitives/containers/bit_array.py +292 -2
  104. qiskit/primitives/containers/data_bin.py +123 -50
  105. qiskit/primitives/containers/estimator_pub.py +10 -3
  106. qiskit/primitives/containers/observables_array.py +2 -2
  107. qiskit/primitives/containers/pub_result.py +1 -1
  108. qiskit/primitives/containers/sampler_pub.py +19 -3
  109. qiskit/primitives/containers/sampler_pub_result.py +74 -0
  110. qiskit/primitives/containers/shape.py +1 -1
  111. qiskit/primitives/statevector_estimator.py +4 -4
  112. qiskit/primitives/statevector_sampler.py +7 -12
  113. qiskit/providers/__init__.py +17 -18
  114. qiskit/providers/backend.py +2 -2
  115. qiskit/providers/backend_compat.py +8 -10
  116. qiskit/providers/basic_provider/basic_provider_tools.py +67 -31
  117. qiskit/providers/basic_provider/basic_simulator.py +81 -21
  118. qiskit/providers/fake_provider/fake_1q.py +1 -1
  119. qiskit/providers/fake_provider/fake_backend.py +3 -408
  120. qiskit/providers/fake_provider/generic_backend_v2.py +26 -14
  121. qiskit/providers/provider.py +16 -0
  122. qiskit/pulse/builder.py +4 -1
  123. qiskit/pulse/parameter_manager.py +60 -4
  124. qiskit/pulse/schedule.py +29 -13
  125. qiskit/pulse/utils.py +61 -20
  126. qiskit/qasm2/__init__.py +1 -5
  127. qiskit/qasm2/parse.py +1 -4
  128. qiskit/qasm3/__init__.py +42 -5
  129. qiskit/qasm3/ast.py +19 -0
  130. qiskit/qasm3/exporter.py +178 -106
  131. qiskit/qasm3/printer.py +27 -5
  132. qiskit/qpy/__init__.py +247 -13
  133. qiskit/qpy/binary_io/circuits.py +216 -47
  134. qiskit/qpy/binary_io/schedules.py +42 -36
  135. qiskit/qpy/binary_io/value.py +201 -22
  136. qiskit/qpy/common.py +1 -1
  137. qiskit/qpy/exceptions.py +20 -0
  138. qiskit/qpy/formats.py +29 -0
  139. qiskit/qpy/type_keys.py +21 -0
  140. qiskit/quantum_info/analysis/distance.py +3 -3
  141. qiskit/quantum_info/analysis/make_observable.py +2 -1
  142. qiskit/quantum_info/analysis/z2_symmetries.py +2 -1
  143. qiskit/quantum_info/operators/channel/chi.py +9 -8
  144. qiskit/quantum_info/operators/channel/choi.py +10 -9
  145. qiskit/quantum_info/operators/channel/kraus.py +2 -1
  146. qiskit/quantum_info/operators/channel/ptm.py +10 -9
  147. qiskit/quantum_info/operators/channel/quantum_channel.py +2 -1
  148. qiskit/quantum_info/operators/channel/stinespring.py +2 -1
  149. qiskit/quantum_info/operators/channel/superop.py +12 -11
  150. qiskit/quantum_info/operators/channel/transformations.py +12 -11
  151. qiskit/quantum_info/operators/dihedral/dihedral.py +5 -4
  152. qiskit/quantum_info/operators/operator.py +43 -30
  153. qiskit/quantum_info/operators/scalar_op.py +10 -9
  154. qiskit/quantum_info/operators/symplectic/base_pauli.py +70 -59
  155. qiskit/quantum_info/operators/symplectic/clifford.py +36 -9
  156. qiskit/quantum_info/operators/symplectic/pauli.py +48 -4
  157. qiskit/quantum_info/operators/symplectic/pauli_list.py +36 -14
  158. qiskit/quantum_info/operators/symplectic/random.py +3 -2
  159. qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py +54 -33
  160. qiskit/quantum_info/states/densitymatrix.py +13 -13
  161. qiskit/quantum_info/states/stabilizerstate.py +3 -3
  162. qiskit/quantum_info/states/statevector.py +14 -13
  163. qiskit/quantum_info/states/utils.py +5 -3
  164. qiskit/result/mitigation/correlated_readout_mitigator.py +3 -2
  165. qiskit/result/mitigation/local_readout_mitigator.py +2 -1
  166. qiskit/result/mitigation/utils.py +3 -2
  167. qiskit/synthesis/__init__.py +2 -0
  168. qiskit/synthesis/discrete_basis/commutator_decompose.py +2 -2
  169. qiskit/synthesis/evolution/lie_trotter.py +7 -14
  170. qiskit/synthesis/evolution/qdrift.py +3 -4
  171. qiskit/synthesis/linear/cnot_synth.py +1 -3
  172. qiskit/synthesis/linear/linear_circuits_utils.py +1 -1
  173. qiskit/synthesis/linear_phase/cz_depth_lnn.py +4 -18
  174. qiskit/synthesis/permutation/__init__.py +1 -0
  175. qiskit/synthesis/permutation/permutation_reverse_lnn.py +90 -0
  176. qiskit/synthesis/qft/qft_decompose_lnn.py +2 -6
  177. qiskit/synthesis/two_qubit/two_qubit_decompose.py +165 -954
  178. qiskit/synthesis/two_qubit/xx_decompose/circuits.py +13 -12
  179. qiskit/synthesis/two_qubit/xx_decompose/decomposer.py +7 -1
  180. qiskit/synthesis/unitary/aqc/__init__.py +1 -1
  181. qiskit/synthesis/unitary/aqc/cnot_structures.py +2 -1
  182. qiskit/synthesis/unitary/aqc/fast_gradient/fast_gradient.py +2 -1
  183. qiskit/synthesis/unitary/qsd.py +3 -2
  184. qiskit/transpiler/__init__.py +7 -3
  185. qiskit/transpiler/layout.py +140 -61
  186. qiskit/transpiler/passes/__init__.py +6 -0
  187. qiskit/transpiler/passes/basis/basis_translator.py +7 -2
  188. qiskit/transpiler/passes/basis/unroll_3q_or_more.py +1 -1
  189. qiskit/transpiler/passes/basis/unroll_custom_definitions.py +1 -1
  190. qiskit/transpiler/passes/calibration/rzx_builder.py +2 -1
  191. qiskit/transpiler/passes/layout/apply_layout.py +8 -3
  192. qiskit/transpiler/passes/layout/sabre_layout.py +15 -3
  193. qiskit/transpiler/passes/layout/set_layout.py +1 -1
  194. qiskit/transpiler/passes/optimization/__init__.py +2 -0
  195. qiskit/transpiler/passes/optimization/commutation_analysis.py +2 -2
  196. qiskit/transpiler/passes/optimization/commutative_cancellation.py +1 -1
  197. qiskit/transpiler/passes/optimization/consolidate_blocks.py +1 -1
  198. qiskit/transpiler/passes/optimization/cx_cancellation.py +10 -0
  199. qiskit/transpiler/passes/optimization/elide_permutations.py +114 -0
  200. qiskit/transpiler/passes/optimization/optimize_1q_decomposition.py +9 -3
  201. qiskit/transpiler/passes/optimization/optimize_annotated.py +248 -12
  202. qiskit/transpiler/passes/optimization/remove_final_reset.py +37 -0
  203. qiskit/transpiler/passes/optimization/template_matching/forward_match.py +1 -3
  204. qiskit/transpiler/passes/routing/__init__.py +1 -0
  205. qiskit/transpiler/passes/routing/basic_swap.py +13 -2
  206. qiskit/transpiler/passes/routing/lookahead_swap.py +7 -1
  207. qiskit/transpiler/passes/routing/sabre_swap.py +10 -6
  208. qiskit/transpiler/passes/routing/star_prerouting.py +417 -0
  209. qiskit/transpiler/passes/routing/stochastic_swap.py +24 -8
  210. qiskit/transpiler/passes/scheduling/__init__.py +1 -1
  211. qiskit/transpiler/passes/scheduling/alap.py +1 -2
  212. qiskit/transpiler/passes/scheduling/alignments/align_measures.py +1 -2
  213. qiskit/transpiler/passes/scheduling/alignments/check_durations.py +9 -6
  214. qiskit/transpiler/passes/scheduling/alignments/pulse_gate_validation.py +8 -0
  215. qiskit/transpiler/passes/scheduling/alignments/reschedule.py +13 -4
  216. qiskit/transpiler/passes/scheduling/asap.py +1 -2
  217. qiskit/transpiler/passes/scheduling/base_scheduler.py +21 -2
  218. qiskit/transpiler/passes/scheduling/dynamical_decoupling.py +26 -4
  219. qiskit/transpiler/passes/scheduling/padding/dynamical_decoupling.py +24 -2
  220. qiskit/transpiler/passes/scheduling/time_unit_conversion.py +28 -4
  221. qiskit/transpiler/passes/synthesis/aqc_plugin.py +2 -2
  222. qiskit/transpiler/passes/synthesis/high_level_synthesis.py +120 -13
  223. qiskit/transpiler/passes/synthesis/unitary_synthesis.py +162 -55
  224. qiskit/transpiler/passes/utils/gates_basis.py +3 -3
  225. qiskit/transpiler/passmanager.py +44 -1
  226. qiskit/transpiler/preset_passmanagers/__init__.py +3 -3
  227. qiskit/transpiler/preset_passmanagers/builtin_plugins.py +34 -16
  228. qiskit/transpiler/preset_passmanagers/common.py +4 -6
  229. qiskit/transpiler/preset_passmanagers/plugin.py +9 -1
  230. qiskit/utils/optionals.py +6 -2
  231. qiskit/visualization/array.py +1 -1
  232. qiskit/visualization/bloch.py +2 -3
  233. qiskit/visualization/circuit/matplotlib.py +44 -14
  234. qiskit/visualization/circuit/text.py +38 -18
  235. qiskit/visualization/counts_visualization.py +3 -6
  236. qiskit/visualization/dag_visualization.py +6 -7
  237. qiskit/visualization/pulse_v2/interface.py +8 -3
  238. qiskit/visualization/state_visualization.py +3 -2
  239. qiskit/visualization/timeline/interface.py +18 -8
  240. {qiskit-1.0.2.dist-info → qiskit-1.1.0rc1.dist-info}/METADATA +12 -8
  241. {qiskit-1.0.2.dist-info → qiskit-1.1.0rc1.dist-info}/RECORD +245 -235
  242. {qiskit-1.0.2.dist-info → qiskit-1.1.0rc1.dist-info}/WHEEL +1 -1
  243. qiskit/_qasm2.pyd +0 -0
  244. qiskit/_qasm3.pyd +0 -0
  245. {qiskit-1.0.2.dist-info → qiskit-1.1.0rc1.dist-info}/LICENSE.txt +0 -0
  246. {qiskit-1.0.2.dist-info → qiskit-1.1.0rc1.dist-info}/entry_points.txt +0 -0
  247. {qiskit-1.0.2.dist-info → qiskit-1.1.0rc1.dist-info}/top_level.txt +0 -0
@@ -15,7 +15,7 @@ ScalarOp class
15
15
  """
16
16
 
17
17
  from __future__ import annotations
18
- import copy
18
+ import copy as _copy
19
19
  from numbers import Number
20
20
  import numpy as np
21
21
 
@@ -52,10 +52,11 @@ class ScalarOp(LinearOp):
52
52
  self._coeff = coeff
53
53
  super().__init__(input_dims=dims, output_dims=dims)
54
54
 
55
- def __array__(self, dtype=None):
56
- if dtype:
57
- return np.asarray(self.to_matrix(), dtype=dtype)
58
- return self.to_matrix()
55
+ def __array__(self, dtype=None, copy=None):
56
+ if copy is False:
57
+ raise ValueError("could not produce matrix without calculation")
58
+ arr = self.to_matrix()
59
+ return arr if dtype is None else arr.astype(dtype, copy=False)
59
60
 
60
61
  def __repr__(self):
61
62
  return f"ScalarOp({self.input_dims()}, coeff={self.coeff})"
@@ -104,7 +105,7 @@ class ScalarOp(LinearOp):
104
105
  # If other is also an ScalarOp we only need to
105
106
  # update the coefficient and dimensions
106
107
  if isinstance(other, ScalarOp):
107
- ret = copy.copy(self)
108
+ ret = _copy.copy(self)
108
109
  ret._coeff = self.coeff * other.coeff
109
110
  ret._op_shape = new_shape
110
111
  return ret
@@ -112,7 +113,7 @@ class ScalarOp(LinearOp):
112
113
  # If we are composing on the full system we return the
113
114
  # other operator with reshaped dimensions
114
115
  if qargs is None:
115
- ret = copy.copy(other)
116
+ ret = _copy.copy(other)
116
117
  ret._op_shape = new_shape
117
118
  # Other operator might not support scalar multiplication
118
119
  # so we treat the identity as a special case to avoid a
@@ -148,7 +149,7 @@ class ScalarOp(LinearOp):
148
149
  other = Operator(other)
149
150
 
150
151
  if isinstance(other, ScalarOp):
151
- ret = copy.copy(self)
152
+ ret = _copy.copy(self)
152
153
  ret._coeff = self.coeff * other.coeff
153
154
  ret._op_shape = self._op_shape.tensor(other._op_shape)
154
155
  return ret
@@ -160,7 +161,7 @@ class ScalarOp(LinearOp):
160
161
  other = Operator(other)
161
162
 
162
163
  if isinstance(other, ScalarOp):
163
- ret = copy.copy(self)
164
+ ret = _copy.copy(self)
164
165
  ret._coeff = self.coeff * other.coeff
165
166
  ret._op_shape = self._op_shape.expand(other._op_shape)
166
167
  return ret
@@ -541,75 +541,50 @@ class BasePauli(BaseOperator, AdjointMixin, MultiplyMixin):
541
541
  if qargs is None:
542
542
  qargs = list(range(self.num_qubits))
543
543
 
544
- if isinstance(circuit, QuantumCircuit):
545
- gate = circuit.to_instruction()
546
- else:
544
+ if not isinstance(circuit, QuantumCircuit):
547
545
  gate = circuit
548
546
 
549
- # Basis Clifford Gates
550
- basis_1q = {
551
- "i": _evolve_i,
552
- "id": _evolve_i,
553
- "iden": _evolve_i,
554
- "x": _evolve_x,
555
- "y": _evolve_y,
556
- "z": _evolve_z,
557
- "h": _evolve_h,
558
- "s": _evolve_s,
559
- "sdg": _evolve_sdg,
560
- "sinv": _evolve_sdg,
561
- }
562
- basis_2q = {"cx": _evolve_cx, "cz": _evolve_cz, "cy": _evolve_cy, "swap": _evolve_swap}
563
-
564
- # Non-Clifford gates
565
- non_clifford = ["t", "tdg", "ccx", "ccz"]
566
-
567
- if isinstance(gate, str):
568
- # Check if gate is a valid Clifford basis gate string
569
- if gate not in basis_1q and gate not in basis_2q:
570
- raise QiskitError(f"Invalid Clifford gate name string {gate}")
571
- name = gate
572
- else:
573
- # Assume gate is an Instruction
574
- name = gate.name
575
-
576
- # Apply gate if it is a Clifford basis gate
577
- if name in non_clifford:
578
- raise QiskitError(f"Cannot update Pauli with non-Clifford gate {name}")
579
- if name in basis_1q:
580
- if len(qargs) != 1:
581
- raise QiskitError("Invalid qubits for 1-qubit gate.")
582
- return basis_1q[name](self, qargs[0])
583
- if name in basis_2q:
584
- if len(qargs) != 2:
585
- raise QiskitError("Invalid qubits for 2-qubit gate.")
586
- return basis_2q[name](self, qargs[0], qargs[1])
587
-
588
- # If not a Clifford basis gate we try to unroll the gate and
589
- # raise an exception if unrolling reaches a non-Clifford gate.
590
- if gate.definition is None:
591
- raise QiskitError(f"Cannot apply Instruction: {gate.name}")
592
- if not isinstance(gate.definition, QuantumCircuit):
593
- raise QiskitError(
594
- "{} instruction definition is {}; expected QuantumCircuit".format(
595
- gate.name, type(gate.definition)
547
+ if isinstance(gate, str):
548
+ # Check if gate is a valid Clifford basis gate string
549
+ if gate not in _basis_1q and gate not in _basis_2q:
550
+ raise QiskitError(f"Invalid Clifford gate name string {gate}")
551
+ name = gate
552
+ else:
553
+ # Assume gate is an Instruction
554
+ name = gate.name
555
+
556
+ # Apply gate if it is a Clifford basis gate
557
+ if name in _non_clifford:
558
+ raise QiskitError(f"Cannot update Pauli with non-Clifford gate {name}")
559
+ if name in _basis_1q:
560
+ if len(qargs) != 1:
561
+ raise QiskitError("Invalid qubits for 1-qubit gate.")
562
+ return _basis_1q[name](self, qargs[0])
563
+ if name in _basis_2q:
564
+ if len(qargs) != 2:
565
+ raise QiskitError("Invalid qubits for 2-qubit gate.")
566
+ return _basis_2q[name](self, qargs[0], qargs[1])
567
+
568
+ # If not a Clifford basis gate we try to unroll the gate and
569
+ # raise an exception if unrolling reaches a non-Clifford gate.
570
+ if gate.definition is None:
571
+ raise QiskitError(f"Cannot apply Instruction: {gate.name}")
572
+ if not isinstance(gate.definition, QuantumCircuit):
573
+ raise QiskitError(
574
+ "{} instruction definition is {}; expected QuantumCircuit".format(
575
+ gate.name, type(gate.definition)
576
+ )
596
577
  )
597
- )
598
578
 
599
- flat_instr = gate.definition
600
- bit_indices = {
601
- bit: index
602
- for bits in [flat_instr.qubits, flat_instr.clbits]
603
- for index, bit in enumerate(bits)
604
- }
579
+ circuit = gate.definition
605
580
 
606
- for instruction in flat_instr:
581
+ for instruction in circuit:
607
582
  if instruction.clbits:
608
583
  raise QiskitError(
609
584
  f"Cannot apply Instruction with classical bits: {instruction.operation.name}"
610
585
  )
611
586
  # Get the integer position of the flat register
612
- new_qubits = [qargs[bit_indices[tup]] for tup in instruction.qubits]
587
+ new_qubits = [qargs[circuit.find_bit(qb)[0]] for qb in instruction.qubits]
613
588
  self._append_circuit(instruction.operation, new_qubits)
614
589
 
615
590
  # Since the individual gate evolution functions don't take mod
@@ -715,6 +690,42 @@ def _evolve_swap(base_pauli, q1, q2):
715
690
  return base_pauli
716
691
 
717
692
 
693
+ def _evolve_ecr(base_pauli, q1, q2):
694
+ """Update P -> ECR.P.ECR"""
695
+ base_pauli = _evolve_s(base_pauli, q1)
696
+ base_pauli = _evolve_h(base_pauli, q2)
697
+ base_pauli = _evolve_s(base_pauli, q2)
698
+ base_pauli = _evolve_h(base_pauli, q2)
699
+ base_pauli = _evolve_cx(base_pauli, q1, q2)
700
+ base_pauli = _evolve_x(base_pauli, q1)
701
+ return base_pauli
702
+
703
+
718
704
  def _count_y(x, z, dtype=None):
719
705
  """Count the number of I Paulis"""
720
706
  return (x & z).sum(axis=1, dtype=dtype)
707
+
708
+
709
+ # Basis Clifford Gates
710
+ _basis_1q = {
711
+ "i": _evolve_i,
712
+ "id": _evolve_i,
713
+ "iden": _evolve_i,
714
+ "x": _evolve_x,
715
+ "y": _evolve_y,
716
+ "z": _evolve_z,
717
+ "h": _evolve_h,
718
+ "s": _evolve_s,
719
+ "sdg": _evolve_sdg,
720
+ "sinv": _evolve_sdg,
721
+ }
722
+ _basis_2q = {
723
+ "cx": _evolve_cx,
724
+ "cz": _evolve_cz,
725
+ "cy": _evolve_cy,
726
+ "swap": _evolve_swap,
727
+ "ecr": _evolve_ecr,
728
+ }
729
+
730
+ # Non-Clifford gates
731
+ _non_clifford = ["t", "tdg", "ccx", "ccz"]
@@ -16,6 +16,7 @@ from __future__ import annotations
16
16
 
17
17
  import functools
18
18
  import itertools
19
+ import math
19
20
  import re
20
21
  from typing import Literal
21
22
 
@@ -36,7 +37,23 @@ from .clifford_circuits import _append_circuit, _append_operation
36
37
 
37
38
 
38
39
  class Clifford(BaseOperator, AdjointMixin, Operation):
39
- """An N-qubit unitary operator from the Clifford group.
40
+ r"""
41
+ An N-qubit unitary operator from the Clifford group.
42
+
43
+ An N-qubit Clifford operator takes Paulis to Paulis via conjugation
44
+ (up to a global phase). More precisely, the Clifford group :math:`\mathcal{C}_N`
45
+ is defined as
46
+
47
+ .. math::
48
+
49
+ \mathcal{C}_N = \{ U \in U(2^N) | U \mathcal{P}_N U^{\dagger} = \mathcal{P}_N \} / U(1)
50
+
51
+ where :math:`\mathcal{P}_N` is the Pauli group on :math:`N` qubits
52
+ that is generated by single-qubit Pauli operators,
53
+ and :math:`U` is a unitary operator in the unitary group
54
+ :math:`U(2^N)` representing operations on :math:`N` qubits.
55
+ :math:`\mathcal{C}_N` is the quotient group by the subgroup of
56
+ scalar unitary matrices :math:`U(1)`.
40
57
 
41
58
  **Representation**
42
59
 
@@ -90,7 +107,7 @@ class Clifford(BaseOperator, AdjointMixin, Operation):
90
107
  :class:`~qiskit.circuit.library.SGate`, :class:`~qiskit.circuit.library.SdgGate`,
91
108
  :class:`~qiskit.circuit.library.SXGate`, :class:`~qiskit.circuit.library.SXdgGate`,
92
109
  :class:`~qiskit.circuit.library.CXGate`, :class:`~qiskit.circuit.library.CZGate`,
93
- :class:`~qiskit.circuit.library.CYGate`, :class:`~qiskit.circuit.library.DXGate`,
110
+ :class:`~qiskit.circuit.library.CYGate`, :class:`~qiskit.circuit.library.DCXGate`,
94
111
  :class:`~qiskit.circuit.library.SwapGate`, :class:`~qiskit.circuit.library.iSwapGate`,
95
112
  :class:`~qiskit.circuit.library.ECRGate`, :class:`~qiskit.circuit.library.LinearFunction`,
96
113
  :class:`~qiskit.circuit.library.PermutationGate`.
@@ -121,10 +138,11 @@ class Clifford(BaseOperator, AdjointMixin, Operation):
121
138
  _COMPOSE_PHASE_LOOKUP = None
122
139
  _COMPOSE_1Q_LOOKUP = None
123
140
 
124
- def __array__(self, dtype=None):
125
- if dtype:
126
- return np.asarray(self.to_matrix(), dtype=dtype)
127
- return self.to_matrix()
141
+ def __array__(self, dtype=None, copy=None):
142
+ if copy is False:
143
+ raise ValueError("unable to avoid copy while creating an array as requested")
144
+ arr = self.to_matrix()
145
+ return arr if dtype is None else arr.astype(dtype, copy=False)
128
146
 
129
147
  def __init__(self, data, validate=True, copy=True):
130
148
  """Initialize an operator object."""
@@ -163,8 +181,17 @@ class Clifford(BaseOperator, AdjointMixin, Operation):
163
181
 
164
182
  # Initialize StabilizerTable directly from the data
165
183
  else:
166
- if isinstance(data, (list, np.ndarray)) and np.asarray(data, dtype=bool).ndim == 2:
167
- data = np.array(data, dtype=bool, copy=copy)
184
+ if (
185
+ isinstance(data, (list, np.ndarray))
186
+ and (data_asarray := np.asarray(data, dtype=bool)).ndim == 2
187
+ ):
188
+ # This little dance is to avoid Numpy 1/2 incompatiblities between the availability
189
+ # and meaning of the 'copy' argument in 'array' and 'asarray', when the input needs
190
+ # its dtype converting. 'asarray' prefers to return 'self' if possible in both.
191
+ if copy and np.may_share_memory(data, data_asarray):
192
+ data = data_asarray.copy()
193
+ else:
194
+ data = data_asarray
168
195
  if data.shape[0] == data.shape[1]:
169
196
  self.tableau = self._stack_table_phase(
170
197
  data, np.zeros(data.shape[0], dtype=bool)
@@ -973,7 +1000,7 @@ class Clifford(BaseOperator, AdjointMixin, Operation):
973
1000
  @staticmethod
974
1001
  def _unitary_matrix_to_tableau(matrix):
975
1002
  # pylint: disable=invalid-name
976
- num_qubits = int(np.log2(len(matrix)))
1003
+ num_qubits = int(math.log2(len(matrix)))
977
1004
 
978
1005
  stab = np.empty((num_qubits, 2 * num_qubits + 1), dtype=bool)
979
1006
  for i in range(num_qubits):
@@ -33,6 +33,7 @@ from qiskit.quantum_info.operators.symplectic.base_pauli import BasePauli, _coun
33
33
  if TYPE_CHECKING:
34
34
  from qiskit.quantum_info.operators.symplectic.clifford import Clifford
35
35
  from qiskit.quantum_info.operators.symplectic.pauli_list import PauliList
36
+ from qiskit.transpiler.layout import TranspileLayout
36
37
 
37
38
 
38
39
  class Pauli(BasePauli):
@@ -221,10 +222,11 @@ class Pauli(BasePauli):
221
222
  return front + "..."
222
223
  return self.to_label()
223
224
 
224
- def __array__(self, dtype=None):
225
- if dtype:
226
- return np.asarray(self.to_matrix(), dtype=dtype)
227
- return self.to_matrix()
225
+ def __array__(self, dtype=None, copy=None):
226
+ if copy is False:
227
+ raise ValueError("unable to avoid copy while creating an array as requested")
228
+ arr = self.to_matrix()
229
+ return arr if dtype is None else arr.astype(dtype, copy=False)
228
230
 
229
231
  @classmethod
230
232
  def set_truncation(cls, val: int):
@@ -697,6 +699,48 @@ class Pauli(BasePauli):
697
699
  ret = ret.compose(next_instr, qargs=qargs)
698
700
  return ret._z, ret._x, ret._phase
699
701
 
702
+ def apply_layout(
703
+ self, layout: TranspileLayout | list[int] | None, num_qubits: int | None = None
704
+ ) -> Pauli:
705
+ """Apply a transpiler layout to this :class:`~.Pauli`
706
+
707
+ Args:
708
+ layout: Either a :class:`~.TranspileLayout`, a list of integers or None.
709
+ If both layout and num_qubits are none, a copy of the operator is
710
+ returned.
711
+ num_qubits: The number of qubits to expand the operator to. If not
712
+ provided then if ``layout`` is a :class:`~.TranspileLayout` the
713
+ number of the transpiler output circuit qubits will be used by
714
+ default. If ``layout`` is a list of integers the permutation
715
+ specified will be applied without any expansion. If layout is
716
+ None, the operator will be expanded to the given number of qubits.
717
+
718
+ Returns:
719
+ A new :class:`.Pauli` with the provided layout applied
720
+ """
721
+ from qiskit.transpiler.layout import TranspileLayout
722
+
723
+ if layout is None and num_qubits is None:
724
+ return self.copy()
725
+
726
+ n_qubits = self.num_qubits
727
+ if isinstance(layout, TranspileLayout):
728
+ n_qubits = len(layout._output_qubit_list)
729
+ layout = layout.final_index_layout()
730
+ if num_qubits is not None:
731
+ if num_qubits < n_qubits:
732
+ raise QiskitError(
733
+ f"The input num_qubits is too small, a {num_qubits} qubit layout cannot be "
734
+ f"applied to a {n_qubits} qubit operator"
735
+ )
736
+ n_qubits = num_qubits
737
+ if layout is None:
738
+ layout = list(range(self.num_qubits))
739
+ elif any(x >= n_qubits for x in layout):
740
+ raise QiskitError("Provided layout contains indices outside the number of qubits.")
741
+ new_op = type(self)("I" * n_qubits)
742
+ return new_op.compose(self, qargs=layout)
743
+
700
744
 
701
745
  # Update docstrings for API docs
702
746
  generate_apidocs(Pauli)
@@ -148,14 +148,15 @@ class PauliList(BasePauli, LinearMixin, GroupMixin):
148
148
  """Return settings."""
149
149
  return {"data": self.to_labels()}
150
150
 
151
- def __array__(self, dtype=None):
151
+ def __array__(self, dtype=None, copy=None):
152
152
  """Convert to numpy array"""
153
- # pylint: disable=unused-argument
153
+ if copy is False:
154
+ raise ValueError("cannot provide a matrix without calculation")
154
155
  shape = (len(self),) + 2 * (2**self.num_qubits,)
155
156
  ret = np.zeros(shape, dtype=complex)
156
157
  for i, mat in enumerate(self.matrix_iter()):
157
158
  ret[i] = mat
158
- return ret
159
+ return ret if dtype is None else ret.astype(dtype, copy=False)
159
160
 
160
161
  @staticmethod
161
162
  def _from_paulis(data):
@@ -1155,23 +1156,50 @@ class PauliList(BasePauli, LinearMixin, GroupMixin):
1155
1156
  # results from one triangle to avoid symmetric duplications.
1156
1157
  return list(zip(*np.where(np.triu(adjacency_mat, k=1))))
1157
1158
 
1158
- def _create_graph(self, qubit_wise):
1159
- """Transform measurement operator grouping problem into graph coloring problem
1159
+ def noncommutation_graph(self, qubit_wise: bool) -> rx.PyGraph:
1160
+ """Create the non-commutation graph of this PauliList.
1161
+
1162
+ This transforms the measurement operator grouping problem into graph coloring problem. The
1163
+ constructed graph contains one node for each Pauli. The nodes will be connecting for any two
1164
+ Pauli terms that do _not_ commute.
1160
1165
 
1161
1166
  Args:
1162
1167
  qubit_wise (bool): whether the commutation rule is applied to the whole operator,
1163
1168
  or on a per-qubit basis.
1164
1169
 
1165
1170
  Returns:
1166
- rustworkx.PyGraph: A class of undirected graphs
1171
+ rustworkx.PyGraph: the non-commutation graph with nodes for each Pauli and edges
1172
+ indicating a non-commutation relation. Each node will hold the index of the Pauli
1173
+ term it corresponds to in its data. The edges of the graph hold no data.
1167
1174
  """
1168
-
1169
1175
  edges = self._noncommutation_graph(qubit_wise)
1170
1176
  graph = rx.PyGraph()
1171
1177
  graph.add_nodes_from(range(self.size))
1172
1178
  graph.add_edges_from_no_data(edges)
1173
1179
  return graph
1174
1180
 
1181
+ def _commuting_groups(self, qubit_wise: bool) -> dict[int, list[int]]:
1182
+ """Partition a PauliList into sets of commuting Pauli strings.
1183
+
1184
+ This is the internal logic of the public ``PauliList.group_commuting`` method which returns
1185
+ a mapping of colors to Pauli indices. The same logic is re-used by
1186
+ ``SparsePauliOp.group_commuting``.
1187
+
1188
+ Args:
1189
+ qubit_wise (bool): whether the commutation rule is applied to the whole operator,
1190
+ or on a per-qubit basis.
1191
+
1192
+ Returns:
1193
+ dict[int, list[int]]: Dictionary of color indices mapping to a list of Pauli indices.
1194
+ """
1195
+ graph = self.noncommutation_graph(qubit_wise)
1196
+ # Keys in coloring_dict are nodes, values are colors
1197
+ coloring_dict = rx.graph_greedy_color(graph)
1198
+ groups = defaultdict(list)
1199
+ for idx, color in coloring_dict.items():
1200
+ groups[color].append(idx)
1201
+ return groups
1202
+
1175
1203
  def group_qubit_wise_commuting(self) -> list[PauliList]:
1176
1204
  """Partition a PauliList into sets of mutually qubit-wise commuting Pauli strings.
1177
1205
 
@@ -1199,11 +1227,5 @@ class PauliList(BasePauli, LinearMixin, GroupMixin):
1199
1227
  Returns:
1200
1228
  list[PauliList]: List of PauliLists where each PauliList contains commuting Pauli operators.
1201
1229
  """
1202
-
1203
- graph = self._create_graph(qubit_wise)
1204
- # Keys in coloring_dict are nodes, values are colors
1205
- coloring_dict = rx.graph_greedy_color(graph)
1206
- groups = defaultdict(list)
1207
- for idx, color in coloring_dict.items():
1208
- groups[color].append(idx)
1230
+ groups = self._commuting_groups(qubit_wise)
1209
1231
  return [self[group] for group in groups.values()]
@@ -14,6 +14,7 @@ Random symplectic operator functions
14
14
  """
15
15
 
16
16
  from __future__ import annotations
17
+ import math
17
18
 
18
19
  import numpy as np
19
20
  from numpy.random import default_rng
@@ -80,7 +81,7 @@ def random_pauli_list(
80
81
  z = rng.integers(2, size=(size, num_qubits)).astype(bool)
81
82
  x = rng.integers(2, size=(size, num_qubits)).astype(bool)
82
83
  if phase:
83
- _phase = rng.integers(4, size=(size))
84
+ _phase = rng.integers(4, size=size)
84
85
  return PauliList.from_symplectic(z, x, _phase)
85
86
  return PauliList.from_symplectic(z, x)
86
87
 
@@ -173,7 +174,7 @@ def _sample_qmallows(n, rng=None):
173
174
  m = n - i
174
175
  eps = 4 ** (-m)
175
176
  r = rng.uniform(0, 1)
176
- index = -int(np.ceil(np.log2(r + (1 - r) * eps)))
177
+ index = -math.ceil(math.log2(r + (1 - r) * eps))
177
178
  had[i] = index < m
178
179
  if index < m:
179
180
  k = index
@@ -16,7 +16,6 @@ N-Qubit Sparse Pauli Operator class.
16
16
  from __future__ import annotations
17
17
  from typing import TYPE_CHECKING, List
18
18
 
19
- from collections import defaultdict
20
19
  from collections.abc import Mapping, Sequence, Iterable
21
20
  from numbers import Number
22
21
  from copy import deepcopy
@@ -24,7 +23,13 @@ from copy import deepcopy
24
23
  import numpy as np
25
24
  import rustworkx as rx
26
25
 
27
- from qiskit._accelerate.sparse_pauli_op import unordered_unique, decompose_dense
26
+ from qiskit._accelerate.sparse_pauli_op import (
27
+ ZXPaulis,
28
+ decompose_dense,
29
+ to_matrix_dense,
30
+ to_matrix_sparse,
31
+ unordered_unique,
32
+ )
28
33
  from qiskit.circuit.parameter import Parameter
29
34
  from qiskit.circuit.parameterexpression import ParameterExpression
30
35
  from qiskit.circuit.parametertable import ParameterView
@@ -143,7 +148,12 @@ class SparsePauliOp(LinearOp):
143
148
  if coeffs is None:
144
149
  coeffs = np.ones(pauli_list.size, dtype=complex)
145
150
  else:
146
- coeffs = np.array(coeffs, copy=copy, dtype=dtype)
151
+ coeffs_asarray = np.asarray(coeffs, dtype=dtype)
152
+ coeffs = (
153
+ coeffs_asarray.copy()
154
+ if copy and np.may_share_memory(coeffs, coeffs_asarray)
155
+ else coeffs_asarray
156
+ )
147
157
 
148
158
  if ignore_pauli_phase:
149
159
  # Fast path used in copy operations, where the phase of the PauliList is already known
@@ -167,10 +177,11 @@ class SparsePauliOp(LinearOp):
167
177
  # Initialize LinearOp
168
178
  super().__init__(num_qubits=self._pauli_list.num_qubits)
169
179
 
170
- def __array__(self, dtype=None):
171
- if dtype:
172
- return np.asarray(self.to_matrix(), dtype=dtype)
173
- return self.to_matrix()
180
+ def __array__(self, dtype=None, copy=None):
181
+ if copy is False:
182
+ raise ValueError("unable to avoid copy while creating an array as requested")
183
+ arr = self.to_matrix()
184
+ return arr if dtype is None else arr.astype(dtype, copy=False)
174
185
 
175
186
  def __repr__(self):
176
187
  prefix = "SparsePauliOp("
@@ -920,24 +931,39 @@ class SparsePauliOp(LinearOp):
920
931
  return labels
921
932
  return labels.tolist()
922
933
 
923
- def to_matrix(self, sparse: bool = False) -> np.ndarray:
934
+ def to_matrix(self, sparse: bool = False, force_serial: bool = False) -> np.ndarray:
924
935
  """Convert to a dense or sparse matrix.
925
936
 
926
937
  Args:
927
- sparse (bool): if True return a sparse CSR matrix, otherwise
928
- return dense Numpy array (Default: False).
938
+ sparse: if ``True`` return a sparse CSR matrix, otherwise return dense Numpy
939
+ array (the default).
940
+ force_serial: if ``True``, use an unthreaded implementation, regardless of the state of
941
+ the `Qiskit threading-control environment variables
942
+ <https://docs.quantum.ibm.com/start/configure-qiskit-local#environment-variables>`__.
943
+ By default, this will use threaded parallelism over the available CPUs.
929
944
 
930
945
  Returns:
931
946
  array: A dense matrix if `sparse=False`.
932
947
  csr_matrix: A sparse matrix in CSR format if `sparse=True`.
933
948
  """
934
- mat = None
935
- for i in self.matrix_iter(sparse=sparse):
936
- if mat is None:
937
- mat = i
938
- else:
939
- mat += i
940
- return mat
949
+ if self.coeffs.dtype == object:
950
+ # Fallback to slow Python-space method.
951
+ return sum(self.matrix_iter(sparse=sparse))
952
+
953
+ pauli_list = self.paulis
954
+ zx = ZXPaulis(
955
+ pauli_list.x.astype(np.bool_),
956
+ pauli_list.z.astype(np.bool_),
957
+ pauli_list.phase.astype(np.uint8),
958
+ self.coeffs.astype(np.complex128),
959
+ )
960
+ if sparse:
961
+ from scipy.sparse import csr_matrix
962
+
963
+ data, indices, indptr = to_matrix_sparse(zx, force_serial=force_serial)
964
+ side = 1 << self.num_qubits
965
+ return csr_matrix((data, indices, indptr), shape=(side, side))
966
+ return to_matrix_dense(zx, force_serial=force_serial)
941
967
 
942
968
  def to_operator(self) -> Operator:
943
969
  """Convert to a matrix Operator object"""
@@ -1000,22 +1026,23 @@ class SparsePauliOp(LinearOp):
1000
1026
 
1001
1027
  return MatrixIterator(self)
1002
1028
 
1003
- def _create_graph(self, qubit_wise):
1004
- """Transform measurement operator grouping problem into graph coloring problem
1029
+ def noncommutation_graph(self, qubit_wise: bool) -> rx.PyGraph:
1030
+ """Create the non-commutation graph of this SparsePauliOp.
1031
+
1032
+ This transforms the measurement operator grouping problem into graph coloring problem. The
1033
+ constructed graph contains one node for each Pauli. The nodes will be connecting for any two
1034
+ Pauli terms that do _not_ commute.
1005
1035
 
1006
1036
  Args:
1007
1037
  qubit_wise (bool): whether the commutation rule is applied to the whole operator,
1008
1038
  or on a per-qubit basis.
1009
1039
 
1010
1040
  Returns:
1011
- rustworkx.PyGraph: A class of undirected graphs
1041
+ rustworkx.PyGraph: the non-commutation graph with nodes for each Pauli and edges
1042
+ indicating a non-commutation relation. Each node will hold the index of the Pauli
1043
+ term it corresponds to in its data. The edges of the graph hold no data.
1012
1044
  """
1013
-
1014
- edges = self.paulis._noncommutation_graph(qubit_wise)
1015
- graph = rx.PyGraph()
1016
- graph.add_nodes_from(range(self.size))
1017
- graph.add_edges_from_no_data(edges)
1018
- return graph
1045
+ return self.paulis.noncommutation_graph(qubit_wise)
1019
1046
 
1020
1047
  def group_commuting(self, qubit_wise: bool = False) -> list[SparsePauliOp]:
1021
1048
  """Partition a SparsePauliOp into sets of commuting Pauli strings.
@@ -1039,13 +1066,7 @@ class SparsePauliOp(LinearOp):
1039
1066
  list[SparsePauliOp]: List of SparsePauliOp where each SparsePauliOp contains
1040
1067
  commuting Pauli operators.
1041
1068
  """
1042
-
1043
- graph = self._create_graph(qubit_wise)
1044
- # Keys in coloring_dict are nodes, values are colors
1045
- coloring_dict = rx.graph_greedy_color(graph)
1046
- groups = defaultdict(list)
1047
- for idx, color in coloring_dict.items():
1048
- groups[color].append(idx)
1069
+ groups = self.paulis._commuting_groups(qubit_wise)
1049
1070
  return [self[group] for group in groups.values()]
1050
1071
 
1051
1072
  @property