qiskit 2.0.0rc2__cp39-abi3-win_amd64.whl → 2.0.2__cp39-abi3-win_amd64.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 (55) hide show
  1. qiskit/VERSION.txt +1 -1
  2. qiskit/__init__.py +1 -4
  3. qiskit/_accelerate.pyd +0 -0
  4. qiskit/circuit/__init__.py +11 -5
  5. qiskit/circuit/classical/expr/constructors.py +0 -12
  6. qiskit/circuit/library/__init__.py +449 -163
  7. qiskit/circuit/library/boolean_logic/quantum_or.py +2 -2
  8. qiskit/circuit/library/graph_state.py +1 -0
  9. qiskit/circuit/library/n_local/efficient_su2.py +1 -1
  10. qiskit/circuit/library/n_local/evolved_operator_ansatz.py +3 -1
  11. qiskit/circuit/library/n_local/excitation_preserving.py +1 -1
  12. qiskit/circuit/library/quantum_volume.py +9 -0
  13. qiskit/circuit/library/standard_gates/r.py +4 -3
  14. qiskit/circuit/library/standard_gates/x.py +1 -2
  15. qiskit/circuit/quantumcircuit.py +405 -80
  16. qiskit/converters/circuit_to_dag.py +2 -2
  17. qiskit/converters/dag_to_circuit.py +2 -3
  18. qiskit/dagcircuit/dagdependency_v2.py +3 -2
  19. qiskit/primitives/statevector_estimator.py +1 -1
  20. qiskit/qpy/__init__.py +21 -0
  21. qiskit/qpy/binary_io/circuits.py +5 -0
  22. qiskit/result/models.py +1 -2
  23. qiskit/result/result.py +10 -8
  24. qiskit/synthesis/discrete_basis/commutator_decompose.py +30 -6
  25. qiskit/synthesis/discrete_basis/gate_sequence.py +10 -4
  26. qiskit/synthesis/discrete_basis/generate_basis_approximations.py +1 -1
  27. qiskit/synthesis/discrete_basis/solovay_kitaev.py +36 -13
  28. qiskit/transpiler/passes/__init__.py +2 -0
  29. qiskit/transpiler/passes/basis/basis_translator.py +1 -1
  30. qiskit/transpiler/passes/layout/full_ancilla_allocation.py +2 -3
  31. qiskit/transpiler/passes/layout/sabre_layout.py +3 -1
  32. qiskit/transpiler/passes/layout/vf2_utils.py +2 -5
  33. qiskit/transpiler/passes/optimization/__init__.py +1 -0
  34. qiskit/transpiler/passes/optimization/consolidate_blocks.py +6 -1
  35. qiskit/transpiler/passes/scheduling/alignments/check_durations.py +1 -1
  36. qiskit/transpiler/passes/scheduling/padding/base_padding.py +2 -2
  37. qiskit/transpiler/passes/scheduling/padding/dynamical_decoupling.py +5 -5
  38. qiskit/transpiler/passes/scheduling/padding/pad_delay.py +1 -1
  39. qiskit/transpiler/passes/scheduling/time_unit_conversion.py +10 -6
  40. qiskit/transpiler/passes/synthesis/solovay_kitaev_synthesis.py +29 -19
  41. qiskit/transpiler/passes/synthesis/unitary_synthesis.py +2 -1
  42. qiskit/transpiler/preset_passmanagers/generate_preset_pass_manager.py +13 -7
  43. qiskit/transpiler/target.py +11 -0
  44. qiskit/visualization/circuit/text.py +1 -1
  45. qiskit/visualization/counts_visualization.py +4 -0
  46. qiskit/visualization/library.py +4 -1
  47. qiskit/visualization/state_visualization.py +13 -2
  48. qiskit/visualization/timeline/core.py +1 -1
  49. qiskit/visualization/timeline/plotters/matplotlib.py +4 -1
  50. {qiskit-2.0.0rc2.dist-info → qiskit-2.0.2.dist-info}/METADATA +4 -3
  51. {qiskit-2.0.0rc2.dist-info → qiskit-2.0.2.dist-info}/RECORD +55 -55
  52. {qiskit-2.0.0rc2.dist-info → qiskit-2.0.2.dist-info}/WHEEL +1 -1
  53. {qiskit-2.0.0rc2.dist-info → qiskit-2.0.2.dist-info}/entry_points.txt +0 -0
  54. {qiskit-2.0.0rc2.dist-info → qiskit-2.0.2.dist-info/licenses}/LICENSE.txt +0 -0
  55. {qiskit-2.0.0rc2.dist-info → qiskit-2.0.2.dist-info}/top_level.txt +0 -0
@@ -75,6 +75,6 @@ def circuit_to_dag(circuit, copy_operations=True, *, qubit_order=None, clbit_ord
75
75
 
76
76
  dagcircuit = core_circuit_to_dag(circuit, copy_operations, qubit_order, clbit_order)
77
77
 
78
- dagcircuit.duration = circuit._duration
79
- dagcircuit.unit = circuit._unit
78
+ dagcircuit._duration = circuit._duration
79
+ dagcircuit._unit = circuit._unit
80
80
  return dagcircuit
@@ -74,7 +74,6 @@ def dag_to_circuit(dag, copy_operations=True):
74
74
  circuit.add_stretch(stretch)
75
75
  circuit.metadata = dag.metadata or {}
76
76
  circuit._data = circuit_data
77
-
78
- circuit._duration = dag.duration
79
- circuit._unit = dag.unit
77
+ circuit._duration = dag._duration
78
+ circuit._unit = dag._unit
80
79
  return circuit
@@ -447,8 +447,6 @@ class _DAGDependencyV2:
447
447
  target_dag = _DAGDependencyV2()
448
448
  target_dag.name = self.name
449
449
  target_dag._global_phase = self._global_phase
450
- target_dag.duration = self.duration
451
- target_dag.unit = self.unit
452
450
  target_dag.metadata = self.metadata
453
451
  target_dag._key_cache = self._key_cache
454
452
  target_dag.comm_checker = self.comm_checker
@@ -456,6 +454,9 @@ class _DAGDependencyV2:
456
454
  target_dag.add_qubits(self.qubits)
457
455
  target_dag.add_clbits(self.clbits)
458
456
 
457
+ target_dag.duration = self.duration
458
+ target_dag.unit = self.unit
459
+
459
460
  for qreg in self.qregs.values():
460
461
  target_dag.add_qreg(qreg)
461
462
  for creg in self.cregs.values():
@@ -33,7 +33,7 @@ class StatevectorEstimator(BaseEstimatorV2):
33
33
  Simple implementation of :class:`BaseEstimatorV2` with full state vector simulation.
34
34
 
35
35
  This class is implemented via :class:`~.Statevector` which turns provided circuits into
36
- pure state vectors. These states are subsequently acted on by :class:~.SparsePauliOp`,
36
+ pure state vectors. These states are subsequently acted on by :class:`~.SparsePauliOp`,
37
37
  which implies that, at present, this implementation is only compatible with Pauli-based
38
38
  observables.
39
39
 
qiskit/qpy/__init__.py CHANGED
@@ -194,6 +194,27 @@ of QPY in qiskit-terra 0.18.0.
194
194
  * - Qiskit (qiskit-terra for < 1.0.0) version
195
195
  - :func:`.dump` format(s) output versions
196
196
  - :func:`.load` maximum supported version (older format versions can always be read)
197
+ * - 2.0.0
198
+ - 13, 14
199
+ - 14
200
+ * - 1.4.2
201
+ - 10, 11, 12, 13
202
+ - 13
203
+ * - 1.4.1
204
+ - 10, 11, 12, 13
205
+ - 13
206
+ * - 1.4.0
207
+ - 10, 11, 12, 13
208
+ - 13
209
+ * - 1.3.3
210
+ - 10, 11, 12, 13
211
+ - 13
212
+ * - 1.3.2
213
+ - 10, 11, 12, 13
214
+ - 13
215
+ * - 1.3.1
216
+ - 10, 11, 12, 13
217
+ - 13
197
218
  * - 1.3.0
198
219
  - 10, 11, 12, 13
199
220
  - 13
@@ -783,6 +783,11 @@ def _write_instruction(
783
783
  custom_operations[gate_class_name] = instruction.operation
784
784
  custom_operations_list.append(gate_class_name)
785
785
 
786
+ elif isinstance(instruction.operation, library.MCMTGate):
787
+ gate_class_name = instruction.operation.name + "_" + str(uuid.uuid4())
788
+ custom_operations[gate_class_name] = instruction.operation
789
+ custom_operations_list.append(gate_class_name)
790
+
786
791
  condition_type = type_keys.Condition.NONE
787
792
  condition_register = b""
788
793
  condition_value = 0
qiskit/result/models.py CHANGED
@@ -145,8 +145,7 @@ class ExperimentResult:
145
145
  status (str): The status of the experiment
146
146
  seed (int): The seed used for simulation (if run on a simulator)
147
147
  meas_return (str): The type of measurement returned
148
- header (dict): A free form dictionary
149
- header for the experiment
148
+ header (dict): A free form dictionary header for the experiment
150
149
  kwargs: Arbitrary extra fields
151
150
 
152
151
  Raises:
qiskit/result/result.py CHANGED
@@ -34,6 +34,9 @@ class Result:
34
34
  each experiment success)
35
35
  results (list[ExperimentResult]): corresponding results for array of
36
36
  experiments of the input
37
+ date (str): optional date field
38
+ status (str): optional status field
39
+ header (dict): an optional free form dictionary header
37
40
  """
38
41
 
39
42
  _metadata = {}
@@ -88,7 +91,7 @@ class Result:
88
91
  "backend_name": self.backend_name,
89
92
  "backend_version": self.backend_version,
90
93
  "date": self.date,
91
- "header": None if self.header is None else self.header.to_dict(),
94
+ "header": self.header,
92
95
  "job_id": self.job_id,
93
96
  "status": self.status,
94
97
  "success": self.success,
@@ -128,11 +131,10 @@ class Result:
128
131
  the get_xxx method, and the data will be post-processed for the data type.
129
132
 
130
133
  Args:
131
- experiment (str or QuantumCircuit or Schedule or int or None): the index of the
134
+ experiment (str or QuantumCircuit or int or None): the index of the
132
135
  experiment. Several types are accepted for convenience::
133
136
  * str: the name of the experiment.
134
137
  * QuantumCircuit: the name of the circuit instance will be used.
135
- * Schedule: the name of the schedule instance will be used.
136
138
  * int: the position of the experiment.
137
139
  * None: if there is only one experiment, returns it.
138
140
 
@@ -179,7 +181,7 @@ class Result:
179
181
  ['00000', '01000', '10100', '10100', '11101', '11100', '00101', ..., '01010']
180
182
 
181
183
  Args:
182
- experiment (str or QuantumCircuit or Schedule or int or None): the index of the
184
+ experiment (str or QuantumCircuit or int or None): the index of the
183
185
  experiment, as specified by ``data()``.
184
186
 
185
187
  Returns:
@@ -231,7 +233,7 @@ class Result:
231
233
  """Get the histogram data of an experiment.
232
234
 
233
235
  Args:
234
- experiment (str or QuantumCircuit or Schedule or int or None): the index of the
236
+ experiment (str or QuantumCircuit or int or None): the index of the
235
237
  experiment, as specified by ``data([experiment])``.
236
238
 
237
239
  Returns:
@@ -283,7 +285,7 @@ class Result:
283
285
  """Get the final statevector of an experiment.
284
286
 
285
287
  Args:
286
- experiment (str or QuantumCircuit or Schedule or int or None): the index of the
288
+ experiment (str or QuantumCircuit or int or None): the index of the
287
289
  experiment, as specified by ``data()``.
288
290
  decimals (int): the number of decimals in the statevector.
289
291
  If None, does not round.
@@ -305,7 +307,7 @@ class Result:
305
307
  """Get the final unitary of an experiment.
306
308
 
307
309
  Args:
308
- experiment (str or QuantumCircuit or Schedule or int or None): the index of the
310
+ experiment (str or QuantumCircuit or int or None): the index of the
309
311
  experiment, as specified by ``data()``.
310
312
  decimals (int): the number of decimals in the unitary.
311
313
  If None, does not round.
@@ -326,7 +328,7 @@ class Result:
326
328
  """Return a single experiment result from a given key.
327
329
 
328
330
  Args:
329
- key (str or QuantumCircuit or Schedule or int or None): the index of the
331
+ key (str or QuantumCircuit or int or None): the index of the
330
332
  experiment, as specified by ``data()``.
331
333
 
332
334
  Returns:
@@ -53,16 +53,40 @@ def _compute_rotation_axis(matrix: np.ndarray) -> np.ndarray:
53
53
  """
54
54
  _check_is_so3(matrix)
55
55
 
56
+ # If theta represents the rotation angle, then trace = 1 + 2cos(theta).
56
57
  trace = _compute_trace_so3(matrix)
57
- theta = math.acos(0.5 * (trace - 1))
58
- if math.sin(theta) > 1e-10:
59
- x = 1 / (2 * math.sin(theta)) * (matrix[2][1] - matrix[1][2])
60
- y = 1 / (2 * math.sin(theta)) * (matrix[0][2] - matrix[2][0])
61
- z = 1 / (2 * math.sin(theta)) * (matrix[1][0] - matrix[0][1])
62
- else:
58
+
59
+ if trace >= 3 - 1e-10:
60
+ # The matrix is the identity (rotation by 0)
63
61
  x = 1.0
64
62
  y = 0.0
65
63
  z = 0.0
64
+
65
+ elif trace <= -1 + 1e-10:
66
+ # The matrix is the 180-degree rotation
67
+ squares = (1 + np.diagonal(matrix)) / 2
68
+ index_of_max = np.argmax(squares)
69
+
70
+ if index_of_max == 0:
71
+ x = math.sqrt(squares[0])
72
+ y = matrix[0][1] / (2 * x)
73
+ z = matrix[0][2] / (2 * x)
74
+ elif index_of_max == 1:
75
+ y = math.sqrt(squares[1])
76
+ x = matrix[0][1] / (2 * y)
77
+ z = matrix[1][2] / (2 * y)
78
+ else:
79
+ z = math.sqrt(squares[2])
80
+ x = matrix[0][2] / (2 * z)
81
+ y = matrix[1][2] / (2 * z)
82
+
83
+ else:
84
+ # The matrix is the rotation by theta with sin(theta)!=0
85
+ theta = math.acos(0.5 * (trace - 1))
86
+ x = 1 / (2 * math.sin(theta)) * (matrix[2][1] - matrix[1][2])
87
+ y = 1 / (2 * math.sin(theta)) * (matrix[0][2] - matrix[2][0])
88
+ z = 1 / (2 * math.sin(theta)) * (matrix[1][0] - matrix[0][1])
89
+
66
90
  return np.array([x, y, z])
67
91
 
68
92
 
@@ -55,7 +55,6 @@ class GateSequence:
55
55
  self.name = " ".join(self.labels)
56
56
  self.global_phase = global_phase
57
57
  self.product = so3_matrix
58
- self.product_su2 = su2_matrix
59
58
 
60
59
  def remove_cancelling_pair(self, indices: Sequence[int]) -> None:
61
60
  """Remove a pair of indices that cancel each other and *do not* change the matrices."""
@@ -106,6 +105,16 @@ class GateSequence:
106
105
 
107
106
  return circuit
108
107
 
108
+ def _to_u2(self):
109
+ """Creates the U2 matrix corresponding to the stored sequence of gates
110
+ and the global phase.
111
+ """
112
+ u2 = np.eye(2, dtype=complex)
113
+ for mat in self.gates:
114
+ u2 = mat.to_matrix().dot(u2)
115
+ u2 = np.exp(1j * self.global_phase) * u2
116
+ return u2
117
+
109
118
  def to_dag(self):
110
119
  """Convert to a :class:`.DAGCircuit`.
111
120
 
@@ -149,7 +158,6 @@ class GateSequence:
149
158
  so3 = _convert_su2_to_so3(su2)
150
159
 
151
160
  self.product = so3.dot(self.product)
152
- self.product_su2 = su2.dot(self.product_su2)
153
161
  self.global_phase = self.global_phase + phase
154
162
 
155
163
  self.gates.append(gate)
@@ -172,7 +180,6 @@ class GateSequence:
172
180
  adjoint.labels = [inv.name for inv in adjoint.gates]
173
181
  adjoint.name = " ".join(adjoint.labels)
174
182
  adjoint.product = np.conj(self.product).T
175
- adjoint.product_su2 = np.conj(self.product_su2).T
176
183
  adjoint.global_phase = -self.global_phase
177
184
 
178
185
  return adjoint
@@ -190,7 +197,6 @@ class GateSequence:
190
197
  out.matrices = self.matrices.copy()
191
198
  out.global_phase = self.global_phase
192
199
  out.product = self.product.copy()
193
- out.product_su2 = self.product_su2.copy()
194
200
  out.name = self.name
195
201
  out._eulers = self._eulers
196
202
  return out
@@ -76,7 +76,7 @@ def _check_candidate_greedy(candidate, existing_sequences, tol=1e-10):
76
76
  return False
77
77
 
78
78
  for existing in existing_sequences:
79
- if matrix_equal(existing.product_su2, candidate.product_su2, ignore_phase=True, atol=tol):
79
+ if matrix_equal(existing.product, candidate.product, ignore_phase=True, atol=tol):
80
80
  # is the new sequence less or more efficient?
81
81
  return len(candidate.gates) < len(existing.gates)
82
82
  return True
@@ -24,7 +24,7 @@ from .generate_basis_approximations import generate_basic_approximations, _1q_ga
24
24
  class SolovayKitaevDecomposition:
25
25
  """The Solovay Kitaev discrete decomposition algorithm.
26
26
 
27
- This class is called recursively by the transpiler pass, which is why it is separeted.
27
+ This class is called recursively by the transpiler pass, which is why it is separated.
28
28
  See :class:`qiskit.transpiler.passes.SolovayKitaev` for more information.
29
29
  """
30
30
 
@@ -33,7 +33,7 @@ class SolovayKitaevDecomposition:
33
33
  ) -> None:
34
34
  """
35
35
  Args:
36
- basic_approximations: A specification of the basic SU(2) approximations in terms
36
+ basic_approximations: A specification of the basic SO(3) approximations in terms
37
37
  of discrete gates. At each iteration this algorithm, the remaining error is
38
38
  approximated with the closest sequence of gates in this set.
39
39
  If a ``str``, this specifies a ``.npy`` filename from which to load the
@@ -116,23 +116,33 @@ class SolovayKitaevDecomposition:
116
116
  """
117
117
  # make input matrix SU(2) and get the according global phase
118
118
  z = 1 / np.sqrt(np.linalg.det(gate_matrix))
119
- gate_matrix_su2 = GateSequence.from_matrix(z * gate_matrix)
119
+
120
+ gate_matrix_su2 = z * gate_matrix
121
+ gate_matrix_as_sequence = GateSequence.from_matrix(gate_matrix_su2)
120
122
  global_phase = np.arctan2(np.imag(z), np.real(z))
121
123
 
122
124
  # get the decomposition as GateSequence type
123
- decomposition = self._recurse(gate_matrix_su2, recursion_degree, check_input=check_input)
125
+ decomposition = self._recurse(
126
+ gate_matrix_as_sequence, recursion_degree, check_input=check_input
127
+ )
124
128
 
125
129
  # simplify
126
130
  _remove_identities(decomposition)
127
131
  _remove_inverse_follows_gate(decomposition)
128
132
 
133
+ # adjust to the correct SU(2) phase
134
+ adjust_phase = (
135
+ np.pi if _should_adjust_phase(decomposition._to_u2(), gate_matrix_su2) else 0.0
136
+ )
137
+
129
138
  # convert to a circuit and attach the right phases
130
139
  if return_dag:
131
140
  out = decomposition.to_dag()
132
141
  else:
133
142
  out = decomposition.to_circuit()
134
143
 
135
- out.global_phase = decomposition.global_phase - global_phase
144
+ out.global_phase += adjust_phase
145
+ out.global_phase -= global_phase
136
146
 
137
147
  return out
138
148
 
@@ -155,17 +165,20 @@ class SolovayKitaevDecomposition:
155
165
  raise ValueError("Shape of U must be (3, 3) but is", sequence.shape)
156
166
 
157
167
  if n == 0:
158
- return self.find_basic_approximation(sequence)
168
+ res = self.find_basic_approximation(sequence)
159
169
 
160
- u_n1 = self._recurse(sequence, n - 1, check_input=check_input)
170
+ else:
171
+ u_n1 = self._recurse(sequence, n - 1, check_input=check_input)
161
172
 
162
- v_n, w_n = commutator_decompose(
163
- sequence.dot(u_n1.adjoint()).product, check_input=check_input
164
- )
173
+ v_n, w_n = commutator_decompose(
174
+ sequence.dot(u_n1.adjoint()).product, check_input=check_input
175
+ )
176
+
177
+ v_n1 = self._recurse(v_n, n - 1, check_input=check_input)
178
+ w_n1 = self._recurse(w_n, n - 1, check_input=check_input)
179
+ res = v_n1.dot(w_n1).dot(v_n1.adjoint()).dot(w_n1.adjoint()).dot(u_n1)
165
180
 
166
- v_n1 = self._recurse(v_n, n - 1, check_input=check_input)
167
- w_n1 = self._recurse(w_n, n - 1, check_input=check_input)
168
- return v_n1.dot(w_n1).dot(v_n1.adjoint()).dot(w_n1.adjoint()).dot(u_n1)
181
+ return res
169
182
 
170
183
  def find_basic_approximation(self, sequence: GateSequence) -> GateSequence:
171
184
  """Find ``GateSequence`` in ``self._basic_approximations`` that approximates ``sequence``.
@@ -215,3 +228,13 @@ def _remove_identities(sequence):
215
228
  sequence.gates.pop(index)
216
229
  else:
217
230
  index += 1
231
+
232
+
233
+ def _should_adjust_phase(computed: np.ndarray, target: np.ndarray) -> bool:
234
+ """
235
+ The implemented SolovayKitaevDecomposition has a global phase uncertainty of +-1,
236
+ due to approximating not the original SU(2) matrix but its projection onto SO(3).
237
+ This function returns ``True`` if the global phase of the computed approximation
238
+ should be adjusted (by adding pi) to better much the target.
239
+ """
240
+ return np.linalg.norm(-computed - target) < np.linalg.norm(computed - target)
@@ -91,6 +91,7 @@ Optimizations
91
91
  Split2QUnitaries
92
92
  RemoveIdentityEquivalent
93
93
  ContractIdleWiresInControlFlow
94
+ LightCone
94
95
 
95
96
  Scheduling
96
97
  =============
@@ -225,6 +226,7 @@ from .optimization import OptimizeAnnotated
225
226
  from .optimization import RemoveIdentityEquivalent
226
227
  from .optimization import Split2QUnitaries
227
228
  from .optimization import ContractIdleWiresInControlFlow
229
+ from .optimization import LightCone
228
230
 
229
231
  # circuit analysis
230
232
  from .analysis import ResourceEstimation
@@ -18,7 +18,7 @@ import logging
18
18
  from collections import defaultdict
19
19
 
20
20
  from qiskit.transpiler.basepasses import TransformationPass
21
- from qiskit._accelerate.basis.basis_translator import base_run
21
+ from qiskit._accelerate.basis_translator import base_run
22
22
 
23
23
  logger = logging.getLogger(__name__)
24
24
 
@@ -92,9 +92,8 @@ class FullAncillaAllocation(AnalysisPass):
92
92
 
93
93
  if idle_physical_qubits:
94
94
  if self.ancilla_name in dag.qregs:
95
- qreg = QuantumRegister(
96
- len(idle_physical_qubits),
97
- name=f"{self.ancilla_name}{QuantumRegister.instances_count + 1}",
95
+ qreg = QuantumRegister._new_with_prefix(
96
+ len(idle_physical_qubits), self.ancilla_name
98
97
  )
99
98
  else:
100
99
  qreg = QuantumRegister(len(idle_physical_qubits), name=self.ancilla_name)
@@ -305,6 +305,9 @@ class SabreLayout(TransformationPass):
305
305
  # the layout and routing together as part of resolving the Sabre result.
306
306
  physical_qubits = QuantumRegister(self.coupling_map.size(), "q")
307
307
  mapped_dag = DAGCircuit()
308
+ mapped_dag.name = dag.name
309
+ mapped_dag.metadata = dag.metadata
310
+ mapped_dag.global_phase = dag.global_phase
308
311
  mapped_dag.add_qreg(physical_qubits)
309
312
  mapped_dag.add_clbits(dag.clbits)
310
313
  for creg in dag.cregs.values():
@@ -319,7 +322,6 @@ class SabreLayout(TransformationPass):
319
322
  mapped_dag.add_captured_stretch(stretch)
320
323
  for stretch in dag.iter_declared_stretches():
321
324
  mapped_dag.add_declared_stretch(stretch)
322
- mapped_dag.global_phase = dag.global_phase
323
325
  self.property_set["original_qubit_indices"] = {
324
326
  bit: index for index, bit in enumerate(dag.qubits)
325
327
  }
@@ -179,11 +179,8 @@ def build_average_error_map(target, coupling_map):
179
179
  coupling_map = target.build_coupling_map()
180
180
  if not built and coupling_map is not None and num_qubits is not None:
181
181
  for qubit in range(num_qubits):
182
- avg_map.add_error(
183
- (qubit, qubit),
184
- (coupling_map.graph.out_degree(qubit) + coupling_map.graph.in_degree(qubit))
185
- / num_qubits,
186
- )
182
+ degree = len(set(coupling_map.graph.neighbors_undirected(qubit)))
183
+ avg_map.add_error((qubit, qubit), degree / num_qubits)
187
184
  for edge in coupling_map.graph.edge_list():
188
185
  avg_map.add_error(edge, (avg_map[edge[0], edge[0]] + avg_map[edge[1], edge[1]]) / 2)
189
186
  built = True
@@ -39,3 +39,4 @@ from .remove_identity_equiv import RemoveIdentityEquivalent
39
39
  from .split_2q_unitaries import Split2QUnitaries
40
40
  from .collect_and_collapse import CollectAndCollapse
41
41
  from .contract_idle_wires_in_control_flow import ContractIdleWiresInControlFlow
42
+ from .light_cone import LightCone
@@ -91,6 +91,7 @@ class ConsolidateBlocks(TransformationPass):
91
91
  """
92
92
  super().__init__()
93
93
  self.basis_gates = None
94
+ self.basis_gate_name = None
94
95
  # Bypass target if it doesn't contain any basis gates (i.e. it's a _FakeTarget), as this
95
96
  # not part of the official target model.
96
97
  self.target = target if target is not None and len(target.operation_names) > 0 else None
@@ -99,6 +100,7 @@ class ConsolidateBlocks(TransformationPass):
99
100
  self.force_consolidate = force_consolidate
100
101
  if kak_basis_gate is not None:
101
102
  self.decomposer = TwoQubitBasisDecomposer(kak_basis_gate)
103
+ self.basis_gate_name = kak_basis_gate.name
102
104
  elif basis_gates is not None:
103
105
  kak_gates = KAK_GATE_NAMES.keys() & (basis_gates or [])
104
106
  kak_param_gates = KAK_GATE_PARAM_NAMES.keys() & (basis_gates or [])
@@ -106,14 +108,17 @@ class ConsolidateBlocks(TransformationPass):
106
108
  self.decomposer = TwoQubitControlledUDecomposer(
107
109
  KAK_GATE_PARAM_NAMES[list(kak_param_gates)[0]]
108
110
  )
111
+ self.basis_gate_name = list(kak_param_gates)[0]
109
112
  elif kak_gates:
110
113
  self.decomposer = TwoQubitBasisDecomposer(
111
114
  KAK_GATE_NAMES[list(kak_gates)[0]], basis_fidelity=approximation_degree or 1.0
112
115
  )
116
+ self.basis_gate_name = list(kak_gates)[0]
113
117
  else:
114
118
  self.decomposer = None
115
119
  else:
116
120
  self.decomposer = TwoQubitBasisDecomposer(CXGate())
121
+ self.basis_gate_name = "cx"
117
122
 
118
123
  def run(self, dag):
119
124
  """Run the ConsolidateBlocks pass on `dag`.
@@ -134,7 +139,7 @@ class ConsolidateBlocks(TransformationPass):
134
139
  consolidate_blocks(
135
140
  dag,
136
141
  self.decomposer._inner_decomposer,
137
- self.decomposer.gate_name,
142
+ self.basis_gate_name,
138
143
  self.force_consolidate,
139
144
  target=self.target,
140
145
  basis_gates=self.basis_gates,
@@ -59,7 +59,7 @@ class InstructionDurationCheck(AnalysisPass):
59
59
  self.property_set["reschedule_required"] = False
60
60
 
61
61
  # Rescheduling is not necessary
62
- if self.acquire_align == 1 and self.pulse_align == 1:
62
+ if (self.acquire_align == 1 and self.pulse_align == 1) or dag._num_stretches != 0:
63
63
  return
64
64
 
65
65
  # Check delay durations
@@ -124,7 +124,7 @@ class BasePadding(TransformationPass):
124
124
 
125
125
  new_dag.name = dag.name
126
126
  new_dag.metadata = dag.metadata
127
- new_dag.unit = self.property_set["time_unit"]
127
+ new_dag._unit = self.property_set["time_unit"]
128
128
  new_dag.global_phase = dag.global_phase
129
129
 
130
130
  idle_after = {bit: 0 for bit in dag.qubits}
@@ -186,7 +186,7 @@ class BasePadding(TransformationPass):
186
186
  prev_node=prev_node,
187
187
  )
188
188
 
189
- new_dag.duration = circuit_duration
189
+ new_dag._duration = circuit_duration
190
190
 
191
191
  return new_dag
192
192
 
@@ -310,14 +310,14 @@ class PadDynamicalDecoupling(BasePadding):
310
310
 
311
311
  if not self.__is_dd_qubit(dag.qubits.index(qubit)):
312
312
  # Target physical qubit is not the target of this DD sequence.
313
- self._apply_scheduled_op(dag, t_start, Delay(time_interval, dag.unit), qubit)
313
+ self._apply_scheduled_op(dag, t_start, Delay(time_interval, dag._unit), qubit)
314
314
  return
315
315
 
316
316
  if self._skip_reset_qubits and (
317
317
  isinstance(prev_node, DAGInNode) or isinstance(prev_node.op, Reset)
318
318
  ):
319
319
  # Previous node is the start edge or reset, i.e. qubit is ground state.
320
- self._apply_scheduled_op(dag, t_start, Delay(time_interval, dag.unit), qubit)
320
+ self._apply_scheduled_op(dag, t_start, Delay(time_interval, dag._unit), qubit)
321
321
  return
322
322
 
323
323
  slack = time_interval - np.sum(self._dd_sequence_lengths[qubit])
@@ -325,7 +325,7 @@ class PadDynamicalDecoupling(BasePadding):
325
325
 
326
326
  if slack <= 0:
327
327
  # Interval too short.
328
- self._apply_scheduled_op(dag, t_start, Delay(time_interval, dag.unit), qubit)
328
+ self._apply_scheduled_op(dag, t_start, Delay(time_interval, dag._unit), qubit)
329
329
  return
330
330
 
331
331
  if len(self._dd_sequence) == 1:
@@ -351,7 +351,7 @@ class PadDynamicalDecoupling(BasePadding):
351
351
  sequence_gphase += phase
352
352
  else:
353
353
  # Don't do anything if there's no single-qubit gate to absorb the inverse
354
- self._apply_scheduled_op(dag, t_start, Delay(time_interval, dag.unit), qubit)
354
+ self._apply_scheduled_op(dag, t_start, Delay(time_interval, dag._unit), qubit)
355
355
  return
356
356
 
357
357
  def _constrained_length(values):
@@ -387,7 +387,7 @@ class PadDynamicalDecoupling(BasePadding):
387
387
  if dd_ind < len(taus):
388
388
  tau = taus[dd_ind]
389
389
  if tau > 0:
390
- self._apply_scheduled_op(dag, idle_after, Delay(tau, dag.unit), qubit)
390
+ self._apply_scheduled_op(dag, idle_after, Delay(tau, dag._unit), qubit)
391
391
  idle_after += tau
392
392
  if dd_ind < len(self._dd_sequence):
393
393
  gate = self._dd_sequence[dd_ind]
@@ -87,4 +87,4 @@ class PadDelay(BasePadding):
87
87
  return
88
88
 
89
89
  time_interval = t_end - t_start
90
- self._apply_scheduled_op(dag, t_start, Delay(time_interval, dag.unit), qubit)
90
+ self._apply_scheduled_op(dag, t_start, Delay(time_interval, dag._unit), qubit)
@@ -126,15 +126,19 @@ class TimeUnitConversion(TransformationPass):
126
126
  # Check what units are used in other instructions: dt or SI or mixed
127
127
  units_other = inst_durations.units_used()
128
128
  unified_unit = self._unified(units_other)
129
- if unified_unit == "SI" and not has_dt:
130
- time_unit = "s"
131
- elif unified_unit == "dt" and not has_si:
132
- time_unit = "dt"
133
- else:
129
+ has_si = has_si or unified_unit in {"SI", "mixed"}
130
+ has_dt = has_dt or unified_unit in {"dt", "mixed"}
131
+ if has_si and has_dt:
134
132
  raise TranspilerError(
135
133
  "Fail to unify time units. SI units "
136
134
  "and dt unit must not be mixed when dt is not supplied."
137
135
  )
136
+ if has_si:
137
+ time_unit = "s"
138
+ else:
139
+ # Either dt units were used or no units were used and we
140
+ # default to dt
141
+ time_unit = "dt"
138
142
  else:
139
143
  time_unit = "dt"
140
144
 
@@ -154,7 +158,7 @@ class TimeUnitConversion(TransformationPass):
154
158
  @staticmethod
155
159
  def _unified(unit_set: Set[str]) -> str:
156
160
  if not unit_set:
157
- return "dt"
161
+ return "none"
158
162
 
159
163
  if len(unit_set) == 1 and "dt" in unit_set:
160
164
  return "dt"