iqm-pulse 9.21.0__py3-none-any.whl → 10.1.0__py3-none-any.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.
- iqm/pulse/builder.py +188 -89
- iqm/pulse/circuit_operations.py +0 -5
- iqm/pulse/gate_implementation.py +242 -100
- iqm/pulse/gates/__init__.py +67 -183
- iqm/pulse/gates/conditional.py +3 -1
- iqm/pulse/gates/cz.py +7 -15
- iqm/pulse/gates/default_gates.py +17 -117
- iqm/pulse/gates/flux_multiplexer.py +2 -2
- iqm/pulse/gates/measure.py +46 -41
- iqm/pulse/gates/prx.py +8 -5
- iqm/pulse/gates/reset.py +8 -16
- iqm/pulse/gates/rz.py +1 -1
- iqm/pulse/gates/sx.py +1 -1
- iqm/pulse/gates/u.py +1 -1
- iqm/pulse/playlist/instructions.py +7 -7
- iqm/pulse/playlist/waveforms.py +29 -1
- iqm/pulse/quantum_ops.py +29 -31
- iqm/pulse/utils.py +0 -92
- {iqm_pulse-9.21.0.dist-info → iqm_pulse-10.1.0.dist-info}/METADATA +1 -1
- {iqm_pulse-9.21.0.dist-info → iqm_pulse-10.1.0.dist-info}/RECORD +23 -23
- {iqm_pulse-9.21.0.dist-info → iqm_pulse-10.1.0.dist-info}/LICENSE.txt +0 -0
- {iqm_pulse-9.21.0.dist-info → iqm_pulse-10.1.0.dist-info}/WHEEL +0 -0
- {iqm_pulse-9.21.0.dist-info → iqm_pulse-10.1.0.dist-info}/top_level.txt +0 -0
iqm/pulse/gates/__init__.py
CHANGED
|
@@ -24,6 +24,7 @@ Likewise, a single GateImplementation subclass can be sometimes used to implemen
|
|
|
24
24
|
through different calibration data.
|
|
25
25
|
"""
|
|
26
26
|
|
|
27
|
+
import copy
|
|
27
28
|
from dataclasses import replace
|
|
28
29
|
|
|
29
30
|
from iqm.pulse.gate_implementation import GateImplementation
|
|
@@ -44,7 +45,7 @@ from iqm.pulse.gates.cz import (
|
|
|
44
45
|
FluxPulseGate_CRF_CRF,
|
|
45
46
|
FluxPulseGate_TGSS_CRF,
|
|
46
47
|
)
|
|
47
|
-
from iqm.pulse.gates.default_gates import _quantum_ops_library
|
|
48
|
+
from iqm.pulse.gates.default_gates import _default_implementations, _quantum_ops_library
|
|
48
49
|
from iqm.pulse.gates.delay import Delay
|
|
49
50
|
from iqm.pulse.gates.flux_multiplexer import FluxMultiplexer_SampleLinear
|
|
50
51
|
from iqm.pulse.gates.measure import Measure_Constant, Measure_Constant_Qnd, Shelved_Measure_Constant
|
|
@@ -75,7 +76,7 @@ from iqm.pulse.gates.u import UGate, get_unitary_u
|
|
|
75
76
|
from iqm.pulse.quantum_ops import QuantumOp, QuantumOpTable
|
|
76
77
|
|
|
77
78
|
_exposed_implementations: dict[str, type[GateImplementation]] = {
|
|
78
|
-
cls.__name__: cls
|
|
79
|
+
cls.__name__: cls
|
|
79
80
|
for cls in (
|
|
80
81
|
Barrier,
|
|
81
82
|
Constant_PRX_with_smooth_rise_fall,
|
|
@@ -120,7 +121,7 @@ _exposed_implementations: dict[str, type[GateImplementation]] = {
|
|
|
120
121
|
|
|
121
122
|
def get_implementation_class(class_name: str) -> type[GateImplementation] | None:
|
|
122
123
|
"""Get gate implementation class by class name."""
|
|
123
|
-
return _exposed_implementations.get(class_name
|
|
124
|
+
return _exposed_implementations.get(class_name)
|
|
124
125
|
|
|
125
126
|
|
|
126
127
|
def expose_implementation(implementation: type[GateImplementation], overwrite: bool = False) -> None:
|
|
@@ -138,219 +139,102 @@ def expose_implementation(implementation: type[GateImplementation], overwrite: b
|
|
|
138
139
|
_exposed_implementations[name] = implementation
|
|
139
140
|
|
|
140
141
|
|
|
141
|
-
def
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
:
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
op1: First QuantumOp instance
|
|
148
|
-
op2: Second QuantumOp instance
|
|
149
|
-
|
|
150
|
-
Returns:
|
|
151
|
-
True if the operations have identical parameters, False otherwise
|
|
152
|
-
|
|
153
|
-
"""
|
|
154
|
-
IGNORED_FIELDS = {"implementations", "defaults_for_locus", "unitary"}
|
|
155
|
-
op1_dict = vars(op1)
|
|
156
|
-
op2_dict = vars(op2)
|
|
157
|
-
return all(
|
|
158
|
-
(op1_dict[field] == op2_dict[field] or (field == "params" and tuple(op1_dict[field]) == tuple(op2_dict[field])))
|
|
159
|
-
for field in op1_dict
|
|
160
|
-
if field not in IGNORED_FIELDS
|
|
161
|
-
)
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
def _validate_operation(
|
|
165
|
-
new_op: QuantumOp,
|
|
166
|
-
gate_name: str,
|
|
167
|
-
operations: QuantumOpTable,
|
|
168
|
-
overwrite: bool = False,
|
|
169
|
-
) -> QuantumOp:
|
|
170
|
-
"""Validate new operation against existing operations and set unitary if needed.
|
|
142
|
+
def _validate_implementation(
|
|
143
|
+
op_name: str,
|
|
144
|
+
impl_name: str,
|
|
145
|
+
impl_class_name: str,
|
|
146
|
+
) -> None:
|
|
147
|
+
"""Check that canonical implementation names cannot be overridden.
|
|
171
148
|
|
|
172
149
|
Args:
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
overwrite: Whether to allow overwriting existing operations
|
|
177
|
-
|
|
178
|
-
Returns:
|
|
179
|
-
Validated QuantumOp with unitary set if needed
|
|
150
|
+
op_name: Name of the operation.
|
|
151
|
+
impl_name: Name of the new implementation.
|
|
152
|
+
impl_class_name: Name of the GateImplementation class it maps to.
|
|
180
153
|
|
|
181
154
|
Raises:
|
|
182
|
-
ValueError:
|
|
183
|
-
|
|
184
|
-
"""
|
|
185
|
-
if not overwrite and gate_name in operations:
|
|
186
|
-
old = operations.get(gate_name)
|
|
187
|
-
same = _compare_operations(new_op, old) # type: ignore[arg-type]
|
|
188
|
-
if not same:
|
|
189
|
-
raise ValueError(f"{gate_name} already registered with different parameters")
|
|
190
|
-
|
|
191
|
-
if gate_name in _quantum_ops_library:
|
|
192
|
-
default = _quantum_ops_library.get(gate_name)
|
|
193
|
-
same = _compare_operations(new_op, default) # type: ignore[arg-type]
|
|
194
|
-
if not same:
|
|
195
|
-
raise ValueError(f"{gate_name} conflicts with a canonical operation in iqm-pulse")
|
|
196
|
-
|
|
197
|
-
if new_op.unitary is None:
|
|
198
|
-
if gate_name in operations and operations[gate_name].unitary is not None:
|
|
199
|
-
unitary = operations[gate_name].unitary
|
|
200
|
-
elif gate_name in _quantum_ops_library and _quantum_ops_library[gate_name].unitary is not None:
|
|
201
|
-
unitary = default.unitary # type: ignore[union-attr]
|
|
202
|
-
else:
|
|
203
|
-
unitary = None
|
|
204
|
-
new_op = replace(new_op, unitary=unitary)
|
|
205
|
-
|
|
206
|
-
return new_op
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
def _register_gate(
|
|
210
|
-
operations: QuantumOpTable,
|
|
211
|
-
gate_name: str,
|
|
212
|
-
impl_class: type[GateImplementation],
|
|
213
|
-
quantum_op_specs: QuantumOp | dict | None = None,
|
|
214
|
-
) -> QuantumOp:
|
|
215
|
-
"""Create the quantum operation for the new gate.
|
|
216
|
-
|
|
217
|
-
Args:
|
|
218
|
-
operations: Known operations, mapping gate names to QuantumOps
|
|
219
|
-
gate_name: Name of the gate to register
|
|
220
|
-
impl_class: The implementation class
|
|
221
|
-
quantum_op_specs: The quantum operation specifications
|
|
222
|
-
|
|
223
|
-
Returns:
|
|
224
|
-
The instance of the new quantum operation
|
|
155
|
+
ValueError: A canonical implementation name is being redefined.
|
|
225
156
|
|
|
226
157
|
"""
|
|
227
|
-
|
|
228
|
-
new_op = quantum_op_specs
|
|
229
|
-
elif quantum_op_specs is None and gate_name in operations:
|
|
230
|
-
new_op = operations[gate_name]
|
|
231
|
-
else:
|
|
232
|
-
new_kwargs = {
|
|
233
|
-
"name": gate_name,
|
|
234
|
-
"arity": 1,
|
|
235
|
-
"params": tuple(),
|
|
236
|
-
"implementations": {},
|
|
237
|
-
"symmetric": impl_class.symmetric,
|
|
238
|
-
"factorizable": False,
|
|
239
|
-
}
|
|
240
|
-
if quantum_op_specs:
|
|
241
|
-
new_kwargs |= quantum_op_specs
|
|
242
|
-
new_kwargs["params"] = tuple(new_kwargs.get("params", ())) # type: ignore[arg-type]
|
|
158
|
+
default_implementations = _default_implementations.get(op_name, {})
|
|
243
159
|
|
|
244
|
-
|
|
160
|
+
# check if the implementation name is canonical for this op
|
|
161
|
+
if (impl_class := default_implementations.get(impl_name)) is not None:
|
|
162
|
+
if impl_class_name != impl_class.__name__:
|
|
163
|
+
raise ValueError(
|
|
164
|
+
f"'{op_name}': '{impl_name}' is a reserved implementation name that refers to "
|
|
165
|
+
f"'{impl_class.__name__}', and cannot be overridden. "
|
|
166
|
+
"Consider renaming your implementation."
|
|
167
|
+
)
|
|
245
168
|
|
|
246
|
-
return new_op
|
|
247
169
|
|
|
248
|
-
|
|
249
|
-
def _add_implementation(
|
|
170
|
+
def register_operation(
|
|
250
171
|
operations: dict[str, QuantumOp],
|
|
251
|
-
|
|
252
|
-
impl_name: str,
|
|
253
|
-
impl_class: type[GateImplementation],
|
|
254
|
-
set_as_default: bool = False,
|
|
172
|
+
op: QuantumOp,
|
|
255
173
|
overwrite: bool = False,
|
|
256
174
|
) -> None:
|
|
257
|
-
"""Register a new
|
|
258
|
-
|
|
259
|
-
Args:
|
|
260
|
-
operations: Table of existing quantum operations
|
|
261
|
-
new_op: The new quantum operation
|
|
262
|
-
impl_name: The name for the implementation that is added
|
|
263
|
-
impl_class: The GateImplementation class corresponding to the new implementation
|
|
264
|
-
set_as_default: Whether to set as default implementation
|
|
265
|
-
overwrite: If True, allows replacing existing implementation
|
|
266
|
-
|
|
267
|
-
Returns:
|
|
268
|
-
new_op: QuantumOp with the new implementation
|
|
269
|
-
|
|
270
|
-
"""
|
|
271
|
-
new_op.implementations[impl_name] = impl_class
|
|
272
|
-
_validate_implementation(operations, new_op, impl_name, impl_class)
|
|
273
|
-
if set_as_default and len(new_op.implementations) >= 1:
|
|
274
|
-
new_op.set_default_implementation(impl_name)
|
|
275
|
-
if not get_implementation_class(impl_class.__name__):
|
|
276
|
-
expose_implementation(impl_class, overwrite)
|
|
277
|
-
|
|
278
|
-
return new_op # type: ignore[return-value]
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
def _validate_implementation(
|
|
282
|
-
operations: QuantumOpTable,
|
|
283
|
-
new_op: QuantumOp,
|
|
284
|
-
impl_name: str,
|
|
285
|
-
impl_class: type[GateImplementation],
|
|
286
|
-
) -> None:
|
|
287
|
-
"""Validate new implementation against existing implementations.
|
|
175
|
+
"""Register a new QuantumOp to the given operations table.
|
|
288
176
|
|
|
289
177
|
Args:
|
|
290
|
-
operations:
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
impl_class: The GateImplementation class corresponding to the new
|
|
294
|
-
implementation
|
|
178
|
+
operations: Known operations, to which the new operation is added.
|
|
179
|
+
op: Definition for the new operation.
|
|
180
|
+
overwrite: If True, allows replacing an existing operation in ``operations``.
|
|
295
181
|
|
|
296
182
|
Raises:
|
|
297
|
-
ValueError:
|
|
298
|
-
|
|
183
|
+
ValueError: ``op.name`` exists in ``operations`` and ``overwrite==False``.
|
|
184
|
+
ValueError: ``op.name`` is the name of a canonical operation in iqm-pulse.
|
|
299
185
|
|
|
300
186
|
"""
|
|
301
|
-
if
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
if impl_class.__name__ != default_impl_cls_name:
|
|
309
|
-
raise ValueError(
|
|
310
|
-
f"The implementation '{default_impl_name}' already exists in default implementations with '{default_impl_cls.__name__}' as corresponding GateImplementation class." # noqa: E501
|
|
311
|
-
)
|
|
312
|
-
|
|
313
|
-
if new_op.name in operations:
|
|
314
|
-
existing_op = operations[new_op.name]
|
|
315
|
-
for existing_impl_name, existing_impl_cls in existing_op.implementations.items():
|
|
316
|
-
if impl_name == existing_impl_name:
|
|
317
|
-
existing_impl_cls_name = (
|
|
318
|
-
existing_impl_cls if isinstance(existing_impl_cls, str) else existing_impl_cls.__name__
|
|
319
|
-
) # noqa: E501
|
|
320
|
-
if impl_class.__name__ != existing_impl_cls_name:
|
|
321
|
-
raise ValueError(
|
|
322
|
-
f"The implementation '{existing_impl_name}' already exists with '{existing_impl_cls.__name__}' as corresponding GateImplementation class." # noqa: E501
|
|
323
|
-
)
|
|
187
|
+
if op.name in operations and not overwrite:
|
|
188
|
+
raise ValueError(f"'{op.name}' already registered.")
|
|
189
|
+
if op.name in _quantum_ops_library:
|
|
190
|
+
raise ValueError(f"'{op.name}' conflicts with a canonical operation in iqm-pulse. Use a different name.")
|
|
191
|
+
|
|
192
|
+
# make a deep copy since the dicts inside QuantumOp are mutable
|
|
193
|
+
operations[op.name] = copy.deepcopy(op)
|
|
324
194
|
|
|
325
195
|
|
|
326
196
|
def register_implementation(
|
|
327
197
|
operations: dict[str, QuantumOp],
|
|
328
|
-
|
|
198
|
+
op_name: str,
|
|
329
199
|
impl_name: str,
|
|
330
200
|
impl_class: type[GateImplementation],
|
|
201
|
+
*,
|
|
331
202
|
set_as_default: bool = False,
|
|
332
203
|
overwrite: bool = False,
|
|
333
|
-
quantum_op_specs: QuantumOp | dict | None = None,
|
|
334
204
|
) -> None:
|
|
335
|
-
"""Register a new
|
|
205
|
+
"""Register a new GateImplementation.
|
|
336
206
|
|
|
337
207
|
Args:
|
|
338
|
-
operations: Known operations,
|
|
339
|
-
|
|
340
|
-
impl_name:
|
|
341
|
-
impl_class:
|
|
342
|
-
set_as_default: Whether to set as default implementation
|
|
343
|
-
overwrite: If True, allows replacing existing
|
|
344
|
-
quantum_op_specs: Specs for creating new quantum op if needed
|
|
208
|
+
operations: Known operations, to which the new implementation is added.
|
|
209
|
+
op_name: Name of the operation under which the implementation is registered.
|
|
210
|
+
impl_name: Name of the implementation to register.
|
|
211
|
+
impl_class: Implementation class to register.
|
|
212
|
+
set_as_default: Whether to set as the default implementation for ``op_name``.
|
|
213
|
+
overwrite: If True, allows replacing an existing implementation.
|
|
345
214
|
|
|
346
215
|
Raises:
|
|
347
|
-
ValueError:
|
|
216
|
+
ValueError: ``op_name`` does not exist in ``operations``.
|
|
217
|
+
ValueError: The implementation exists and ``overwrite==False``.
|
|
218
|
+
|
|
348
219
|
|
|
349
220
|
"""
|
|
350
|
-
|
|
221
|
+
if (op := operations.get(op_name)) is None:
|
|
222
|
+
raise ValueError(f"Operation '{op_name}' is not known, register it first.")
|
|
351
223
|
|
|
352
|
-
|
|
224
|
+
# canonical implementation names must not be overridden
|
|
225
|
+
_validate_implementation(op_name, impl_name, impl_class.__name__)
|
|
226
|
+
|
|
227
|
+
# only overwrite existing implementations with permission
|
|
228
|
+
if (old_class := op.implementations.get(impl_name)) is not None and not overwrite:
|
|
229
|
+
if old_class is not impl_class:
|
|
230
|
+
# cannot change an existing implementation name
|
|
231
|
+
raise ValueError(f"'{op_name}' already has an implementation named '{impl_name}'.")
|
|
232
|
+
else:
|
|
233
|
+
# add the new implementation
|
|
234
|
+
op.implementations[impl_name] = impl_class
|
|
353
235
|
|
|
354
|
-
|
|
236
|
+
if set_as_default:
|
|
237
|
+
op.set_default_implementation(impl_name)
|
|
355
238
|
|
|
356
|
-
|
|
239
|
+
if get_implementation_class(impl_class.__name__) is None:
|
|
240
|
+
expose_implementation(impl_class, overwrite=overwrite)
|
iqm/pulse/gates/conditional.py
CHANGED
|
@@ -32,6 +32,8 @@ class CCPRX_Composite(CompositeGate):
|
|
|
32
32
|
Uses the default implementation of PRX underneath, so no extra calibration is needed.
|
|
33
33
|
"""
|
|
34
34
|
|
|
35
|
+
registered_gates = ("prx",)
|
|
36
|
+
|
|
35
37
|
parameters = {"control_delays": Parameter("", "Control delays", "s", collection_type=CollectionType.NDARRAY)}
|
|
36
38
|
"""``control_delays`` contains the times it takes for the classical control signal from each
|
|
37
39
|
probe line (readout instrument) to become usable for the drive AWG implementing the PRX gate.
|
|
@@ -81,7 +83,7 @@ class CCPRX_Composite(CompositeGate):
|
|
|
81
83
|
pulse_instruction = ConditionalInstruction(
|
|
82
84
|
duration=pulse.duration, condition=default_label, outcomes=(wait, pulse)
|
|
83
85
|
)
|
|
84
|
-
delays = self.
|
|
86
|
+
delays = self.calibration_data["control_delays"]
|
|
85
87
|
if len(delays) == 0:
|
|
86
88
|
raise ValueError(f"'control_delays' for '{self.name}' on {qubit} is empty (not calibrated).")
|
|
87
89
|
|
iqm/pulse/gates/cz.py
CHANGED
|
@@ -100,7 +100,7 @@ class FluxPulseGate(GateImplementation):
|
|
|
100
100
|
builder: ScheduleBuilder,
|
|
101
101
|
):
|
|
102
102
|
super().__init__(parent, name, locus, calibration_data, builder)
|
|
103
|
-
duration = calibration_data["duration"]
|
|
103
|
+
duration = calibration_data["duration"] # shared between all pulses
|
|
104
104
|
flux_pulses = {}
|
|
105
105
|
|
|
106
106
|
def build_flux_pulse(waveform_class: type[Waveform], component_name: str, cal_node_name: str) -> None:
|
|
@@ -110,14 +110,11 @@ class FluxPulseGate(GateImplementation):
|
|
|
110
110
|
calibration_data[cal_node_name],
|
|
111
111
|
self.parameters[cal_node_name], # type: ignore[arg-type]
|
|
112
112
|
builder.channels[flux_channel],
|
|
113
|
-
duration,
|
|
113
|
+
duration=duration,
|
|
114
114
|
)
|
|
115
|
-
# TODO convert_calibration_data should be able to do this too
|
|
116
|
-
n_samples = builder.channels[flux_channel].duration_to_int_samples(duration) if duration > 0 else 0
|
|
117
|
-
params["n_samples"] = n_samples
|
|
118
115
|
amplitude = params.pop("amplitude")
|
|
119
116
|
flux_pulses[flux_channel] = FluxPulse(
|
|
120
|
-
duration=n_samples,
|
|
117
|
+
duration=params["n_samples"],
|
|
121
118
|
wave=waveform_class(**params),
|
|
122
119
|
scale=amplitude,
|
|
123
120
|
)
|
|
@@ -283,8 +280,7 @@ class CouplerFluxPulseQubitACStarkPulseGate(GateImplementation):
|
|
|
283
280
|
builder: ScheduleBuilder,
|
|
284
281
|
):
|
|
285
282
|
super().__init__(parent, name, locus, calibration_data, builder)
|
|
286
|
-
|
|
287
|
-
duration = calibration_data["duration"]
|
|
283
|
+
duration = calibration_data["duration"] # shared between all pulses
|
|
288
284
|
flux_pulses = {}
|
|
289
285
|
qubit_drive_pulses = {}
|
|
290
286
|
rz = calibration_data["rz"]
|
|
@@ -296,13 +292,11 @@ class CouplerFluxPulseQubitACStarkPulseGate(GateImplementation):
|
|
|
296
292
|
calibration_data[cal_node_name],
|
|
297
293
|
self.parameters[cal_node_name], # type: ignore[arg-type]
|
|
298
294
|
builder.channels[flux_channel],
|
|
299
|
-
duration,
|
|
295
|
+
duration=duration,
|
|
300
296
|
)
|
|
301
|
-
n_samples = builder.channels[flux_channel].duration_to_int_samples(duration) if duration > 0 else 0
|
|
302
|
-
params["n_samples"] = n_samples
|
|
303
297
|
amplitude = params.pop("amplitude")
|
|
304
298
|
flux_pulses[flux_channel] = FluxPulse(
|
|
305
|
-
duration=n_samples,
|
|
299
|
+
duration=params["n_samples"],
|
|
306
300
|
wave=waveform_class(**params),
|
|
307
301
|
scale=amplitude,
|
|
308
302
|
)
|
|
@@ -314,10 +308,8 @@ class CouplerFluxPulseQubitACStarkPulseGate(GateImplementation):
|
|
|
314
308
|
calibration_data[cal_node_name],
|
|
315
309
|
self.parameters[cal_node_name], # type: ignore[arg-type]
|
|
316
310
|
builder.channels[drive_channel],
|
|
317
|
-
duration,
|
|
311
|
+
duration=duration,
|
|
318
312
|
)
|
|
319
|
-
n_samples = builder.channels[drive_channel].duration_to_int_samples(duration) if duration > 0 else 0
|
|
320
|
-
params["n_samples"] = n_samples
|
|
321
313
|
params["phase_increment"] = rz[component_name]
|
|
322
314
|
qubit_drive_pulses[drive_channel] = self._ac_stark_pulse(**params)
|
|
323
315
|
|
iqm/pulse/gates/default_gates.py
CHANGED
|
@@ -11,7 +11,11 @@
|
|
|
11
11
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
|
-
"""
|
|
14
|
+
"""Canonical quantum operations and implementations provided by iqm-pulse."""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from typing import TYPE_CHECKING
|
|
15
19
|
|
|
16
20
|
import numpy as np
|
|
17
21
|
|
|
@@ -53,11 +57,13 @@ from iqm.pulse.gates.rz import (
|
|
|
53
57
|
)
|
|
54
58
|
from iqm.pulse.gates.sx import SXGate
|
|
55
59
|
from iqm.pulse.gates.u import UGate, get_unitary_u
|
|
56
|
-
from iqm.pulse.quantum_ops import QuantumOp
|
|
60
|
+
from iqm.pulse.quantum_ops import QuantumOp
|
|
57
61
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
62
|
+
if TYPE_CHECKING:
|
|
63
|
+
from iqm.pulse.gate_implementation import GateImplementation
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
_default_implementations: dict[str, dict[str, type[GateImplementation]]] = {
|
|
61
67
|
"barrier": {"": Barrier},
|
|
62
68
|
"delay": {"wait": Delay},
|
|
63
69
|
"measure": {
|
|
@@ -99,119 +105,10 @@ _default_implementations = {
|
|
|
99
105
|
"reset_wait": {"reset_wait": Reset_Wait},
|
|
100
106
|
"flux_multiplexer": {"sample_linear": FluxMultiplexer_SampleLinear},
|
|
101
107
|
}
|
|
108
|
+
"""Canonical implementation names for the canonical quantum operations.
|
|
109
|
+
A collection of mappings between default implementation names and their GateImplementation classes,
|
|
110
|
+
for different gates."""
|
|
102
111
|
|
|
103
|
-
"""A table of quantum operations (`_default_operations`) defining characteristics:
|
|
104
|
-
- Number of qubits involved (arity)
|
|
105
|
-
- Required parameters
|
|
106
|
-
- GateImplementation classes
|
|
107
|
-
- Unitary matrices where applicable
|
|
108
|
-
- Properties like symmetry and factorizability
|
|
109
|
-
This table is here so that we retain information about operations, even though they might
|
|
110
|
-
be deleted in the future.
|
|
111
|
-
"""
|
|
112
|
-
_default_operations: QuantumOpTable = {
|
|
113
|
-
op.name: op
|
|
114
|
-
for op in [
|
|
115
|
-
QuantumOp(
|
|
116
|
-
"barrier",
|
|
117
|
-
0,
|
|
118
|
-
implementations=_default_implementations["barrier"], # type: ignore[arg-type]
|
|
119
|
-
symmetric=True,
|
|
120
|
-
),
|
|
121
|
-
QuantumOp(
|
|
122
|
-
"delay",
|
|
123
|
-
0,
|
|
124
|
-
("duration",),
|
|
125
|
-
implementations=_default_implementations["delay"], # type: ignore[arg-type]
|
|
126
|
-
symmetric=True,
|
|
127
|
-
),
|
|
128
|
-
QuantumOp(
|
|
129
|
-
"measure",
|
|
130
|
-
0,
|
|
131
|
-
("key",),
|
|
132
|
-
implementations=_default_implementations["measure"], # type: ignore[arg-type]
|
|
133
|
-
factorizable=True,
|
|
134
|
-
),
|
|
135
|
-
QuantumOp(
|
|
136
|
-
"prx",
|
|
137
|
-
1,
|
|
138
|
-
("angle", "phase"),
|
|
139
|
-
implementations=_default_implementations["prx"], # type: ignore[arg-type]
|
|
140
|
-
unitary=get_unitary_prx,
|
|
141
|
-
),
|
|
142
|
-
QuantumOp(
|
|
143
|
-
"prx_12",
|
|
144
|
-
1,
|
|
145
|
-
("angle", "phase"),
|
|
146
|
-
implementations=_default_implementations["prx_12"], # type: ignore[arg-type]
|
|
147
|
-
),
|
|
148
|
-
QuantumOp(
|
|
149
|
-
"u",
|
|
150
|
-
1,
|
|
151
|
-
("theta", "phi", "lam"),
|
|
152
|
-
implementations=_default_implementations["u"], # type: ignore[arg-type]
|
|
153
|
-
unitary=get_unitary_u,
|
|
154
|
-
),
|
|
155
|
-
QuantumOp(
|
|
156
|
-
"sx",
|
|
157
|
-
1,
|
|
158
|
-
implementations=_default_implementations["sx"], # type: ignore[arg-type]
|
|
159
|
-
unitary=lambda: get_unitary_prx(np.pi / 2, 0),
|
|
160
|
-
),
|
|
161
|
-
QuantumOp(
|
|
162
|
-
"rz",
|
|
163
|
-
1,
|
|
164
|
-
("angle",),
|
|
165
|
-
implementations=_default_implementations["rz"], # type: ignore[arg-type]
|
|
166
|
-
unitary=get_unitary_rz,
|
|
167
|
-
),
|
|
168
|
-
QuantumOp(
|
|
169
|
-
"rz_physical",
|
|
170
|
-
1,
|
|
171
|
-
implementations=_default_implementations["rz_physical"], # type: ignore[arg-type]
|
|
172
|
-
),
|
|
173
|
-
QuantumOp(
|
|
174
|
-
"cz",
|
|
175
|
-
2,
|
|
176
|
-
(),
|
|
177
|
-
implementations=_default_implementations["cz"], # type: ignore[arg-type]
|
|
178
|
-
symmetric=True,
|
|
179
|
-
unitary=lambda: np.diag([1.0, 1.0, 1.0, -1.0]),
|
|
180
|
-
),
|
|
181
|
-
QuantumOp(
|
|
182
|
-
"move",
|
|
183
|
-
2,
|
|
184
|
-
implementations=_default_implementations["move"], # type: ignore[arg-type]
|
|
185
|
-
),
|
|
186
|
-
QuantumOp(
|
|
187
|
-
"cc_prx",
|
|
188
|
-
1,
|
|
189
|
-
("angle", "phase", "feedback_qubit", "feedback_key"),
|
|
190
|
-
implementations=_default_implementations["cc_prx"], # type: ignore[arg-type]
|
|
191
|
-
),
|
|
192
|
-
QuantumOp(
|
|
193
|
-
"reset",
|
|
194
|
-
0,
|
|
195
|
-
implementations=_default_implementations["reset"], # type: ignore[arg-type]
|
|
196
|
-
symmetric=True,
|
|
197
|
-
factorizable=True,
|
|
198
|
-
),
|
|
199
|
-
QuantumOp(
|
|
200
|
-
"reset_wait",
|
|
201
|
-
0,
|
|
202
|
-
implementations=_default_implementations["reset_wait"], # type: ignore[arg-type]
|
|
203
|
-
symmetric=True,
|
|
204
|
-
factorizable=True,
|
|
205
|
-
),
|
|
206
|
-
QuantumOp(
|
|
207
|
-
"flux_multiplexer",
|
|
208
|
-
0,
|
|
209
|
-
implementations=_default_implementations["flux_multiplexer"], # type: ignore[arg-type]
|
|
210
|
-
),
|
|
211
|
-
]
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
"""A library for all canonical Quantum Operations (gates)"""
|
|
215
112
|
_quantum_ops_library = {
|
|
216
113
|
op.name: op
|
|
217
114
|
for op in [
|
|
@@ -313,3 +210,6 @@ _quantum_ops_library = {
|
|
|
313
210
|
),
|
|
314
211
|
]
|
|
315
212
|
}
|
|
213
|
+
"""Library of the current canonical quantum operations provided by iqm-pulse."""
|
|
214
|
+
# NOTE If a canonical operation is removed in the future, consider adding a second dict for former
|
|
215
|
+
# canonical operations so that we retain information about them.
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
from __future__ import annotations
|
|
11
11
|
|
|
12
|
-
from collections.abc import Iterable
|
|
12
|
+
from collections.abc import Iterable, Mapping
|
|
13
13
|
from copy import deepcopy
|
|
14
14
|
|
|
15
15
|
from iqm.models.playlist.waveforms import Samples
|
|
@@ -170,7 +170,7 @@ class FluxMultiplexer_SampleLinear(GateImplementation):
|
|
|
170
170
|
|
|
171
171
|
@classmethod
|
|
172
172
|
def get_custom_locus_mapping(
|
|
173
|
-
cls, chip_topology: ChipTopology, component_to_channels:
|
|
173
|
+
cls, chip_topology: ChipTopology, component_to_channels: Mapping[str, Iterable[str]]
|
|
174
174
|
) -> dict[tuple[str, ...] | frozenset[str], tuple[str, ...]] | None:
|
|
175
175
|
"""Locus is "global" (the whole QPU) represented by an empty tuple for now."""
|
|
176
176
|
# pylint: disable=unused-argument
|