iqm-pulse 11.2.0__py3-none-any.whl → 12.0.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/__init__.py +3 -0
- iqm/pulse/builder.py +68 -16
- iqm/pulse/circuit_operations.py +63 -3
- iqm/pulse/gates/default_gates.py +25 -9
- iqm/pulse/quantum_ops.py +6 -3
- {iqm_pulse-11.2.0.dist-info → iqm_pulse-12.0.0.dist-info}/METADATA +1 -1
- {iqm_pulse-11.2.0.dist-info → iqm_pulse-12.0.0.dist-info}/RECORD +10 -10
- {iqm_pulse-11.2.0.dist-info → iqm_pulse-12.0.0.dist-info}/LICENSE.txt +0 -0
- {iqm_pulse-11.2.0.dist-info → iqm_pulse-12.0.0.dist-info}/WHEEL +0 -0
- {iqm_pulse-11.2.0.dist-info → iqm_pulse-12.0.0.dist-info}/top_level.txt +0 -0
iqm/pulse/__init__.py
CHANGED
iqm/pulse/builder.py
CHANGED
|
@@ -80,7 +80,36 @@ logger = logging.getLogger(__name__)
|
|
|
80
80
|
|
|
81
81
|
@dataclass
|
|
82
82
|
class CircuitOperation:
|
|
83
|
-
"""Specific quantum operation applied on a specific part of the QPU, e.g. in a quantum circuit.
|
|
83
|
+
r"""Specific quantum operation applied on a specific part of the QPU, e.g. in a quantum circuit.
|
|
84
|
+
|
|
85
|
+
We currently support the following native operations for circuit execution:
|
|
86
|
+
|
|
87
|
+
================ =========== ======================================= ===========
|
|
88
|
+
name # of qubits args description
|
|
89
|
+
================ =========== ======================================= ===========
|
|
90
|
+
measure >= 1 ``key: str``, ``feedback_key: str`` Measurement in the Z basis.
|
|
91
|
+
prx 1 ``angle: float``, ``phase: float`` Phased x-rotation gate.
|
|
92
|
+
cc_prx 1 ``angle: float``, ``phase: float``,
|
|
93
|
+
``feedback_qubit: str``,
|
|
94
|
+
``feedback_key: str`` Classically controlled PRX gate.
|
|
95
|
+
reset >= 1 Reset the qubit(s) to :math:`|0\rangle`.
|
|
96
|
+
cz 2 Controlled-Z gate.
|
|
97
|
+
move 2 Move a qubit state between a qubit and a
|
|
98
|
+
computational resonator, as long as
|
|
99
|
+
at least one of the components is
|
|
100
|
+
in the :math:`|0\rangle` state.
|
|
101
|
+
barrier >= 1 Execution barrier.
|
|
102
|
+
delay >= 1 ``duration: float`` Force a delay between circuit operations.
|
|
103
|
+
================ =========== ======================================= ===========
|
|
104
|
+
|
|
105
|
+
For each CircuitOperation you may also optionally specify :attr:`implementation`,
|
|
106
|
+
which contains the name of an implementation of the operation to use.
|
|
107
|
+
Support for multiple implementations is currently experimental and in normal use the
|
|
108
|
+
field should be omitted, this selects the default implementation for the operation for that locus.
|
|
109
|
+
|
|
110
|
+
See the submodules under :mod:`iqm.pulse.gates` for more details about each operation.
|
|
111
|
+
|
|
112
|
+
"""
|
|
84
113
|
|
|
85
114
|
name: str
|
|
86
115
|
"""name of the quantum operation"""
|
|
@@ -101,31 +130,54 @@ class CircuitOperation:
|
|
|
101
130
|
ValueError: operation is not valid
|
|
102
131
|
|
|
103
132
|
"""
|
|
104
|
-
# find the op type
|
|
105
133
|
op_type = op_table.get(self.name)
|
|
106
134
|
if op_type is None:
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
if
|
|
135
|
+
message = ", ".join(op_table)
|
|
136
|
+
raise ValueError(f"Unknown operation '{self.name}'. Supported operations are '{message}'.")
|
|
137
|
+
self._validate_implementation(op_type)
|
|
138
|
+
self._validate_locus(op_type)
|
|
139
|
+
self._validate_args(op_type)
|
|
140
|
+
|
|
141
|
+
def _validate_implementation(self, op_type: QuantumOp) -> None:
|
|
142
|
+
if self.implementation is not None:
|
|
143
|
+
if not self.implementation:
|
|
144
|
+
raise ValueError("Implementation of the instruction should be None, or a non-empty string")
|
|
145
|
+
if self.implementation not in op_type.implementations:
|
|
146
|
+
raise ValueError(f"Unknown implementation '{self.implementation}' for quantum operation '{self.name}'.")
|
|
147
|
+
|
|
148
|
+
def _validate_locus(self, op_type: QuantumOp) -> None:
|
|
149
|
+
arity = op_type.arity
|
|
150
|
+
if (0 < arity) and (arity != len(self.locus)):
|
|
115
151
|
raise ValueError(
|
|
116
|
-
f"The '{self.name}' operation acts on {
|
|
117
|
-
f"but {len(self.locus)} were given: {self.locus}"
|
|
152
|
+
f"The '{self.name}' operation acts on {arity} qubit(s), but {len(self.locus)} were given: {self.locus}."
|
|
118
153
|
)
|
|
119
|
-
|
|
120
154
|
if len(self.locus) != len(set(self.locus)):
|
|
121
155
|
raise ValueError(f"Repeated locus components: {self.locus}.")
|
|
122
156
|
|
|
123
|
-
|
|
157
|
+
def _validate_args(self, op_type: QuantumOp) -> None:
|
|
158
|
+
# Check argument names
|
|
159
|
+
submitted_arg_names = set(self.args)
|
|
160
|
+
allowed_arg_types = op_type.params | op_type.optional_params
|
|
161
|
+
if not set(op_type.params) <= submitted_arg_names:
|
|
124
162
|
raise ValueError(
|
|
125
|
-
f"The '{self.name}'
|
|
126
|
-
f"
|
|
163
|
+
f"The operation '{self.name}' requires "
|
|
164
|
+
f"the argument(s) {tuple(op_type.params)}, "
|
|
165
|
+
f"but {tuple(submitted_arg_names)} were given."
|
|
127
166
|
)
|
|
128
167
|
|
|
168
|
+
if not submitted_arg_names <= set(allowed_arg_types):
|
|
169
|
+
allowed_arg_names = tuple(allowed_arg_types)
|
|
170
|
+
message = f"the arguments {allowed_arg_names}" if allowed_arg_names else "no arguments"
|
|
171
|
+
raise ValueError(f"The operation '{self.name}' allows {message}, but {submitted_arg_names} were given.")
|
|
172
|
+
# Check argument types
|
|
173
|
+
for arg_name, arg_value in self.args.items():
|
|
174
|
+
allowed_types = allowed_arg_types[arg_name]
|
|
175
|
+
if not isinstance(arg_value, allowed_types):
|
|
176
|
+
raise ValueError(
|
|
177
|
+
f"The argument '{arg_name}' should be of one of the following supported types"
|
|
178
|
+
f" {allowed_types}, but ({type(arg_value)}) was given."
|
|
179
|
+
)
|
|
180
|
+
|
|
129
181
|
|
|
130
182
|
def load_config(path: str) -> tuple[QuantumOpTable, OpCalibrationDataTree]:
|
|
131
183
|
"""Load quantum operation definitions and calibration data from a YAML config file.
|
iqm/pulse/circuit_operations.py
CHANGED
|
@@ -11,13 +11,14 @@ from __future__ import annotations
|
|
|
11
11
|
|
|
12
12
|
from collections import Counter
|
|
13
13
|
from collections.abc import Iterable, Sequence
|
|
14
|
+
from dataclasses import dataclass
|
|
14
15
|
from functools import lru_cache
|
|
15
|
-
from typing import Self
|
|
16
|
+
from typing import Any, Self
|
|
16
17
|
|
|
17
18
|
import numpy as np
|
|
18
19
|
|
|
19
20
|
from iqm.pulse.builder import CircuitOperation, build_quantum_ops
|
|
20
|
-
from iqm.pulse.quantum_ops import QuantumOpTable
|
|
21
|
+
from iqm.pulse.quantum_ops import QuantumOp, QuantumOpTable
|
|
21
22
|
|
|
22
23
|
|
|
23
24
|
def reorder(A: np.ndarray, perm: list[int]) -> np.ndarray:
|
|
@@ -363,8 +364,15 @@ class CircuitOperationList(list):
|
|
|
363
364
|
params = self.table[name].params
|
|
364
365
|
if len(params) != len(args):
|
|
365
366
|
raise TypeError(
|
|
366
|
-
f"Operation {name} has the following arguments: {params}, but {len(args)} values were provided."
|
|
367
|
+
f"Operation {name} has the following arguments: {tuple(params)}, but {len(args)} values were provided."
|
|
367
368
|
)
|
|
369
|
+
# Convert int args to floats, so that one can pass e.g. 0 instead of 0.0 for the arg.
|
|
370
|
+
# Note that this conversion is not exact if the int value is too large, but such values are not realistic
|
|
371
|
+
# for the current parameter types.
|
|
372
|
+
args = tuple(
|
|
373
|
+
float(arg) if (float in param_types and int not in param_types) and isinstance(arg, int) else arg
|
|
374
|
+
for arg, param_types in zip(args, params.values())
|
|
375
|
+
)
|
|
368
376
|
new_op = CircuitOperation(name=name, args=dict(zip(params, args)), implementation=impl_name, locus=locus)
|
|
369
377
|
new_op.validate(self.table)
|
|
370
378
|
self.append(new_op)
|
|
@@ -503,3 +511,55 @@ class CircuitOperationList(list):
|
|
|
503
511
|
f" optionally to fix the implementation of the operation in iqm-pulse."
|
|
504
512
|
)
|
|
505
513
|
setattr(CircuitOperationList, name, _add_specific_op)
|
|
514
|
+
|
|
515
|
+
|
|
516
|
+
@dataclass
|
|
517
|
+
class Circuit:
|
|
518
|
+
"""Quantum circuit.
|
|
519
|
+
|
|
520
|
+
Used e.g. for client-server communication.
|
|
521
|
+
|
|
522
|
+
Consists of a sequence of native quantum operations, each represented by an instance of the
|
|
523
|
+
:class:`CircuitOperation` class.
|
|
524
|
+
"""
|
|
525
|
+
|
|
526
|
+
name: str
|
|
527
|
+
"""name of the circuit"""
|
|
528
|
+
instructions: tuple[CircuitOperation, ...]
|
|
529
|
+
"""operations comprising the circuit"""
|
|
530
|
+
metadata: dict[str, Any] | None = None
|
|
531
|
+
"""optional metadata for the circuit"""
|
|
532
|
+
|
|
533
|
+
def all_locus_components(self) -> set[str]:
|
|
534
|
+
"""Return the names of all locus components (typically qubits) in the circuit."""
|
|
535
|
+
components: set[str] = set()
|
|
536
|
+
for instruction in self.instructions:
|
|
537
|
+
components.update(instruction.locus)
|
|
538
|
+
return components
|
|
539
|
+
|
|
540
|
+
def validate(self, supported_operations: dict[str, QuantumOp]) -> None:
|
|
541
|
+
"""Validate the circuit against the supported quantum operations.
|
|
542
|
+
|
|
543
|
+
Args:
|
|
544
|
+
supported_operations: mapping of supported quantum operation names to their definitions
|
|
545
|
+
|
|
546
|
+
Raises:
|
|
547
|
+
ValueError: circuit is not valid
|
|
548
|
+
|
|
549
|
+
"""
|
|
550
|
+
self._validate_name()
|
|
551
|
+
self._validate_instructions(supported_operations)
|
|
552
|
+
|
|
553
|
+
def _validate_name(self) -> None:
|
|
554
|
+
if not self.name:
|
|
555
|
+
raise ValueError("A circuit should have a non-empty string for a name.")
|
|
556
|
+
|
|
557
|
+
def _validate_instructions(self, supported_operations: dict[str, QuantumOp]) -> None:
|
|
558
|
+
if not self.instructions:
|
|
559
|
+
raise ValueError("Each circuit should have at least one instruction.")
|
|
560
|
+
|
|
561
|
+
for instruction in self.instructions:
|
|
562
|
+
if isinstance(instruction, CircuitOperation):
|
|
563
|
+
instruction.validate(supported_operations)
|
|
564
|
+
else:
|
|
565
|
+
raise ValueError("Every instruction in a circuit should be of type <CircuitOperation>")
|
iqm/pulse/gates/default_gates.py
CHANGED
|
@@ -125,41 +125,52 @@ _quantum_ops_library = {
|
|
|
125
125
|
QuantumOp(
|
|
126
126
|
"delay",
|
|
127
127
|
0,
|
|
128
|
-
|
|
128
|
+
{"duration": (float,)},
|
|
129
129
|
implementations=_implementation_library["delay"],
|
|
130
130
|
symmetric=True,
|
|
131
131
|
),
|
|
132
132
|
QuantumOp(
|
|
133
133
|
"measure",
|
|
134
134
|
0,
|
|
135
|
-
|
|
135
|
+
{"key": (str,)},
|
|
136
|
+
optional_params={"feedback_key": (str,)},
|
|
136
137
|
implementations=_implementation_library["measure"],
|
|
137
138
|
factorizable=True,
|
|
138
139
|
),
|
|
139
140
|
QuantumOp(
|
|
140
141
|
"measure_fidelity",
|
|
141
142
|
0,
|
|
142
|
-
|
|
143
|
+
{"key": (str,)},
|
|
143
144
|
implementations=_implementation_library["measure_fidelity"],
|
|
144
145
|
factorizable=True,
|
|
145
146
|
),
|
|
146
147
|
QuantumOp(
|
|
147
148
|
"prx",
|
|
148
149
|
1,
|
|
149
|
-
|
|
150
|
+
{
|
|
151
|
+
"angle": (float,),
|
|
152
|
+
"phase": (float,),
|
|
153
|
+
},
|
|
150
154
|
implementations=_implementation_library["prx"],
|
|
151
155
|
unitary=get_unitary_prx,
|
|
152
156
|
),
|
|
153
157
|
QuantumOp(
|
|
154
158
|
"prx_12",
|
|
155
159
|
1,
|
|
156
|
-
|
|
160
|
+
{
|
|
161
|
+
"angle": (float,),
|
|
162
|
+
"phase": (float,),
|
|
163
|
+
},
|
|
157
164
|
implementations=_implementation_library["prx_12"],
|
|
158
165
|
),
|
|
159
166
|
QuantumOp(
|
|
160
167
|
"u",
|
|
161
168
|
1,
|
|
162
|
-
|
|
169
|
+
{
|
|
170
|
+
"theta": (float,),
|
|
171
|
+
"phi": (float,),
|
|
172
|
+
"lam": (float,),
|
|
173
|
+
},
|
|
163
174
|
implementations=_implementation_library["u"],
|
|
164
175
|
unitary=get_unitary_u,
|
|
165
176
|
),
|
|
@@ -172,7 +183,7 @@ _quantum_ops_library = {
|
|
|
172
183
|
QuantumOp(
|
|
173
184
|
"rz",
|
|
174
185
|
1,
|
|
175
|
-
|
|
186
|
+
{"angle": (float,)},
|
|
176
187
|
implementations=_implementation_library["rz"],
|
|
177
188
|
unitary=get_unitary_rz,
|
|
178
189
|
),
|
|
@@ -184,7 +195,7 @@ _quantum_ops_library = {
|
|
|
184
195
|
QuantumOp(
|
|
185
196
|
"cz",
|
|
186
197
|
2,
|
|
187
|
-
|
|
198
|
+
{},
|
|
188
199
|
implementations=_implementation_library["cz"],
|
|
189
200
|
symmetric=True,
|
|
190
201
|
unitary=lambda: np.diag([1.0, 1.0, 1.0, -1.0]),
|
|
@@ -197,7 +208,12 @@ _quantum_ops_library = {
|
|
|
197
208
|
QuantumOp(
|
|
198
209
|
"cc_prx",
|
|
199
210
|
1,
|
|
200
|
-
|
|
211
|
+
{
|
|
212
|
+
"angle": (float,),
|
|
213
|
+
"phase": (float,),
|
|
214
|
+
"feedback_qubit": (str,),
|
|
215
|
+
"feedback_key": (str,),
|
|
216
|
+
},
|
|
201
217
|
implementations=_implementation_library["cc_prx"],
|
|
202
218
|
),
|
|
203
219
|
QuantumOp(
|
iqm/pulse/quantum_ops.py
CHANGED
|
@@ -78,8 +78,10 @@ class QuantumOp:
|
|
|
78
78
|
Each locus component corresponds to a quantum subsystem in the definition of the operation.
|
|
79
79
|
The computational subspace always consists of the lowest two levels of the subsystem.
|
|
80
80
|
Zero means the operation can be applied on any number of locus components."""
|
|
81
|
-
params:
|
|
82
|
-
"""
|
|
81
|
+
params: dict[str, tuple[type, ...]] = field(default_factory=dict)
|
|
82
|
+
"""Maps names of required operation parameters to their allowed types."""
|
|
83
|
+
optional_params: dict[str, tuple[type, ...]] = field(default_factory=dict)
|
|
84
|
+
"""Maps names of optional operation parameters to their allowed types."""
|
|
83
85
|
implementations: dict[str, type[GateImplementation]] = field(default_factory=dict)
|
|
84
86
|
"""Maps implementation names to :class:`.GateImplementation` classes that provide them.
|
|
85
87
|
Each such class should describe the implementation in detail in its docstring.
|
|
@@ -100,7 +102,8 @@ class QuantumOp:
|
|
|
100
102
|
"""Unitary matrix that represents the effect of this quantum operation in the computational basis, or ``None``
|
|
101
103
|
if the quantum operation is not unitary or the exact unitary is not known.
|
|
102
104
|
The Callable needs to take exactly the arguments given in :attr:`params`, for example if
|
|
103
|
-
``params=
|
|
105
|
+
``params={'angle': (float,), 'phase': (float,)}``, the function must have signature
|
|
106
|
+
``f(angle: float, phase: float) -> np.ndarray``.
|
|
104
107
|
For operations acting on more than 1 qubit, unitary should be given in the big-endian order, i.e. in the basis
|
|
105
108
|
``np.kron(first_qubit_basis_ket, second_qubit_basis_ket)``."""
|
|
106
109
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
iqm/pulse/__init__.py,sha256=
|
|
1
|
+
iqm/pulse/__init__.py,sha256=aQhtfbLQgr_1IvbZLH1MCuf0aFomokeu7DSjxuDWZC4,978
|
|
2
2
|
iqm/pulse/base_utils.py,sha256=ll3k8wbVxjHi6XcVo3PEWIjHx5QjG1C-yH218zTG8jU,2657
|
|
3
|
-
iqm/pulse/builder.py,sha256=
|
|
4
|
-
iqm/pulse/circuit_operations.py,sha256=
|
|
3
|
+
iqm/pulse/builder.py,sha256=uL2MsAvRs1dJfe2mVNrrHEqUoBBTJlABt0KmBtB6hl0,78294
|
|
4
|
+
iqm/pulse/circuit_operations.py,sha256=P83gevhGcaY4DaopGKO3CHN_CnWAPfEu-MHoLkqhu5Y,24847
|
|
5
5
|
iqm/pulse/gate_implementation.py,sha256=yyj2d1aDL0K_qouo0qLHnZ49_1qHsJEShKW14398t8Q,39546
|
|
6
6
|
iqm/pulse/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
-
iqm/pulse/quantum_ops.py,sha256=
|
|
7
|
+
iqm/pulse/quantum_ops.py,sha256=cV13V1t2rj9lgNKXgGZXMcNgEPJ7oraHL6eJJTlLvWk,14825
|
|
8
8
|
iqm/pulse/scheduler.py,sha256=62gH3AcxmsgmShcmtNaiCqFLRl1Wll6Q24zIkhw_-XM,22768
|
|
9
9
|
iqm/pulse/timebox.py,sha256=Yi8I43KiSPs2s_l1r0rMVm4mw0EpSCrpq3BCUNk5vyE,16618
|
|
10
10
|
iqm/pulse/utils.py,sha256=0J4KQNvKtZahme7sP7vLa1FtENepC-eb9_A5WaghvVU,3710
|
|
@@ -13,7 +13,7 @@ iqm/pulse/gates/__init__.py,sha256=l07iD52FiKKQyVFwswRapZMqEE0XfILSc4aZ5sXGJo4,8
|
|
|
13
13
|
iqm/pulse/gates/barrier.py,sha256=WhYV70lf4lh4Wa9UZuMk2lp9JbUQIu8lzewRC2P7pNE,2546
|
|
14
14
|
iqm/pulse/gates/conditional.py,sha256=NE-1GYlFcfaCmRuhqGEIhtoJmBArhyY5KhVnO4y4zng,6289
|
|
15
15
|
iqm/pulse/gates/cz.py,sha256=72KO8OINZRVOye4g4quHAEzwNplOlIZE0hU1IjE8Gfk,33366
|
|
16
|
-
iqm/pulse/gates/default_gates.py,sha256=
|
|
16
|
+
iqm/pulse/gates/default_gates.py,sha256=JwdrCh_Blee4mMWZ8v8ymw3Ep0fZBvw9wuaLR3rjDXA,8073
|
|
17
17
|
iqm/pulse/gates/delay.py,sha256=nST9dY2JFp_mpKhiSfsYa5yL4hFKcNJSAyCzXjhauQg,3767
|
|
18
18
|
iqm/pulse/gates/enums.py,sha256=ATwb6vZYpfgQ1gQyFPW53JyIKrdAP3FPHm6jV-t9OAk,2532
|
|
19
19
|
iqm/pulse/gates/flux_multiplexer.py,sha256=sk84ItEvkx7Z0pCHt8MCCczBe7_BHNvqS4_oeNghZw8,9757
|
|
@@ -39,8 +39,8 @@ iqm/pulse/playlist/visualisation/templates/static/logo.png,sha256=rbfQZ6_UEaztpY
|
|
|
39
39
|
iqm/pulse/playlist/visualisation/templates/static/moment.min.js,sha256=4iQZ6BVL4qNKlQ27TExEhBN1HFPvAvAMbFavKKosSWQ,53324
|
|
40
40
|
iqm/pulse/playlist/visualisation/templates/static/vis-timeline-graph2d.min.css,sha256=svzNasPg1yR5gvEaRei2jg-n4Pc3sVyMUWeS6xRAh6U,19837
|
|
41
41
|
iqm/pulse/playlist/visualisation/templates/static/vis-timeline-graph2d.min.js,sha256=OqCqCyA6JnxPz3PGXq_P_9VuSqWptgNt5Ev3T-xjefQ,570288
|
|
42
|
-
iqm_pulse-
|
|
43
|
-
iqm_pulse-
|
|
44
|
-
iqm_pulse-
|
|
45
|
-
iqm_pulse-
|
|
46
|
-
iqm_pulse-
|
|
42
|
+
iqm_pulse-12.0.0.dist-info/LICENSE.txt,sha256=R6Q7eUrLyoCQgWYorQ8WJmVmWKYU3dxA3jYUp0wwQAw,11332
|
|
43
|
+
iqm_pulse-12.0.0.dist-info/METADATA,sha256=xbcLSQbzo0BlNihfwuKsX3Pt1bI82gymRp7grNIw9go,14551
|
|
44
|
+
iqm_pulse-12.0.0.dist-info/WHEEL,sha256=y4mX-SOX4fYIkonsAGA5N0Oy-8_gI4FXw5HNI1xqvWg,91
|
|
45
|
+
iqm_pulse-12.0.0.dist-info/top_level.txt,sha256=NB4XRfyDS6_wG9gMsyX-9LTU7kWnTQxNvkbzIxGv3-c,4
|
|
46
|
+
iqm_pulse-12.0.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|