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
@@ -13,10 +13,12 @@
13
13
  """Arbitrary unitary circuit instruction."""
14
14
 
15
15
  from __future__ import annotations
16
+ import math
16
17
 
17
18
  import typing
18
19
  import numpy
19
20
 
21
+ from qiskit import _numpy_compat
20
22
  from qiskit.circuit.gate import Gate
21
23
  from qiskit.circuit.controlledgate import ControlledGate
22
24
  from qiskit.circuit.annotated_operation import AnnotatedOperation, ControlModifier
@@ -69,6 +71,8 @@ class UnitaryGate(Gate):
69
71
  data: numpy.ndarray | Gate | BaseOperator,
70
72
  label: str | None = None,
71
73
  check_input: bool = True,
74
+ *,
75
+ num_qubits: int | None = None,
72
76
  ) -> None:
73
77
  """Create a gate from a numeric unitary matrix.
74
78
 
@@ -80,6 +84,7 @@ class UnitaryGate(Gate):
80
84
  be skipped. This should only ever be used if you know the
81
85
  input is unitary, setting this to ``False`` and passing in
82
86
  a non-unitary matrix will result unexpected behavior and errors.
87
+ num_qubits: If given, the number of qubits in the matrix. If not given, it is inferred.
83
88
 
84
89
  Raises:
85
90
  ValueError: If input data is not an N-qubit unitary operator.
@@ -96,7 +101,7 @@ class UnitaryGate(Gate):
96
101
  # Convert to numpy array in case not already an array
97
102
  data = numpy.asarray(data, dtype=complex)
98
103
  input_dim, output_dim = data.shape
99
- num_qubits = int(numpy.log2(input_dim))
104
+ num_qubits = num_qubits if num_qubits is not None else int(math.log2(input_dim))
100
105
  if check_input:
101
106
  # Check input is unitary
102
107
  if not is_unitary_matrix(data):
@@ -114,10 +119,10 @@ class UnitaryGate(Gate):
114
119
  return False
115
120
  return matrix_equal(self.params[0], other.params[0])
116
121
 
117
- def __array__(self, dtype=None):
122
+ def __array__(self, dtype=None, copy=_numpy_compat.COPY_ONLY_IF_NEEDED):
118
123
  """Return matrix for the unitary."""
119
- # pylint: disable=unused-argument
120
- return self.params[0]
124
+ dtype = self.params[0].dtype if dtype is None else dtype
125
+ return numpy.array(self.params[0], dtype=dtype, copy=copy)
121
126
 
122
127
  def inverse(self, annotated: bool = False):
123
128
  """Return the adjoint of the unitary."""
@@ -15,11 +15,13 @@ Gate described by the time evolution of a Hermitian Hamiltonian operator.
15
15
  """
16
16
 
17
17
  from __future__ import annotations
18
+ import math
18
19
  import typing
19
20
 
20
21
  from numbers import Number
21
22
  import numpy as np
22
23
 
24
+ from qiskit import _numpy_compat
23
25
  from qiskit.circuit.gate import Gate
24
26
  from qiskit.circuit.quantumcircuit import QuantumCircuit
25
27
  from qiskit.circuit.quantumregister import QuantumRegister
@@ -67,7 +69,7 @@ class HamiltonianGate(Gate):
67
69
  # numpy matrix from `Operator.data`.
68
70
  data = data.to_operator().data
69
71
  # Convert to np array in case not already an array
70
- data = np.array(data, dtype=complex)
72
+ data = np.asarray(data, dtype=complex)
71
73
  # Check input is unitary
72
74
  if not is_hermitian_matrix(data):
73
75
  raise ValueError("Input matrix is not Hermitian.")
@@ -75,7 +77,7 @@ class HamiltonianGate(Gate):
75
77
  raise ValueError("Evolution time is not real.")
76
78
  # Check input is N-qubit matrix
77
79
  input_dim, output_dim = data.shape
78
- num_qubits = int(np.log2(input_dim))
80
+ num_qubits = int(math.log2(input_dim))
79
81
  if input_dim != output_dim or 2**num_qubits != input_dim:
80
82
  raise ValueError("Input matrix is not an N-qubit operator.")
81
83
 
@@ -91,18 +93,22 @@ class HamiltonianGate(Gate):
91
93
  times_eq = self.params[1] == other.params[1]
92
94
  return operators_eq and times_eq
93
95
 
94
- def __array__(self, dtype=None):
96
+ def __array__(self, dtype=None, copy=None):
95
97
  """Return matrix for the unitary."""
96
- # pylint: disable=unused-argument
97
98
  import scipy.linalg
98
99
 
100
+ if copy is False:
101
+ raise ValueError("unable to avoid copy while creating an array as requested")
99
102
  try:
100
- return scipy.linalg.expm(-1j * self.params[0] * float(self.params[1]))
103
+ time = float(self.params[1])
101
104
  except TypeError as ex:
102
105
  raise TypeError(
103
106
  "Unable to generate Unitary matrix for "
104
107
  "unbound t parameter {}".format(self.params[1])
105
108
  ) from ex
109
+ arr = scipy.linalg.expm(-1j * self.params[0] * time)
110
+ dtype = complex if dtype is None else dtype
111
+ return np.array(arr, dtype=dtype, copy=_numpy_compat.COPY_ONLY_IF_NEEDED)
106
112
 
107
113
  def inverse(self, annotated: bool = False):
108
114
  """Return the adjoint of the unitary."""
@@ -110,11 +110,11 @@ class EfficientSU2(TwoLocal):
110
110
  If only one gate is provided, the same gate is applied to each qubit.
111
111
  If a list of gates is provided, all gates are applied to each qubit in the provided
112
112
  order.
113
- entanglement: Specifies the entanglement structure. Can be a string ('full', 'linear'
114
- , 'reverse_linear', 'circular' or 'sca'), a list of integer-pairs specifying the indices
115
- of qubits entangled with one another, or a callable returning such a list provided with
116
- the index of the entanglement layer.
117
- Default to 'reverse_linear' entanglement.
113
+ entanglement: Specifies the entanglement structure. Can be a string
114
+ ('full', 'linear', 'reverse_linear', 'pairwise', 'circular', or 'sca'),
115
+ a list of integer-pairs specifying the indices of qubits entangled with one another,
116
+ or a callable returning such a list provided with the index of the entanglement layer.
117
+ Defaults to 'reverse_linear' entanglement.
118
118
  Note that 'reverse_linear' entanglement provides the same unitary as 'full'
119
119
  with fewer entangling gates.
120
120
  See the Examples section of :class:`~qiskit.circuit.library.TwoLocal` for more
@@ -13,16 +13,24 @@
13
13
  """The n-local circuit class."""
14
14
 
15
15
  from __future__ import annotations
16
+
17
+ import collections
18
+ import itertools
16
19
  import typing
17
20
  from collections.abc import Callable, Mapping, Sequence
18
21
 
19
- from itertools import combinations
20
-
21
22
  import numpy
22
23
  from qiskit.circuit.quantumcircuit import QuantumCircuit
23
24
  from qiskit.circuit.quantumregister import QuantumRegister
24
- from qiskit.circuit import Instruction, Parameter, ParameterVector, ParameterExpression
25
+ from qiskit.circuit import (
26
+ Instruction,
27
+ Parameter,
28
+ ParameterVector,
29
+ ParameterExpression,
30
+ CircuitInstruction,
31
+ )
25
32
  from qiskit.exceptions import QiskitError
33
+ from qiskit.circuit.library.standard_gates import get_standard_gate_name_mapping
26
34
 
27
35
  from ..blueprintcircuit import BlueprintCircuit
28
36
 
@@ -154,6 +162,17 @@ class NLocal(BlueprintCircuit):
154
162
  self._bounds: list[tuple[float | None, float | None]] | None = None
155
163
  self._flatten = flatten
156
164
 
165
+ # During the build, if a subclass hasn't overridden our parametrisation methods, we can use
166
+ # a newer fast-path method to parametrise the rotation and entanglement blocks if internally
167
+ # those are just simple stdlib gates that have been promoted to circuits. We don't
168
+ # precalculate the fast-path layers themselves because there's far too much that can be
169
+ # overridden between object construction and build, and far too many subclasses of `NLocal`
170
+ # that override bits and bobs of the internal private methods, so it'd be too hard to keep
171
+ # everything in sync.
172
+ self._allow_fast_path_parametrization = (
173
+ getattr(self._parameter_generator, "__func__", None) is NLocal._parameter_generator
174
+ )
175
+
157
176
  if int(reps) != reps:
158
177
  raise TypeError("The value of reps should be int")
159
178
 
@@ -779,13 +798,10 @@ class NLocal(BlueprintCircuit):
779
798
  else:
780
799
  entangler_map = entanglement
781
800
 
782
- layer = QuantumCircuit(self.num_qubits)
783
801
  for i in entangler_map:
784
802
  params = self.ordered_parameters[-len(get_parameters(block)) :]
785
803
  parameterized_block = self._parameterize_block(block, params=params)
786
- layer.compose(parameterized_block, i, inplace=True)
787
-
788
- self.compose(layer, inplace=True)
804
+ self.compose(parameterized_block, i, inplace=True, copy=False)
789
805
  else:
790
806
  # cannot prepend a block currently, just rebuild
791
807
  self._invalidate()
@@ -843,52 +859,65 @@ class NLocal(BlueprintCircuit):
843
859
  """Build a rotation layer."""
844
860
  # if the unentangled qubits are skipped, compute the set of qubits that are not entangled
845
861
  if self._skip_unentangled_qubits:
846
- unentangled_qubits = self.get_unentangled_qubits()
862
+ skipped_qubits = self.get_unentangled_qubits()
863
+ else:
864
+ skipped_qubits = set()
865
+
866
+ target_qubits = circuit.qubits
847
867
 
848
868
  # iterate over all rotation blocks
849
869
  for j, block in enumerate(self.rotation_blocks):
850
- # create a new layer
851
- layer = QuantumCircuit(*self.qregs)
852
-
853
- # we apply the rotation gates stacked on top of each other, i.e.
854
- # if we have 4 qubits and a rotation block of width 2, we apply two instances
855
- block_indices = [
856
- list(range(k * block.num_qubits, (k + 1) * block.num_qubits))
857
- for k in range(self.num_qubits // block.num_qubits)
858
- ]
859
-
860
- # if unentangled qubits should not be acted on, remove all operations that
861
- # touch an unentangled qubit
862
- if self._skip_unentangled_qubits:
870
+ skipped_blocks = {qubit // block.num_qubits for qubit in skipped_qubits}
871
+ if (
872
+ self._allow_fast_path_parametrization
873
+ and (simple_block := _stdlib_gate_from_simple_block(block)) is not None
874
+ ):
875
+ all_qubits = (
876
+ tuple(target_qubits[k * block.num_qubits : (k + 1) * block.num_qubits])
877
+ for k in range(self.num_qubits // block.num_qubits)
878
+ if k not in skipped_blocks
879
+ )
880
+ for qubits in all_qubits:
881
+ instr = CircuitInstruction(
882
+ simple_block.gate(*itertools.islice(param_iter, simple_block.num_params)),
883
+ qubits,
884
+ )
885
+ circuit._append(instr)
886
+ else:
863
887
  block_indices = [
864
- indices
865
- for indices in block_indices
866
- if set(indices).isdisjoint(unentangled_qubits)
888
+ list(range(k * block.num_qubits, (k + 1) * block.num_qubits))
889
+ for k in range(self.num_qubits // block.num_qubits)
890
+ if k not in skipped_blocks
867
891
  ]
868
-
869
- # apply the operations in the layer
870
- for indices in block_indices:
871
- parameterized_block = self._parameterize_block(block, param_iter, i, j, indices)
872
- layer.compose(parameterized_block, indices, inplace=True)
873
-
874
- # add the layer to the circuit
875
- circuit.compose(layer, inplace=True)
892
+ # apply the operations in the layer
893
+ for indices in block_indices:
894
+ parameterized_block = self._parameterize_block(block, param_iter, i, j, indices)
895
+ circuit.compose(parameterized_block, indices, inplace=True, copy=False)
876
896
 
877
897
  def _build_entanglement_layer(self, circuit, param_iter, i):
878
898
  """Build an entanglement layer."""
879
899
  # iterate over all entanglement blocks
900
+ target_qubits = circuit.qubits
880
901
  for j, block in enumerate(self.entanglement_blocks):
881
- # create a new layer and get the entangler map for this block
882
- layer = QuantumCircuit(*self.qregs)
883
902
  entangler_map = self.get_entangler_map(i, j, block.num_qubits)
884
-
885
- # apply the operations in the layer
886
- for indices in entangler_map:
887
- parameterized_block = self._parameterize_block(block, param_iter, i, j, indices)
888
- layer.compose(parameterized_block, indices, inplace=True)
889
-
890
- # add the layer to the circuit
891
- circuit.compose(layer, inplace=True)
903
+ if (
904
+ self._allow_fast_path_parametrization
905
+ and (simple_block := _stdlib_gate_from_simple_block(block)) is not None
906
+ ):
907
+ for indices in entangler_map:
908
+ # It's actually nontrivially faster to use a listcomp and pass that to `tuple`
909
+ # than to pass a generator expression directly.
910
+ # pylint: disable=consider-using-generator
911
+ instr = CircuitInstruction(
912
+ simple_block.gate(*itertools.islice(param_iter, simple_block.num_params)),
913
+ tuple([target_qubits[i] for i in indices]),
914
+ )
915
+ circuit._append(instr)
916
+ else:
917
+ # apply the operations in the layer
918
+ for indices in entangler_map:
919
+ parameterized_block = self._parameterize_block(block, param_iter, i, j, indices)
920
+ circuit.compose(parameterized_block, indices, inplace=True, copy=False)
892
921
 
893
922
  def _build_additional_layers(self, circuit, which):
894
923
  if which == "appended":
@@ -901,13 +930,10 @@ class NLocal(BlueprintCircuit):
901
930
  raise ValueError("`which` must be either `appended` or `prepended`.")
902
931
 
903
932
  for block, ent in zip(blocks, entanglements):
904
- layer = QuantumCircuit(*self.qregs)
905
933
  if isinstance(ent, str):
906
934
  ent = get_entangler_map(block.num_qubits, self.num_qubits, ent)
907
935
  for indices in ent:
908
- layer.compose(block, indices, inplace=True)
909
-
910
- circuit.compose(layer, inplace=True)
936
+ circuit.compose(block, indices, inplace=True, copy=False)
911
937
 
912
938
  def _build(self) -> None:
913
939
  """If not already built, build the circuit."""
@@ -926,7 +952,7 @@ class NLocal(BlueprintCircuit):
926
952
 
927
953
  # use the initial state as starting circuit, if it is set
928
954
  if self.initial_state:
929
- circuit.compose(self.initial_state.copy(), inplace=True)
955
+ circuit.compose(self.initial_state.copy(), inplace=True, copy=False)
930
956
 
931
957
  param_iter = iter(self.ordered_parameters)
932
958
 
@@ -972,7 +998,7 @@ class NLocal(BlueprintCircuit):
972
998
  except QiskitError:
973
999
  block = circuit.to_instruction()
974
1000
 
975
- self.append(block, self.qubits)
1001
+ self.append(block, self.qubits, copy=False)
976
1002
 
977
1003
  # pylint: disable=unused-argument
978
1004
  def _parameter_generator(self, rep: int, block: int, indices: list[int]) -> Parameter | None:
@@ -1023,7 +1049,7 @@ def get_entangler_map(
1023
1049
  raise ValueError("Pairwise entanglement is not defined for blocks with more than 2 qubits.")
1024
1050
 
1025
1051
  if entanglement == "full":
1026
- return list(combinations(list(range(n)), m))
1052
+ return list(itertools.combinations(list(range(n)), m))
1027
1053
  elif entanglement == "reverse_linear":
1028
1054
  # reverse linear connectivity. In the case of m=2 and the entanglement_block='cx'
1029
1055
  # then it's equivalent to 'full' entanglement
@@ -1057,3 +1083,28 @@ def get_entangler_map(
1057
1083
 
1058
1084
  else:
1059
1085
  raise ValueError(f"Unsupported entanglement type: {entanglement}")
1086
+
1087
+
1088
+ _StdlibGateResult = collections.namedtuple("_StdlibGateResult", ("gate", "num_params"))
1089
+ _STANDARD_GATE_MAPPING = get_standard_gate_name_mapping()
1090
+
1091
+
1092
+ def _stdlib_gate_from_simple_block(block: QuantumCircuit) -> _StdlibGateResult | None:
1093
+ if block.global_phase != 0.0 or len(block) != 1:
1094
+ return None
1095
+ instruction = block.data[0]
1096
+ # If the single instruction isn't a standard-library gate that spans the full width of the block
1097
+ # in the correct order, we're not simple. If the gate isn't fully parametrised with pure,
1098
+ # unique `Parameter` instances (expressions are too complex) that are in order, we're not
1099
+ # simple.
1100
+ if (
1101
+ instruction.clbits
1102
+ or tuple(instruction.qubits) != tuple(block.qubits)
1103
+ or (
1104
+ getattr(_STANDARD_GATE_MAPPING.get(instruction.operation.name), "base_class", None)
1105
+ is not instruction.operation.base_class
1106
+ )
1107
+ or tuple(instruction.operation.params) != tuple(block.parameters)
1108
+ ):
1109
+ return None
1110
+ return _StdlibGateResult(instruction.operation.base_class, len(instruction.operation.params))
@@ -17,35 +17,10 @@ import typing
17
17
  from collections.abc import Callable, Sequence
18
18
 
19
19
  from qiskit.circuit.quantumcircuit import QuantumCircuit
20
- from qiskit.circuit import Gate, Instruction, Parameter
20
+ from qiskit.circuit import Gate, Instruction
21
21
 
22
22
  from .n_local import NLocal
23
- from ..standard_gates import (
24
- IGate,
25
- XGate,
26
- YGate,
27
- ZGate,
28
- RXGate,
29
- RYGate,
30
- RZGate,
31
- HGate,
32
- SGate,
33
- SdgGate,
34
- TGate,
35
- TdgGate,
36
- RXXGate,
37
- RYYGate,
38
- RZXGate,
39
- RZZGate,
40
- SwapGate,
41
- CXGate,
42
- CYGate,
43
- CZGate,
44
- CRXGate,
45
- CRYGate,
46
- CRZGate,
47
- CHGate,
48
- )
23
+ from ..standard_gates import get_standard_gate_name_mapping
49
24
 
50
25
  if typing.TYPE_CHECKING:
51
26
  import qiskit # pylint: disable=cyclic-import
@@ -269,38 +244,7 @@ class TwoLocal(NLocal):
269
244
  if isinstance(layer, QuantumCircuit):
270
245
  return layer
271
246
 
272
- # check the list of valid layers
273
- # this could be a lot easier if the standard layers would have ``name`` and ``num_params``
274
- # as static types, which might be something they should have anyway
275
- theta = Parameter("θ")
276
- valid_layers = {
277
- "ch": CHGate(),
278
- "cx": CXGate(),
279
- "cy": CYGate(),
280
- "cz": CZGate(),
281
- "crx": CRXGate(theta),
282
- "cry": CRYGate(theta),
283
- "crz": CRZGate(theta),
284
- "h": HGate(),
285
- "i": IGate(),
286
- "id": IGate(),
287
- "iden": IGate(),
288
- "rx": RXGate(theta),
289
- "rxx": RXXGate(theta),
290
- "ry": RYGate(theta),
291
- "ryy": RYYGate(theta),
292
- "rz": RZGate(theta),
293
- "rzx": RZXGate(theta),
294
- "rzz": RZZGate(theta),
295
- "s": SGate(),
296
- "sdg": SdgGate(),
297
- "swap": SwapGate(),
298
- "x": XGate(),
299
- "y": YGate(),
300
- "z": ZGate(),
301
- "t": TGate(),
302
- "tdg": TdgGate(),
303
- }
247
+ valid_layers = get_standard_gate_name_mapping()
304
248
 
305
249
  # try to exchange `layer` from a string to a gate instance
306
250
  if isinstance(layer, str):
@@ -26,11 +26,11 @@ class UnitaryOverlap(QuantumCircuit):
26
26
  names `"p1"` (for circuit ``unitary1``) and `"p2"` (for circuit ``unitary_2``) in the output
27
27
  circuit.
28
28
 
29
- This circuit is usually employed in computing the fidelity::
29
+ This circuit is usually employed in computing the fidelity:
30
30
 
31
- .. math::
31
+ .. math::
32
32
 
33
- \left|\langle 0| U_2^{\dag} U_1|0\rangle\right|^{2}
33
+ \left|\langle 0| U_2^{\dag} U_1|0\rangle\right|^{2}
34
34
 
35
35
  by computing the probability of being in the all-zeros bit-string, or equivalently,
36
36
  the expectation value of projector :math:`|0\rangle\langle 0|`.
@@ -87,7 +87,7 @@ class PhaseOracle(QuantumCircuit):
87
87
 
88
88
  super().__init__(oracle.num_qubits, name="Phase Oracle")
89
89
 
90
- self.compose(oracle, inplace=True)
90
+ self.compose(oracle, inplace=True, copy=False)
91
91
 
92
92
  def evaluate_bitstring(self, bitstring: str) -> bool:
93
93
  """Evaluate the oracle on a bitstring.
@@ -15,9 +15,8 @@
15
15
  from typing import Optional, Union
16
16
 
17
17
  import numpy as np
18
- from qiskit.quantum_info.random import random_unitary
19
- from qiskit.circuit import QuantumCircuit
20
- from qiskit.circuit.library.generalized_gates.permutation import Permutation
18
+ from qiskit.circuit import QuantumCircuit, CircuitInstruction
19
+ from qiskit.circuit.library.generalized_gates import PermutationGate, UnitaryGate
21
20
 
22
21
 
23
22
  class QuantumVolume(QuantumCircuit):
@@ -60,6 +59,8 @@ class QuantumVolume(QuantumCircuit):
60
59
  depth: Optional[int] = None,
61
60
  seed: Optional[Union[int, np.random.Generator]] = None,
62
61
  classical_permutation: bool = True,
62
+ *,
63
+ flatten: bool = False,
63
64
  ) -> None:
64
65
  """Create quantum volume model circuit of size num_qubits x depth.
65
66
 
@@ -69,46 +70,46 @@ class QuantumVolume(QuantumCircuit):
69
70
  seed: Random number generator or generator seed.
70
71
  classical_permutation: use classical permutations at every layer,
71
72
  rather than quantum.
73
+ flatten: If ``False`` (the default), construct a circuit that contains a single
74
+ instruction, which in turn has the actual volume structure. If ``True``, construct
75
+ the volume structure directly.
72
76
  """
73
- # Initialize RNG
74
- if seed is None:
75
- rng_set = np.random.default_rng()
76
- seed = rng_set.integers(low=1, high=1000)
77
- if isinstance(seed, np.random.Generator):
78
- rng = seed
79
- else:
80
- rng = np.random.default_rng(seed)
77
+ import scipy.stats
81
78
 
82
79
  # Parameters
83
80
  depth = depth or num_qubits # how many layers of SU(4)
84
- width = int(np.floor(num_qubits / 2)) # how many SU(4)s fit in each layer
85
- name = "quantum_volume_" + str([num_qubits, depth, seed]).replace(" ", "")
81
+ width = num_qubits // 2 # how many SU(4)s fit in each layer
82
+ rng = seed if isinstance(seed, np.random.Generator) else np.random.default_rng(seed)
83
+ if seed is None:
84
+ # Get the internal entropy used to seed the default RNG, if no seed was given. This
85
+ # stays in the output name, so effectively stores a way of regenerating the circuit.
86
+ # This is just best-effort only, for backwards compatibility, and isn't critical (if
87
+ # someone needs full reproducibility, they should be manually controlling the seeding).
88
+ seed = getattr(getattr(rng.bit_generator, "seed_seq", None), "entropy", None)
86
89
 
87
- # Generator random unitary seeds in advance.
88
- # Note that this means we are constructing multiple new generator
89
- # objects from low-entropy integer seeds rather than pass the shared
90
- # generator object to the random_unitary function. This is done so
91
- # that we can use the integer seed as a label for the generated gates.
92
- unitary_seeds = rng.integers(low=1, high=1000, size=[depth, width])
90
+ super().__init__(
91
+ num_qubits, name="quantum_volume_" + str([num_qubits, depth, seed]).replace(" ", "")
92
+ )
93
+ base = self if flatten else QuantumCircuit(num_qubits, name=self.name)
93
94
 
94
95
  # For each layer, generate a permutation of qubits
95
96
  # Then generate and apply a Haar-random SU(4) to each pair
96
- circuit = QuantumCircuit(num_qubits, name=name)
97
- perm_0 = list(range(num_qubits))
98
- for d in range(depth):
99
- perm = rng.permutation(perm_0)
100
- if not classical_permutation:
101
- layer_perm = Permutation(num_qubits, perm)
102
- circuit.compose(layer_perm, inplace=True)
103
- for w in range(width):
104
- seed_u = unitary_seeds[d][w]
105
- su4 = random_unitary(4, seed=seed_u).to_instruction()
106
- su4.label = "su4_" + str(seed_u)
107
- if classical_permutation:
108
- physical_qubits = int(perm[2 * w]), int(perm[2 * w + 1])
109
- circuit.compose(su4, [physical_qubits[0], physical_qubits[1]], inplace=True)
110
- else:
111
- circuit.compose(su4, [2 * w, 2 * w + 1], inplace=True)
112
-
113
- super().__init__(*circuit.qregs, name=circuit.name)
114
- self.compose(circuit.to_instruction(), qubits=self.qubits, inplace=True)
97
+ unitaries = scipy.stats.unitary_group.rvs(4, depth * width, rng).reshape(depth, width, 4, 4)
98
+ qubits = tuple(base.qubits)
99
+ for row in unitaries:
100
+ perm = rng.permutation(num_qubits)
101
+ if classical_permutation:
102
+ for w, unitary in enumerate(row):
103
+ gate = UnitaryGate(unitary, check_input=False, num_qubits=2)
104
+ qubit = 2 * w
105
+ base._append(
106
+ CircuitInstruction(gate, (qubits[perm[qubit]], qubits[perm[qubit + 1]]))
107
+ )
108
+ else:
109
+ base._append(CircuitInstruction(PermutationGate(perm), qubits))
110
+ for w, unitary in enumerate(row):
111
+ gate = UnitaryGate(unitary, check_input=False, num_qubits=2)
112
+ qubit = 2 * w
113
+ base._append(CircuitInstruction(gate, qubits[qubit : qubit + 2]))
114
+ if not flatten:
115
+ self._append(CircuitInstruction(base.to_instruction(), tuple(self.qubits)))
@@ -850,6 +850,56 @@ for inst, qargs, cargs in [
850
850
  def_swap.append(inst, qargs, cargs)
851
851
  _sel.add_equivalence(SwapGate(), def_swap)
852
852
 
853
+ # SwapGate
854
+ #
855
+ # q_0: ─X─
856
+ # │ ≡
857
+ # q_1: ─X─
858
+ #
859
+ # ┌──────────┐┌──────┐ ┌────┐ ┌──────┐┌──────────┐┌──────┐
860
+ # q_0: ┤ Rz(-π/2) ├┤0 ├───┤ √X ├───┤1 ├┤ Rz(-π/2) ├┤0 ├
861
+ # └──┬────┬──┘│ Ecr │┌──┴────┴──┐│ Ecr │└──┬────┬──┘│ Ecr │
862
+ # q_1: ───┤ √X ├───┤1 ├┤ Rz(-π/2) ├┤0 ├───┤ √X ├───┤1 ├
863
+ # └────┘ └──────┘└──────────┘└──────┘ └────┘ └──────┘
864
+ #
865
+ q = QuantumRegister(2, "q")
866
+ def_swap_ecr = QuantumCircuit(q)
867
+ def_swap_ecr.rz(-pi / 2, 0)
868
+ def_swap_ecr.sx(1)
869
+ def_swap_ecr.ecr(0, 1)
870
+ def_swap_ecr.rz(-pi / 2, 1)
871
+ def_swap_ecr.sx(0)
872
+ def_swap_ecr.ecr(1, 0)
873
+ def_swap_ecr.rz(-pi / 2, 0)
874
+ def_swap_ecr.sx(1)
875
+ def_swap_ecr.ecr(0, 1)
876
+ _sel.add_equivalence(SwapGate(), def_swap_ecr)
877
+
878
+ # SwapGate
879
+ #
880
+ # q_0: ─X─
881
+ # │ ≡
882
+ # q_1: ─X─
883
+ #
884
+ # global phase: 3π/2
885
+ # ┌────┐ ┌────┐ ┌────┐
886
+ # q_0: ┤ √X ├─■─┤ √X ├─■─┤ √X ├─■─
887
+ # ├────┤ │ ├────┤ │ ├────┤ │
888
+ # q_1: ┤ √X ├─■─┤ √X ├─■─┤ √X ├─■─
889
+ # └────┘ └────┘ └────┘
890
+ q = QuantumRegister(2, "q")
891
+ def_swap_cz = QuantumCircuit(q, global_phase=-pi / 2)
892
+ def_swap_cz.sx(0)
893
+ def_swap_cz.sx(1)
894
+ def_swap_cz.cz(0, 1)
895
+ def_swap_cz.sx(0)
896
+ def_swap_cz.sx(1)
897
+ def_swap_cz.cz(0, 1)
898
+ def_swap_cz.sx(0)
899
+ def_swap_cz.sx(1)
900
+ def_swap_cz.cz(0, 1)
901
+ _sel.add_equivalence(SwapGate(), def_swap_cz)
902
+
853
903
  # iSwapGate
854
904
  #
855
905
  # ┌────────┐ ┌───┐┌───┐ ┌───┐
@@ -69,10 +69,12 @@ class GlobalPhaseGate(Gate):
69
69
  """
70
70
  return GlobalPhaseGate(-self.params[0])
71
71
 
72
- def __array__(self, dtype=complex):
72
+ def __array__(self, dtype=None, copy=None):
73
73
  """Return a numpy.array for the global_phase gate."""
74
+ if copy is False:
75
+ raise ValueError("unable to avoid copy while creating an array as requested")
74
76
  theta = self.params[0]
75
- return numpy.array([[numpy.exp(1j * theta)]], dtype=dtype)
77
+ return numpy.array([[numpy.exp(1j * theta)]], dtype=dtype or complex)
76
78
 
77
79
  def __eq__(self, other):
78
80
  if isinstance(other, GlobalPhaseGate):
@@ -65,8 +65,7 @@ class IGate(SingletonGate):
65
65
  ."""
66
66
  return IGate() # self-inverse
67
67
 
68
- def power(self, exponent: float):
69
- """Raise gate to a power."""
68
+ def power(self, exponent: float, annotated: bool = False):
70
69
  return IGate()
71
70
 
72
71
  def __eq__(self, other):
@@ -124,8 +124,7 @@ class iSwapGate(SingletonGate):
124
124
 
125
125
  self.definition = qc
126
126
 
127
- def power(self, exponent: float):
128
- """Raise gate to a power."""
127
+ def power(self, exponent: float, annotated: bool = False):
129
128
  return XXPlusYYGate(-np.pi * exponent)
130
129
 
131
130
  def __eq__(self, other):