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
@@ -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):
@@ -143,13 +144,13 @@ class Pauli(BasePauli):
143
144
 
144
145
  .. code-block:: python
145
146
 
146
- p = Pauli('-iXYZ')
147
+ P = Pauli('-iXYZ')
147
148
 
148
149
  print('P[0] =', repr(P[0]))
149
150
  print('P[1] =', repr(P[1]))
150
151
  print('P[2] =', repr(P[2]))
151
152
  print('P[:] =', repr(P[:]))
152
- print('P[::-1] =, repr(P[::-1]))
153
+ print('P[::-1] =', repr(P[::-1]))
153
154
  """
154
155
 
155
156
  # Set the max Pauli string size before truncation
@@ -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,51 @@ 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
+ else:
740
+ if any(x < 0 or x >= n_qubits for x in layout):
741
+ raise QiskitError("Provided layout contains indices outside the number of qubits.")
742
+ if len(set(layout)) != len(layout):
743
+ raise QiskitError("Provided layout contains duplicate indices.")
744
+ new_op = type(self)("I" * n_qubits)
745
+ return new_op.compose(self, qargs=layout)
746
+
700
747
 
701
748
  # Update docstrings for API docs
702
749
  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