qiskit 1.4.1__cp39-abi3-win_amd64.whl → 1.4.3__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.
- qiskit/VERSION.txt +1 -1
- qiskit/_accelerate.pyd +0 -0
- qiskit/circuit/duration.py +16 -16
- qiskit/circuit/library/standard_gates/r.py +4 -3
- qiskit/circuit/library/standard_gates/x.py +1 -2
- qiskit/circuit/parameterexpression.py +6 -1
- qiskit/circuit/quantumcircuit.py +9 -9
- qiskit/circuit/tools/pi_check.py +3 -0
- qiskit/converters/circuit_to_dag.py +2 -2
- qiskit/converters/dag_to_circuit.py +2 -3
- qiskit/dagcircuit/dagdependency_v2.py +3 -2
- qiskit/primitives/statevector_estimator.py +1 -1
- qiskit/qpy/binary_io/circuits.py +21 -4
- qiskit/qpy/binary_io/parse_sympy_repr.py +121 -0
- qiskit/qpy/binary_io/schedules.py +61 -18
- qiskit/qpy/binary_io/value.py +8 -5
- qiskit/qpy/interface.py +19 -5
- qiskit/synthesis/discrete_basis/commutator_decompose.py +30 -6
- qiskit/synthesis/discrete_basis/gate_sequence.py +10 -4
- qiskit/synthesis/discrete_basis/generate_basis_approximations.py +3 -1
- qiskit/synthesis/discrete_basis/solovay_kitaev.py +36 -13
- qiskit/transpiler/passes/layout/sabre_layout.py +4 -1
- qiskit/transpiler/passes/layout/vf2_utils.py +2 -5
- qiskit/transpiler/passes/optimization/template_matching/template_substitution.py +2 -2
- qiskit/transpiler/passes/scheduling/alap.py +2 -2
- qiskit/transpiler/passes/scheduling/alignments/align_measures.py +3 -3
- qiskit/transpiler/passes/scheduling/asap.py +2 -2
- qiskit/transpiler/passes/scheduling/dynamical_decoupling.py +1 -1
- qiskit/transpiler/passes/scheduling/padding/base_padding.py +2 -2
- qiskit/transpiler/passes/scheduling/padding/dynamical_decoupling.py +5 -5
- qiskit/transpiler/passes/scheduling/padding/pad_delay.py +1 -1
- qiskit/transpiler/passes/synthesis/solovay_kitaev_synthesis.py +29 -19
- qiskit/visualization/timeline/core.py +1 -1
- {qiskit-1.4.1.dist-info → qiskit-1.4.3.dist-info}/METADATA +4 -3
- {qiskit-1.4.1.dist-info → qiskit-1.4.3.dist-info}/RECORD +39 -38
- {qiskit-1.4.1.dist-info → qiskit-1.4.3.dist-info}/WHEEL +1 -1
- {qiskit-1.4.1.dist-info → qiskit-1.4.3.dist-info}/entry_points.txt +0 -0
- {qiskit-1.4.1.dist-info → qiskit-1.4.3.dist-info/licenses}/LICENSE.txt +0 -0
- {qiskit-1.4.1.dist-info → qiskit-1.4.3.dist-info}/top_level.txt +0 -0
qiskit/qpy/interface.py
CHANGED
@@ -238,6 +238,7 @@ def dump(
|
|
238
238
|
def load(
|
239
239
|
file_obj: BinaryIO,
|
240
240
|
metadata_deserializer: Optional[Type[JSONDecoder]] = None,
|
241
|
+
trust_payload: bool = False,
|
241
242
|
) -> List[QPY_SUPPORTED_TYPES]:
|
242
243
|
"""Load a QPY binary file
|
243
244
|
|
@@ -277,6 +278,18 @@ def load(
|
|
277
278
|
If this is not specified the circuit metadata will
|
278
279
|
be parsed as JSON with the stdlib ``json.load()`` function using
|
279
280
|
the default ``JSONDecoder`` class.
|
281
|
+
trust_payload: if set to ``False`` (the default),
|
282
|
+
:class:`.ScheduleBlock` objects in the payload that were
|
283
|
+
serialized using ``sympy`` are not allowed and will error. This
|
284
|
+
is because the ``sympy`` parsing uses :func:`eval`, which
|
285
|
+
can allow for arbitrary code execution.
|
286
|
+
The flag should only be set
|
287
|
+
to ``True`` if you trust the QPY payload you are loading.
|
288
|
+
|
289
|
+
.. warning::
|
290
|
+
|
291
|
+
If ``trust_payload`` is set to ``True`` this can enable arbitrary
|
292
|
+
code execution because internally ``sympy`` relies on :func:`eval`.
|
280
293
|
|
281
294
|
Returns:
|
282
295
|
The list of Qiskit programs contained in the QPY data.
|
@@ -344,6 +357,11 @@ def load(
|
|
344
357
|
else:
|
345
358
|
type_key = common.read_type_key(file_obj)
|
346
359
|
|
360
|
+
if data.qpy_version < 10:
|
361
|
+
use_symengine = False
|
362
|
+
else:
|
363
|
+
use_symengine = data.symbolic_encoding == type_keys.SymExprEncoding.SYMENGINE
|
364
|
+
|
347
365
|
if type_key == type_keys.Program.CIRCUIT:
|
348
366
|
loader = binary_io.read_circuit
|
349
367
|
elif type_key == type_keys.Program.SCHEDULE_BLOCK:
|
@@ -359,11 +377,6 @@ def load(
|
|
359
377
|
else:
|
360
378
|
raise TypeError(f"Invalid payload format data kind '{type_key}'.")
|
361
379
|
|
362
|
-
if data.qpy_version < 10:
|
363
|
-
use_symengine = False
|
364
|
-
else:
|
365
|
-
use_symengine = data.symbolic_encoding == type_keys.SymExprEncoding.SYMENGINE
|
366
|
-
|
367
380
|
programs = []
|
368
381
|
for _ in range(data.num_programs):
|
369
382
|
programs.append(
|
@@ -372,6 +385,7 @@ def load(
|
|
372
385
|
data.qpy_version,
|
373
386
|
metadata_deserializer=metadata_deserializer,
|
374
387
|
use_symengine=use_symengine,
|
388
|
+
trust_input=trust_payload,
|
375
389
|
)
|
376
390
|
)
|
377
391
|
return programs
|
@@ -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
|
-
|
58
|
-
if
|
59
|
-
|
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
|
@@ -37,6 +37,8 @@ _1q_inverses = {
|
|
37
37
|
"tdg": "t",
|
38
38
|
"s": "sdg",
|
39
39
|
"sdg": "s",
|
40
|
+
"sx": "sxdg",
|
41
|
+
"sxdg": "sx",
|
40
42
|
}
|
41
43
|
|
42
44
|
_1q_gates = {
|
@@ -74,7 +76,7 @@ def _check_candidate_greedy(candidate, existing_sequences, tol=1e-10):
|
|
74
76
|
return False
|
75
77
|
|
76
78
|
for existing in existing_sequences:
|
77
|
-
if matrix_equal(existing.
|
79
|
+
if matrix_equal(existing.product, candidate.product, ignore_phase=True, atol=tol):
|
78
80
|
# is the new sequence less or more efficient?
|
79
81
|
return len(candidate.gates) < len(existing.gates)
|
80
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
|
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
|
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
|
-
|
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(
|
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
|
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
|
-
|
168
|
+
res = self.find_basic_approximation(sequence)
|
159
169
|
|
160
|
-
|
170
|
+
else:
|
171
|
+
u_n1 = self._recurse(sequence, n - 1, check_input=check_input)
|
161
172
|
|
162
|
-
|
163
|
-
|
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
|
-
|
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)
|
@@ -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():
|
@@ -315,7 +318,7 @@ class SabreLayout(TransformationPass):
|
|
315
318
|
mapped_dag.add_captured_var(var)
|
316
319
|
for var in dag.iter_declared_vars():
|
317
320
|
mapped_dag.add_declared_var(var)
|
318
|
-
|
321
|
+
|
319
322
|
self.property_set["original_qubit_indices"] = {
|
320
323
|
bit: index for index, bit in enumerate(dag.qubits)
|
321
324
|
}
|
@@ -203,11 +203,8 @@ def build_average_error_map(target, properties, coupling_map):
|
|
203
203
|
coupling_map = target.build_coupling_map()
|
204
204
|
if not built and coupling_map is not None:
|
205
205
|
for qubit in range(num_qubits):
|
206
|
-
|
207
|
-
|
208
|
-
(coupling_map.graph.out_degree(qubit) + coupling_map.graph.in_degree(qubit))
|
209
|
-
/ num_qubits,
|
210
|
-
)
|
206
|
+
degree = len(set(coupling_map.graph.neighbors_undirected(qubit)))
|
207
|
+
avg_map.add_error((qubit, qubit), degree / num_qubits)
|
211
208
|
for edge in coupling_map.graph.edge_list():
|
212
209
|
avg_map.add_error(edge, (avg_map[edge[0], edge[0]] + avg_map[edge[1], edge[1]]) / 2)
|
213
210
|
built = True
|
@@ -495,7 +495,6 @@ class TemplateSubstitution:
|
|
495
495
|
parameter constraints, returns None.
|
496
496
|
"""
|
497
497
|
import sympy as sym
|
498
|
-
from sympy.parsing.sympy_parser import parse_expr
|
499
498
|
|
500
499
|
if _optionals.HAS_SYMENGINE:
|
501
500
|
import symengine
|
@@ -571,7 +570,8 @@ class TemplateSubstitution:
|
|
571
570
|
if isinstance(circuit_param, ParameterExpression):
|
572
571
|
circ_param_sym = circuit_param.sympify()
|
573
572
|
else:
|
574
|
-
|
573
|
+
# if it's not a ParameterExpression we're a float
|
574
|
+
circ_param_sym = sym.Float(circuit_param)
|
575
575
|
equations.append(sym.Eq(template_param.sympify(), circ_param_sym))
|
576
576
|
|
577
577
|
for param in template_param.parameters:
|
@@ -147,7 +147,7 @@ class ALAPSchedule(BaseSchedulerTransform):
|
|
147
147
|
new_dag._calibrations_prop = dag._calibrations_prop
|
148
148
|
|
149
149
|
# set circuit duration and unit to indicate it is scheduled
|
150
|
-
new_dag.
|
151
|
-
new_dag.
|
150
|
+
new_dag._duration = circuit_duration
|
151
|
+
new_dag._unit = time_unit
|
152
152
|
|
153
153
|
return new_dag
|
@@ -129,7 +129,7 @@ class AlignMeasures(TransformationPass):
|
|
129
129
|
return dag
|
130
130
|
|
131
131
|
# if circuit is not yet scheduled, schedule with ALAP method
|
132
|
-
if dag.
|
132
|
+
if dag._duration is None:
|
133
133
|
raise TranspilerError(
|
134
134
|
f"This circuit {dag.name} may involve a delay instruction violating the "
|
135
135
|
"pulse controller alignment. To adjust instructions to "
|
@@ -201,8 +201,8 @@ class AlignMeasures(TransformationPass):
|
|
201
201
|
new_dag.metadata = dag.metadata
|
202
202
|
|
203
203
|
# set circuit duration and unit to indicate it is scheduled
|
204
|
-
new_dag.
|
205
|
-
new_dag.
|
204
|
+
new_dag._duration = circuit_duration
|
205
|
+
new_dag._unit = time_unit
|
206
206
|
|
207
207
|
return new_dag
|
208
208
|
|
@@ -170,6 +170,6 @@ class ASAPSchedule(BaseSchedulerTransform):
|
|
170
170
|
new_dag._calibrations_prop = dag._calibrations_prop
|
171
171
|
|
172
172
|
# set circuit duration and unit to indicate it is scheduled
|
173
|
-
new_dag.
|
174
|
-
new_dag.
|
173
|
+
new_dag._duration = circuit_duration
|
174
|
+
new_dag._unit = time_unit
|
175
175
|
return new_dag
|
@@ -168,7 +168,7 @@ class DynamicalDecoupling(TransformationPass):
|
|
168
168
|
if len(dag.qregs) != 1 or dag.qregs.get("q", None) is None:
|
169
169
|
raise TranspilerError("DD runs on physical circuits only.")
|
170
170
|
|
171
|
-
if dag.
|
171
|
+
if dag._duration is None:
|
172
172
|
raise TranspilerError("DD runs after circuit is scheduled.")
|
173
173
|
|
174
174
|
durations = self._update_inst_durations(dag)
|
@@ -98,7 +98,7 @@ class BasePadding(TransformationPass):
|
|
98
98
|
|
99
99
|
new_dag.name = dag.name
|
100
100
|
new_dag.metadata = dag.metadata
|
101
|
-
new_dag.
|
101
|
+
new_dag._unit = self.property_set["time_unit"]
|
102
102
|
new_dag._calibrations_prop = dag._calibrations_prop
|
103
103
|
new_dag.global_phase = dag.global_phase
|
104
104
|
|
@@ -161,7 +161,7 @@ class BasePadding(TransformationPass):
|
|
161
161
|
prev_node=prev_node,
|
162
162
|
)
|
163
163
|
|
164
|
-
new_dag.
|
164
|
+
new_dag._duration = circuit_duration
|
165
165
|
|
166
166
|
return new_dag
|
167
167
|
|
@@ -348,14 +348,14 @@ class PadDynamicalDecoupling(BasePadding):
|
|
348
348
|
|
349
349
|
if not self.__is_dd_qubit(dag.qubits.index(qubit)):
|
350
350
|
# Target physical qubit is not the target of this DD sequence.
|
351
|
-
self._apply_scheduled_op(dag, t_start, Delay(time_interval, dag.
|
351
|
+
self._apply_scheduled_op(dag, t_start, Delay(time_interval, dag._unit), qubit)
|
352
352
|
return
|
353
353
|
|
354
354
|
if self._skip_reset_qubits and (
|
355
355
|
isinstance(prev_node, DAGInNode) or isinstance(prev_node.op, Reset)
|
356
356
|
):
|
357
357
|
# Previous node is the start edge or reset, i.e. qubit is ground state.
|
358
|
-
self._apply_scheduled_op(dag, t_start, Delay(time_interval, dag.
|
358
|
+
self._apply_scheduled_op(dag, t_start, Delay(time_interval, dag._unit), qubit)
|
359
359
|
return
|
360
360
|
|
361
361
|
slack = time_interval - np.sum(self._dd_sequence_lengths[qubit])
|
@@ -363,7 +363,7 @@ class PadDynamicalDecoupling(BasePadding):
|
|
363
363
|
|
364
364
|
if slack <= 0:
|
365
365
|
# Interval too short.
|
366
|
-
self._apply_scheduled_op(dag, t_start, Delay(time_interval, dag.
|
366
|
+
self._apply_scheduled_op(dag, t_start, Delay(time_interval, dag._unit), qubit)
|
367
367
|
return
|
368
368
|
|
369
369
|
if len(self._dd_sequence) == 1:
|
@@ -389,7 +389,7 @@ class PadDynamicalDecoupling(BasePadding):
|
|
389
389
|
sequence_gphase += phase
|
390
390
|
else:
|
391
391
|
# Don't do anything if there's no single-qubit gate to absorb the inverse
|
392
|
-
self._apply_scheduled_op(dag, t_start, Delay(time_interval, dag.
|
392
|
+
self._apply_scheduled_op(dag, t_start, Delay(time_interval, dag._unit), qubit)
|
393
393
|
return
|
394
394
|
|
395
395
|
def _constrained_length(values):
|
@@ -425,7 +425,7 @@ class PadDynamicalDecoupling(BasePadding):
|
|
425
425
|
if dd_ind < len(taus):
|
426
426
|
tau = taus[dd_ind]
|
427
427
|
if tau > 0:
|
428
|
-
self._apply_scheduled_op(dag, idle_after, Delay(tau, dag.
|
428
|
+
self._apply_scheduled_op(dag, idle_after, Delay(tau, dag._unit), qubit)
|
429
429
|
idle_after += tau
|
430
430
|
if dd_ind < len(self._dd_sequence):
|
431
431
|
gate = self._dd_sequence[dd_ind]
|
@@ -201,7 +201,7 @@ class SolovayKitaevSynthesis(UnitarySynthesisPlugin):
|
|
201
201
|
|
202
202
|
Supported parameters in the dictionary:
|
203
203
|
|
204
|
-
|
204
|
+
basic_approximations (str | dict):
|
205
205
|
The basic approximations for the finding the best discrete decomposition at the root of the
|
206
206
|
recursion. If a string, it specifies the ``.npy`` file to load the approximations from.
|
207
207
|
If a dictionary, it contains ``{label: SO(3)-matrix}`` pairs. If None, a default based on
|
@@ -209,19 +209,31 @@ class SolovayKitaevSynthesis(UnitarySynthesisPlugin):
|
|
209
209
|
|
210
210
|
basis_gates (list):
|
211
211
|
A list of strings specifying the discrete basis gates to decompose to. If None,
|
212
|
-
defaults to ``["h", "t", "tdg"]``.
|
212
|
+
it defaults to ``["h", "t", "tdg"]``. If ``basic_approximations`` is not None,
|
213
|
+
``basis_set`` is required to correspond to the basis set that was used to
|
214
|
+
generate it.
|
213
215
|
|
214
216
|
depth (int):
|
215
217
|
The gate-depth of the basic approximations. All possible, unique combinations of the
|
216
218
|
basis gates up to length ``depth`` are considered. If None, defaults to 10.
|
219
|
+
If ``basic_approximations`` is not None, ``depth`` is required to correspond to the
|
220
|
+
depth that was used to generate it.
|
217
221
|
|
218
222
|
recursion_degree (int):
|
219
223
|
The number of times the decomposition is recursively improved. If None, defaults to 3.
|
220
224
|
"""
|
221
225
|
|
222
|
-
#
|
223
|
-
#
|
226
|
+
# Generating basic approximations of single-qubit gates is computationally expensive.
|
227
|
+
# We cache the instance of the Solovay-Kitaev class (which contains the approximations),
|
228
|
+
# as well as the basis gates and the depth (used to generate it).
|
229
|
+
# When the plugin is called again, we check if the specified basis gates and depth are
|
230
|
+
# the same as before. If so, the stored basic approximations are reused, and if not, the
|
231
|
+
# approximations are re-generated. In practice (when the plugin is run as a part of the
|
232
|
+
# UnitarySynthesis transpiler pass), the basis gates and the depth do not change, and
|
233
|
+
# basic approximations are not re-generated.
|
224
234
|
_sk = None
|
235
|
+
_basis_gates = None
|
236
|
+
_depth = None
|
225
237
|
|
226
238
|
@property
|
227
239
|
def max_qubits(self):
|
@@ -271,27 +283,25 @@ class SolovayKitaevSynthesis(UnitarySynthesisPlugin):
|
|
271
283
|
return False
|
272
284
|
|
273
285
|
def run(self, unitary, **options):
|
286
|
+
"""Run the SolovayKitaevSynthesis synthesis plugin on the given unitary."""
|
274
287
|
|
275
|
-
# Runtime imports to avoid the overhead of these imports for
|
276
|
-
# plugin discovery and only use them if the plugin is run/used
|
277
288
|
config = options.get("config") or {}
|
278
|
-
|
289
|
+
basis_gates = options.get("basis_gates", ["h", "t", "tdg"])
|
290
|
+
depth = config.get("depth", 10)
|
291
|
+
basic_approximations = config.get("basic_approximations", None)
|
279
292
|
recursion_degree = config.get("recursion_degree", 3)
|
280
293
|
|
281
|
-
# if we didn't yet construct the Solovay-Kitaev instance
|
282
|
-
# the basic approximations
|
283
|
-
if SolovayKitaevSynthesis._sk is None
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
# if the basic approximations are not generated and not given,
|
288
|
-
# try to generate them if the basis set is specified
|
294
|
+
# Check if we didn't yet construct the Solovay-Kitaev instance (which contains the basic
|
295
|
+
# approximations) or if the basic approximations need need to be recomputed.
|
296
|
+
if (SolovayKitaevSynthesis._sk is None) or (
|
297
|
+
(basis_gates != SolovayKitaevSynthesis._basis_gates)
|
298
|
+
or (depth != SolovayKitaevSynthesis._depth)
|
299
|
+
):
|
289
300
|
if basic_approximations is None:
|
290
|
-
depth = config.get("depth", 10)
|
291
301
|
basic_approximations = generate_basic_approximations(basis_gates, depth)
|
292
302
|
|
303
|
+
SolovayKitaevSynthesis._basis_gates = basis_gates
|
304
|
+
SolovayKitaevSynthesis._depth = depth
|
293
305
|
SolovayKitaevSynthesis._sk = SolovayKitaevDecomposition(basic_approximations)
|
294
|
-
|
295
306
|
approximate_circuit = SolovayKitaevSynthesis._sk.run(unitary, recursion_degree)
|
296
|
-
|
297
|
-
return dag_circuit
|
307
|
+
return circuit_to_dag(approximate_circuit)
|
@@ -254,7 +254,7 @@ class DrawerCanvas:
|
|
254
254
|
self.add_data(datum)
|
255
255
|
|
256
256
|
# update time range
|
257
|
-
t_end = max(program.
|
257
|
+
t_end = max(program._duration, self.formatter["margin.minimum_duration"])
|
258
258
|
self.set_time_range(t_start=0, t_end=t_end)
|
259
259
|
|
260
260
|
def set_time_range(self, t_start: int, t_end: int):
|
@@ -1,6 +1,6 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.4
|
2
2
|
Name: qiskit
|
3
|
-
Version: 1.4.
|
3
|
+
Version: 1.4.3
|
4
4
|
Summary: An open-source SDK for working with quantum computers at the level of extended quantum circuits, operators, and primitives.
|
5
5
|
Author-email: Qiskit Development Team <qiskit@us.ibm.com>
|
6
6
|
License: Apache 2.0
|
@@ -51,6 +51,7 @@ Provides-Extra: csp-layout-pass
|
|
51
51
|
Requires-Dist: python-constraint>=1.4; extra == "csp-layout-pass"
|
52
52
|
Provides-Extra: all
|
53
53
|
Requires-Dist: qiskit[crosstalk-pass,csp-layout-pass,qasm3-import,visualization]; extra == "all"
|
54
|
+
Dynamic: license-file
|
54
55
|
|
55
56
|
# Qiskit
|
56
57
|
|
@@ -121,7 +122,7 @@ from qiskit.primitives import StatevectorSampler
|
|
121
122
|
sampler = StatevectorSampler()
|
122
123
|
job = sampler.run([qc_measured], shots=1000)
|
123
124
|
result = job.result()
|
124
|
-
print(f" > Counts: {result[0].data[
|
125
|
+
print(f" > Counts: {result[0].data['meas'].get_counts()}")
|
125
126
|
```
|
126
127
|
Running this will give an outcome similar to `{'000': 497, '111': 503}` which is `000` 50% of the time and `111` 50% of the time up to statistical fluctuations.
|
127
128
|
To illustrate the power of the Estimator, we now use the quantum information toolbox to create the operator $XXY+XYX+YXX-YYY$ and pass it to the `run()` function, along with our quantum circuit. Note that the Estimator requires a circuit _**without**_ measurements, so we use the `qc` circuit we created earlier.
|