iqm-client 30.2.0__py3-none-any.whl → 31.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/cirq_iqm/serialize.py +50 -41
- iqm/iqm_client/iqm_client.py +1 -1
- iqm/iqm_client/models.py +152 -361
- iqm/iqm_client/transpile.py +37 -31
- iqm/iqm_client/validation.py +23 -19
- iqm/qiskit_iqm/iqm_job.py +2 -1
- iqm/qiskit_iqm/iqm_provider.py +2 -1
- iqm/qiskit_iqm/qiskit_to_iqm.py +32 -31
- {iqm_client-30.2.0.dist-info → iqm_client-31.1.0.dist-info}/METADATA +9 -5
- {iqm_client-30.2.0.dist-info → iqm_client-31.1.0.dist-info}/RECORD +15 -15
- {iqm_client-30.2.0.dist-info → iqm_client-31.1.0.dist-info}/AUTHORS.rst +0 -0
- {iqm_client-30.2.0.dist-info → iqm_client-31.1.0.dist-info}/LICENSE.txt +0 -0
- {iqm_client-30.2.0.dist-info → iqm_client-31.1.0.dist-info}/WHEEL +0 -0
- {iqm_client-30.2.0.dist-info → iqm_client-31.1.0.dist-info}/entry_points.txt +0 -0
- {iqm_client-30.2.0.dist-info → iqm_client-31.1.0.dist-info}/top_level.txt +0 -0
iqm/iqm_client/transpile.py
CHANGED
|
@@ -61,15 +61,15 @@ from collections.abc import Collection, Iterable, Sequence
|
|
|
61
61
|
from enum import Enum
|
|
62
62
|
|
|
63
63
|
from iqm.iqm_client import (
|
|
64
|
-
Circuit,
|
|
65
64
|
CircuitTranspilationError,
|
|
66
65
|
CircuitValidationError,
|
|
67
66
|
DynamicQuantumArchitecture,
|
|
68
|
-
Instruction,
|
|
69
67
|
)
|
|
70
68
|
from iqm.iqm_client.models import GateImplementationInfo, GateInfo, Locus, _op_is_symmetric
|
|
71
69
|
from iqm.iqm_client.validation import validate_circuit_moves, validate_instruction
|
|
72
70
|
|
|
71
|
+
from iqm.pulse import Circuit, CircuitOperation
|
|
72
|
+
|
|
73
73
|
Resolution = tuple[str, str, str]
|
|
74
74
|
"""A (gate qubit, move qubit, resonator) triple that represents a resolution of a fictional
|
|
75
75
|
qubit-qubit gate."""
|
|
@@ -93,10 +93,10 @@ class ExistingMoveHandlingOptions(str, Enum):
|
|
|
93
93
|
|
|
94
94
|
|
|
95
95
|
def _map_loci(
|
|
96
|
-
instructions: Iterable[
|
|
96
|
+
instructions: Iterable[CircuitOperation],
|
|
97
97
|
qubit_mapping: dict[str, str],
|
|
98
98
|
inverse: bool = False,
|
|
99
|
-
) -> tuple[
|
|
99
|
+
) -> tuple[CircuitOperation, ...]:
|
|
100
100
|
"""Map the loci of the given instructions using the given qubit mapping, or its inverse.
|
|
101
101
|
|
|
102
102
|
Args:
|
|
@@ -111,7 +111,13 @@ def _map_loci(
|
|
|
111
111
|
if inverse:
|
|
112
112
|
qubit_mapping = {phys: log for log, phys in qubit_mapping.items()}
|
|
113
113
|
return tuple(
|
|
114
|
-
|
|
114
|
+
CircuitOperation(
|
|
115
|
+
name=inst.name,
|
|
116
|
+
locus=tuple(qubit_mapping[q] for q in inst.locus),
|
|
117
|
+
args=inst.args,
|
|
118
|
+
implementation=inst.implementation,
|
|
119
|
+
)
|
|
120
|
+
for inst in instructions
|
|
115
121
|
)
|
|
116
122
|
|
|
117
123
|
|
|
@@ -212,7 +218,7 @@ class _ResonatorStateTracker:
|
|
|
212
218
|
return cls.from_instructions(circuit.instructions)
|
|
213
219
|
|
|
214
220
|
@classmethod
|
|
215
|
-
def from_instructions(cls, instructions: Iterable[
|
|
221
|
+
def from_instructions(cls, instructions: Iterable[CircuitOperation]) -> _ResonatorStateTracker:
|
|
216
222
|
"""Constructor to make the _ResonatorStateTracker from a sequence of instructions.
|
|
217
223
|
|
|
218
224
|
Infers the resonator connectivity from the MOVE gates.
|
|
@@ -227,7 +233,7 @@ class _ResonatorStateTracker:
|
|
|
227
233
|
qr_gates: dict[str, dict[str, set[str]]] = {}
|
|
228
234
|
for i in instructions:
|
|
229
235
|
if i.name in cls.qr_gate_names:
|
|
230
|
-
q, r = i.
|
|
236
|
+
q, r = i.locus
|
|
231
237
|
qr_gates.setdefault(i.name, {}).setdefault(q, set()).add(r)
|
|
232
238
|
return cls(qr_gates)
|
|
233
239
|
|
|
@@ -270,7 +276,7 @@ class _ResonatorStateTracker:
|
|
|
270
276
|
self,
|
|
271
277
|
qubit: str,
|
|
272
278
|
resonator: str,
|
|
273
|
-
) -> Iterable[
|
|
279
|
+
) -> Iterable[CircuitOperation]:
|
|
274
280
|
"""MOVE instruction(s) to move the state of the given qubit into the given resonator,
|
|
275
281
|
or back to the qubit.
|
|
276
282
|
|
|
@@ -292,7 +298,7 @@ class _ResonatorStateTracker:
|
|
|
292
298
|
if owner not in [qubit, resonator]:
|
|
293
299
|
locus = (owner, resonator)
|
|
294
300
|
self.apply_move(*locus)
|
|
295
|
-
yield
|
|
301
|
+
yield CircuitOperation(name=self.move_gate, locus=locus, args={})
|
|
296
302
|
|
|
297
303
|
# if the qubit does not hold its own state, restore it, unless it's in the resonator
|
|
298
304
|
# find where the qubit state is (it can be in at most one resonator)
|
|
@@ -300,17 +306,17 @@ class _ResonatorStateTracker:
|
|
|
300
306
|
if res and (holder := res[0]) != resonator:
|
|
301
307
|
locus = (qubit, holder)
|
|
302
308
|
self.apply_move(*locus)
|
|
303
|
-
yield
|
|
309
|
+
yield CircuitOperation(name=self.move_gate, locus=locus, args={})
|
|
304
310
|
|
|
305
311
|
# move qubit state to resonator, or back to the qubit
|
|
306
312
|
locus = (qubit, resonator)
|
|
307
313
|
self.apply_move(*locus)
|
|
308
|
-
yield
|
|
314
|
+
yield CircuitOperation(name=self.move_gate, locus=locus, args={})
|
|
309
315
|
|
|
310
316
|
def restore_as_move_instructions(
|
|
311
317
|
self,
|
|
312
318
|
resonators: Iterable[str] | None = None,
|
|
313
|
-
) -> list[
|
|
319
|
+
) -> list[CircuitOperation]:
|
|
314
320
|
"""MOVE instructions that move the states held in the given resonators back to their qubits.
|
|
315
321
|
|
|
316
322
|
Applies the returned MOVE instructions on the tracker state.
|
|
@@ -327,13 +333,13 @@ class _ResonatorStateTracker:
|
|
|
327
333
|
if resonators is None:
|
|
328
334
|
resonators = self.resonators
|
|
329
335
|
|
|
330
|
-
instructions: list[
|
|
336
|
+
instructions: list[CircuitOperation] = []
|
|
331
337
|
for r in resonators:
|
|
332
338
|
q = self.res_state_owner[r]
|
|
333
339
|
# if the state in r does not belong to r, restore it to its owner
|
|
334
340
|
if q != r:
|
|
335
341
|
locus = (q, r)
|
|
336
|
-
instructions.append(
|
|
342
|
+
instructions.append(CircuitOperation(name=self.move_gate, locus=locus, args={}))
|
|
337
343
|
self.apply_move(*locus)
|
|
338
344
|
return instructions
|
|
339
345
|
|
|
@@ -367,7 +373,7 @@ class _ResonatorStateTracker:
|
|
|
367
373
|
"""
|
|
368
374
|
return tuple(self.res_state_owner.get(q, q) for q in locus)
|
|
369
375
|
|
|
370
|
-
def find_resolutions(self, inst:
|
|
376
|
+
def find_resolutions(self, inst: CircuitOperation) -> list[Resolution]:
|
|
371
377
|
"""Find all the possible resolutions for the given fictional qubit-qubit gate.
|
|
372
378
|
|
|
373
379
|
Given a fictional gate G acting on qubits (a, b), finds all resonators r for which the current DQA
|
|
@@ -390,10 +396,10 @@ class _ResonatorStateTracker:
|
|
|
390
396
|
return gate_q2r.get(g, set()) & self.move_q2r.get(m, set())
|
|
391
397
|
|
|
392
398
|
# G is assumed symmetric, hence we may reverse the locus order for more resolutions
|
|
393
|
-
a, b = inst.
|
|
399
|
+
a, b = inst.locus
|
|
394
400
|
return [(a, b, r) for r in get_resonators(a, b)] + [(b, a, r) for r in get_resonators(b, a)]
|
|
395
401
|
|
|
396
|
-
def find_best_resolution(self, inst:
|
|
402
|
+
def find_best_resolution(self, inst: CircuitOperation, lookahead: Iterable[CircuitOperation]) -> Resolution | None:
|
|
397
403
|
"""Find the best resolution for the fictional qubit-qubit gate instruction ``inst``
|
|
398
404
|
using the available native qubit-resonator gates.
|
|
399
405
|
|
|
@@ -419,12 +425,12 @@ class _ResonatorStateTracker:
|
|
|
419
425
|
# but that would scale exponentially in n. Instead we do some heuristics:
|
|
420
426
|
# Look ahead until we find the instructions that target the locus qubits next, and include
|
|
421
427
|
# their costs in the badness calculation.
|
|
422
|
-
followers: dict[str,
|
|
428
|
+
followers: dict[str, CircuitOperation] = {}
|
|
423
429
|
for follower in lookahead:
|
|
424
430
|
if len(followers) == 2:
|
|
425
431
|
break # found a follower for both locus qubits
|
|
426
|
-
for q in follower.
|
|
427
|
-
if q in inst.
|
|
432
|
+
for q in follower.locus:
|
|
433
|
+
if q in inst.locus:
|
|
428
434
|
followers.setdefault(q, follower)
|
|
429
435
|
|
|
430
436
|
def get_badness(res: Resolution, g_holder: str, m_holder: str, r_owner: str) -> int:
|
|
@@ -499,7 +505,7 @@ class _ResonatorStateTracker:
|
|
|
499
505
|
else:
|
|
500
506
|
# follower needs to use same resonator but different move qubit
|
|
501
507
|
badness += 2
|
|
502
|
-
elif len(m_follower.
|
|
508
|
+
elif len(m_follower.locus) == 1:
|
|
503
509
|
# Not a resolvable QR gate. For 1q gates on m, state must be restored.
|
|
504
510
|
badness += 1
|
|
505
511
|
|
|
@@ -529,7 +535,7 @@ class _ResonatorStateTracker:
|
|
|
529
535
|
# return the best option
|
|
530
536
|
return min(options, key=lambda x: x[1])[0]
|
|
531
537
|
|
|
532
|
-
def get_sequence(self, resolution: Resolution, inst:
|
|
538
|
+
def get_sequence(self, resolution: Resolution, inst: CircuitOperation) -> list[CircuitOperation]:
|
|
533
539
|
"""Apply a fictional two-qubit gate using a sequence of native qubit-resonator gates.
|
|
534
540
|
|
|
535
541
|
See :mod:`~iqm.iqm_client.transpile`.
|
|
@@ -547,7 +553,7 @@ class _ResonatorStateTracker:
|
|
|
547
553
|
|
|
548
554
|
"""
|
|
549
555
|
g, m, r = resolution
|
|
550
|
-
seq: list[
|
|
556
|
+
seq: list[CircuitOperation] = []
|
|
551
557
|
# does m state need to be moved to the resonator?
|
|
552
558
|
m_holder = self.qubit_state_holder.get(m, m)
|
|
553
559
|
if m_holder != r:
|
|
@@ -557,14 +563,14 @@ class _ResonatorStateTracker:
|
|
|
557
563
|
if g_holder != g:
|
|
558
564
|
seq += self.restore_as_move_instructions([g_holder])
|
|
559
565
|
# apply G(g, r)
|
|
560
|
-
seq.append(inst.
|
|
566
|
+
seq.append(CircuitOperation(name=inst.name, locus=(g, r), args=inst.args, implementation=inst.implementation))
|
|
561
567
|
return seq
|
|
562
568
|
|
|
563
569
|
def insert_moves(
|
|
564
570
|
self,
|
|
565
|
-
instructions: Sequence[
|
|
571
|
+
instructions: Sequence[CircuitOperation],
|
|
566
572
|
arch: DynamicQuantumArchitecture,
|
|
567
|
-
) -> list[
|
|
573
|
+
) -> list[CircuitOperation]:
|
|
568
574
|
"""Convert a simplified architecture circuit into a equivalent Star architecture circuit with
|
|
569
575
|
resonators and MOVE gates.
|
|
570
576
|
|
|
@@ -587,10 +593,10 @@ class _ResonatorStateTracker:
|
|
|
587
593
|
"""
|
|
588
594
|
# This method can handle real single- and two-qubit gates, real q-r gates including MOVE,
|
|
589
595
|
# and fictional two-qubit gates which it decomposes into real q-r gates.
|
|
590
|
-
new_instructions: list[
|
|
596
|
+
new_instructions: list[CircuitOperation] = []
|
|
591
597
|
|
|
592
598
|
for idx, inst in enumerate(instructions):
|
|
593
|
-
locus = inst.
|
|
599
|
+
locus = inst.locus
|
|
594
600
|
try:
|
|
595
601
|
validate_instruction(architecture=arch, instruction=inst)
|
|
596
602
|
# inst can be applied as is on locus, but we may first need to use MOVEs to make
|
|
@@ -828,11 +834,11 @@ def transpile_remove_moves(circuit: Circuit) -> Circuit:
|
|
|
828
834
|
for inst in circuit.instructions:
|
|
829
835
|
if inst.name == tracker.move_gate:
|
|
830
836
|
# update the state tracking, drop the MOVE
|
|
831
|
-
tracker.apply_move(*inst.
|
|
837
|
+
tracker.apply_move(*inst.locus)
|
|
832
838
|
else:
|
|
833
839
|
# map the instruction locus
|
|
834
|
-
|
|
840
|
+
new_locus = tracker.map_resonators_in_locus(inst.locus)
|
|
835
841
|
new_instructions.append(
|
|
836
|
-
|
|
842
|
+
CircuitOperation(name=inst.name, implementation=inst.implementation, locus=new_locus, args=inst.args)
|
|
837
843
|
)
|
|
838
844
|
return Circuit(name=circuit.name, instructions=tuple(new_instructions), metadata=circuit.metadata)
|
iqm/iqm_client/validation.py
CHANGED
|
@@ -13,14 +13,14 @@ import itertools
|
|
|
13
13
|
from iqm.iqm_client.errors import CircuitValidationError
|
|
14
14
|
from iqm.iqm_client.models import (
|
|
15
15
|
_SUPPORTED_OPERATIONS,
|
|
16
|
-
Circuit,
|
|
17
16
|
CircuitBatch,
|
|
18
17
|
DynamicQuantumArchitecture,
|
|
19
|
-
Instruction,
|
|
20
18
|
MoveGateValidationMode,
|
|
21
19
|
QIRCode,
|
|
22
20
|
)
|
|
23
21
|
|
|
22
|
+
from iqm.pulse import Circuit, CircuitOperation
|
|
23
|
+
|
|
24
24
|
|
|
25
25
|
def validate_qubit_mapping(
|
|
26
26
|
architecture: DynamicQuantumArchitecture,
|
|
@@ -52,7 +52,7 @@ def validate_qubit_mapping(
|
|
|
52
52
|
for i, circuit in enumerate(circuits):
|
|
53
53
|
if isinstance(circuit, (QIRCode)):
|
|
54
54
|
continue
|
|
55
|
-
diff = circuit.
|
|
55
|
+
diff = circuit.all_locus_components() - set(qubit_mapping)
|
|
56
56
|
if diff:
|
|
57
57
|
raise CircuitValidationError(
|
|
58
58
|
f"The qubits {diff} in circuit '{circuit.name}' at index {i} "
|
|
@@ -112,7 +112,7 @@ def validate_circuit_instructions(
|
|
|
112
112
|
|
|
113
113
|
def validate_instruction(
|
|
114
114
|
architecture: DynamicQuantumArchitecture,
|
|
115
|
-
instruction:
|
|
115
|
+
instruction: CircuitOperation,
|
|
116
116
|
qubit_mapping: dict[str, str] | None = None,
|
|
117
117
|
) -> None:
|
|
118
118
|
"""Validate an instruction against the dynamic quantum architecture.
|
|
@@ -134,11 +134,11 @@ def validate_instruction(
|
|
|
134
134
|
raise CircuitValidationError(f"Unknown quantum operation '{instruction.name}'.")
|
|
135
135
|
|
|
136
136
|
# apply the qubit mapping if any
|
|
137
|
-
mapped_qubits = tuple(qubit_mapping[q] for q in instruction.
|
|
137
|
+
mapped_qubits = tuple(qubit_mapping[q] for q in instruction.locus) if qubit_mapping else instruction.locus
|
|
138
138
|
|
|
139
139
|
def check_locus_components(allowed_components: Iterable[str], msg: str) -> None:
|
|
140
140
|
"""Checks that the instruction locus consists of the allowed components only."""
|
|
141
|
-
for q, mapped_q in zip(instruction.
|
|
141
|
+
for q, mapped_q in zip(instruction.locus, mapped_qubits):
|
|
142
142
|
if mapped_q not in allowed_components:
|
|
143
143
|
raise CircuitValidationError(
|
|
144
144
|
f"{instruction!r}: Component {q} = {mapped_q} {msg}."
|
|
@@ -146,10 +146,14 @@ def validate_instruction(
|
|
|
146
146
|
else f"{instruction!r}: Component {q} {msg}."
|
|
147
147
|
)
|
|
148
148
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
149
|
+
impl_name = instruction.implementation or _SUPPORTED_OPERATIONS[
|
|
150
|
+
instruction.name
|
|
151
|
+
].get_default_implementation_for_locus(instruction.locus)
|
|
152
|
+
if (impl := op_info.implementations.get(impl_name)) is not None:
|
|
153
|
+
if not impl.needs_calibration():
|
|
154
|
+
# all QPU loci are allowed
|
|
155
|
+
check_locus_components(architecture.components, msg="does not exist on the QPU")
|
|
156
|
+
return
|
|
153
157
|
|
|
154
158
|
gate_info = architecture.gates.get(instruction.name)
|
|
155
159
|
if gate_info is None:
|
|
@@ -187,13 +191,13 @@ def validate_instruction(
|
|
|
187
191
|
)
|
|
188
192
|
if mapped_qubits not in all_loci:
|
|
189
193
|
raise CircuitValidationError(
|
|
190
|
-
f"{instruction.
|
|
194
|
+
f"{instruction.locus} = {tuple(mapped_qubits)} is not allowed as locus for '{instruction_name}'"
|
|
191
195
|
if qubit_mapping
|
|
192
|
-
else f"{instruction.
|
|
196
|
+
else f"{instruction.locus} is not allowed as locus for '{instruction_name}'"
|
|
193
197
|
)
|
|
194
198
|
|
|
195
199
|
|
|
196
|
-
def validate_circuit_moves(
|
|
200
|
+
def validate_circuit_moves( # noqa: PLR0912
|
|
197
201
|
architecture: DynamicQuantumArchitecture,
|
|
198
202
|
circuit: Circuit,
|
|
199
203
|
qubit_mapping: dict[str, str] | None = None,
|
|
@@ -243,17 +247,17 @@ def validate_circuit_moves(
|
|
|
243
247
|
|
|
244
248
|
for inst in circuit.instructions:
|
|
245
249
|
if inst.name == "move":
|
|
246
|
-
qubit, resonator = inst.
|
|
250
|
+
qubit, resonator = inst.locus
|
|
247
251
|
if not (qubit in all_qubits and resonator in all_resonators):
|
|
248
252
|
raise CircuitValidationError(
|
|
249
|
-
f"MOVE instructions are only allowed between qubit and resonator, not {inst.
|
|
253
|
+
f"MOVE instructions are only allowed between qubit and resonator, not {inst.locus}."
|
|
250
254
|
)
|
|
251
255
|
|
|
252
256
|
if (resonator_qubit := resonator_occupations.get(resonator)) is None:
|
|
253
257
|
# Beginning MOVE: check that the qubit hasn't been moved to another resonator
|
|
254
258
|
if qubit in moved_qubits:
|
|
255
259
|
raise CircuitValidationError(
|
|
256
|
-
f"MOVE instruction {inst.
|
|
260
|
+
f"MOVE instruction {inst.locus}: state of {qubit} is "
|
|
257
261
|
f"in another resonator: {resonator_occupations}."
|
|
258
262
|
)
|
|
259
263
|
resonator_occupations[resonator] = qubit
|
|
@@ -262,16 +266,16 @@ def validate_circuit_moves(
|
|
|
262
266
|
# Ending MOVE: check that the qubit matches to the qubit that was moved to the resonator
|
|
263
267
|
if resonator_qubit != qubit:
|
|
264
268
|
raise CircuitValidationError(
|
|
265
|
-
f"MOVE instruction {inst.
|
|
269
|
+
f"MOVE instruction {inst.locus} to an already occupied resonator: {resonator_occupations}."
|
|
266
270
|
)
|
|
267
271
|
del resonator_occupations[resonator]
|
|
268
272
|
moved_qubits.remove(qubit)
|
|
269
273
|
elif moved_qubits:
|
|
270
274
|
# Validate that moved qubits are not used during MOVE operations
|
|
271
275
|
if inst.name not in allowed_gates:
|
|
272
|
-
if overlap := set(inst.
|
|
276
|
+
if overlap := set(inst.locus) & moved_qubits:
|
|
273
277
|
raise CircuitValidationError(
|
|
274
|
-
f"Instruction {inst.name} acts on {inst.
|
|
278
|
+
f"Instruction {inst.name} acts on {inst.locus} while the state(s) of {overlap} "
|
|
275
279
|
f"are in a resonator. Current resonator occupation: {resonator_occupations}."
|
|
276
280
|
)
|
|
277
281
|
|
iqm/qiskit_iqm/iqm_job.py
CHANGED
|
@@ -24,7 +24,6 @@ import warnings
|
|
|
24
24
|
from iqm.iqm_client import (
|
|
25
25
|
DEFAULT_TIMEOUT_SECONDS,
|
|
26
26
|
APITimeoutError,
|
|
27
|
-
Circuit,
|
|
28
27
|
CircuitMeasurementResults,
|
|
29
28
|
HeraldingMode,
|
|
30
29
|
IQMClient,
|
|
@@ -38,6 +37,8 @@ import numpy as np
|
|
|
38
37
|
from qiskit.providers import JobStatus, JobV1
|
|
39
38
|
from qiskit.result import Counts, Result
|
|
40
39
|
|
|
40
|
+
from iqm.pulse import Circuit
|
|
41
|
+
|
|
41
42
|
if TYPE_CHECKING:
|
|
42
43
|
from iqm.qiskit_iqm.iqm_provider import IQMBackend
|
|
43
44
|
|
iqm/qiskit_iqm/iqm_provider.py
CHANGED
|
@@ -22,7 +22,6 @@ from uuid import UUID
|
|
|
22
22
|
import warnings
|
|
23
23
|
|
|
24
24
|
from iqm.iqm_client import (
|
|
25
|
-
Circuit,
|
|
26
25
|
CircuitBatch,
|
|
27
26
|
CircuitCompilationOptions,
|
|
28
27
|
CircuitValidationError,
|
|
@@ -39,6 +38,8 @@ from iqm.qiskit_iqm.qiskit_to_iqm import serialize_instructions
|
|
|
39
38
|
from qiskit import QuantumCircuit
|
|
40
39
|
from qiskit.providers import JobStatus, JobV1, Options
|
|
41
40
|
|
|
41
|
+
from iqm.pulse import Circuit
|
|
42
|
+
|
|
42
43
|
try:
|
|
43
44
|
__version__ = version("qiskit-iqm")
|
|
44
45
|
except PackageNotFoundError: # pragma: no cover
|
iqm/qiskit_iqm/qiskit_to_iqm.py
CHANGED
|
@@ -17,15 +17,16 @@ from __future__ import annotations
|
|
|
17
17
|
|
|
18
18
|
from collections.abc import Collection
|
|
19
19
|
from dataclasses import dataclass
|
|
20
|
+
from math import pi
|
|
20
21
|
import re
|
|
21
22
|
|
|
22
|
-
from iqm.iqm_client import Instruction
|
|
23
23
|
from iqm.qiskit_iqm.move_gate import MoveGate
|
|
24
|
-
import numpy as np
|
|
25
24
|
from qiskit import QuantumCircuit as QiskitQuantumCircuit
|
|
26
25
|
from qiskit.circuit import ClassicalRegister, Clbit, QuantumRegister
|
|
27
26
|
from qiskit.transpiler.layout import Layout
|
|
28
27
|
|
|
28
|
+
from iqm.pulse import CircuitOperation
|
|
29
|
+
|
|
29
30
|
|
|
30
31
|
class InstructionNotSupportedError(RuntimeError):
|
|
31
32
|
"""Raised when a given instruction is not supported by the IQM server."""
|
|
@@ -94,7 +95,7 @@ class MeasurementKey:
|
|
|
94
95
|
|
|
95
96
|
def serialize_instructions( # noqa: PLR0912, PLR0915
|
|
96
97
|
circuit: QiskitQuantumCircuit, qubit_index_to_name: dict[int, str], allowed_nonnative_gates: Collection[str] = ()
|
|
97
|
-
) -> list[
|
|
98
|
+
) -> list[CircuitOperation]:
|
|
98
99
|
"""Serialize a quantum circuit into the IQM data transfer format.
|
|
99
100
|
|
|
100
101
|
This is IQM's internal helper for :meth:`.IQMBackend.serialize_circuit` that gives slightly more control.
|
|
@@ -116,32 +117,32 @@ def serialize_instructions( # noqa: PLR0912, PLR0915
|
|
|
116
117
|
ValueError: circuit contains an unsupported instruction or is not transpiled in general
|
|
117
118
|
|
|
118
119
|
"""
|
|
119
|
-
instructions: list[
|
|
120
|
+
instructions: list[CircuitOperation] = []
|
|
120
121
|
# maps clbits to the latest "measure" instruction to store its result there
|
|
121
|
-
clbit_to_measure: dict[Clbit,
|
|
122
|
+
clbit_to_measure: dict[Clbit, CircuitOperation] = {}
|
|
122
123
|
for circuit_instruction in circuit.data:
|
|
123
124
|
instruction = circuit_instruction.operation
|
|
124
|
-
qubit_names =
|
|
125
|
+
qubit_names = tuple(qubit_index_to_name[circuit.find_bit(qubit).index] for qubit in circuit_instruction.qubits)
|
|
125
126
|
if instruction.name == "r":
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
native_inst =
|
|
127
|
+
angle = float(instruction.params[0])
|
|
128
|
+
phase = float(instruction.params[1])
|
|
129
|
+
native_inst = CircuitOperation(name="prx", locus=qubit_names, args={"angle": angle, "phase": phase})
|
|
129
130
|
elif instruction.name == "x":
|
|
130
|
-
native_inst =
|
|
131
|
+
native_inst = CircuitOperation(name="prx", locus=qubit_names, args={"angle": pi, "phase": 0.0})
|
|
131
132
|
elif instruction.name == "rx":
|
|
132
|
-
|
|
133
|
-
native_inst =
|
|
133
|
+
angle = float(instruction.params[0])
|
|
134
|
+
native_inst = CircuitOperation(name="prx", locus=qubit_names, args={"angle": angle, "phase": 0.0})
|
|
134
135
|
elif instruction.name == "y":
|
|
135
|
-
native_inst =
|
|
136
|
+
native_inst = CircuitOperation(name="prx", locus=qubit_names, args={"angle": pi, "phase": 0.5 * pi})
|
|
136
137
|
elif instruction.name == "ry":
|
|
137
|
-
|
|
138
|
-
native_inst =
|
|
138
|
+
angle = float(instruction.params[0])
|
|
139
|
+
native_inst = CircuitOperation(name="prx", locus=qubit_names, args={"angle": angle, "phase": 0.5 * pi})
|
|
139
140
|
elif instruction.name == "cz":
|
|
140
|
-
native_inst =
|
|
141
|
+
native_inst = CircuitOperation(name="cz", locus=qubit_names, args={})
|
|
141
142
|
elif instruction.name == "move":
|
|
142
|
-
native_inst =
|
|
143
|
+
native_inst = CircuitOperation(name="move", locus=qubit_names, args={})
|
|
143
144
|
elif instruction.name == "barrier":
|
|
144
|
-
native_inst =
|
|
145
|
+
native_inst = CircuitOperation(name="barrier", locus=qubit_names, args={})
|
|
145
146
|
elif instruction.name == "delay":
|
|
146
147
|
duration = float(instruction.params[0])
|
|
147
148
|
# convert duration to seconds
|
|
@@ -160,7 +161,7 @@ def serialize_instructions( # noqa: PLR0912, PLR0915
|
|
|
160
161
|
duration *= 1e-12
|
|
161
162
|
else:
|
|
162
163
|
raise ValueError(f"Delay: Unsupported unit '{unit}'")
|
|
163
|
-
native_inst =
|
|
164
|
+
native_inst = CircuitOperation(name="delay", locus=qubit_names, args={"duration": duration})
|
|
164
165
|
elif instruction.name == "measure":
|
|
165
166
|
if len(circuit_instruction.clbits) != 1:
|
|
166
167
|
raise ValueError(
|
|
@@ -168,15 +169,15 @@ def serialize_instructions( # noqa: PLR0912, PLR0915
|
|
|
168
169
|
)
|
|
169
170
|
clbit = circuit_instruction.clbits[0] # always a single-qubit measurement
|
|
170
171
|
mk = str(MeasurementKey.from_clbit(clbit, circuit))
|
|
171
|
-
native_inst =
|
|
172
|
+
native_inst = CircuitOperation(name="measure", locus=qubit_names, args={"key": mk})
|
|
172
173
|
clbit_to_measure[clbit] = native_inst
|
|
173
174
|
elif instruction.name == "reset":
|
|
174
|
-
native_inst =
|
|
175
|
+
native_inst = CircuitOperation(name="reset", locus=qubit_names, args={})
|
|
175
176
|
elif instruction.name == "id":
|
|
176
177
|
continue
|
|
177
178
|
elif instruction.name in allowed_nonnative_gates:
|
|
178
179
|
args = {f"p{i}": param for i, param in enumerate(instruction.params)}
|
|
179
|
-
native_inst =
|
|
180
|
+
native_inst = CircuitOperation(name=instruction.name, locus=qubit_names, args=args)
|
|
180
181
|
else:
|
|
181
182
|
raise ValueError(
|
|
182
183
|
f"Instruction '{instruction.name}' in the circuit '{circuit.name}' is not natively supported. "
|
|
@@ -211,7 +212,7 @@ def serialize_instructions( # noqa: PLR0912, PLR0915
|
|
|
211
212
|
measure_inst = clbit_to_measure[clbit]
|
|
212
213
|
feedback_key = measure_inst.args["key"]
|
|
213
214
|
measure_inst.args["feedback_key"] = feedback_key # this measure is used to provide feedback
|
|
214
|
-
physical_qubit_name = measure_inst.
|
|
215
|
+
physical_qubit_name = measure_inst.locus[0] # single-qubit measurement
|
|
215
216
|
native_inst.args["feedback_key"] = feedback_key
|
|
216
217
|
native_inst.args["feedback_qubit"] = physical_qubit_name
|
|
217
218
|
|
|
@@ -220,7 +221,7 @@ def serialize_instructions( # noqa: PLR0912, PLR0915
|
|
|
220
221
|
|
|
221
222
|
|
|
222
223
|
def deserialize_instructions(
|
|
223
|
-
instructions: list[
|
|
224
|
+
instructions: list[CircuitOperation], qubit_name_to_index: dict[str, int], layout: Layout
|
|
224
225
|
) -> QiskitQuantumCircuit:
|
|
225
226
|
"""Helper function to turn a list of IQM Instructions into a Qiskit QuantumCircuit.
|
|
226
227
|
|
|
@@ -276,11 +277,11 @@ def deserialize_instructions(
|
|
|
276
277
|
*(cl_regs.get(i, ClassicalRegister(0)) for i in range(max(cl_regs) + 1 if cl_regs else 0)),
|
|
277
278
|
)
|
|
278
279
|
for instr in instructions:
|
|
279
|
-
locus = [index_to_qiskit_qubit[qubit_name_to_index[q]] for q in instr.
|
|
280
|
+
locus = [index_to_qiskit_qubit[qubit_name_to_index[q]] for q in instr.locus]
|
|
280
281
|
if instr.name == "prx":
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
circuit.r(
|
|
282
|
+
angle = instr.args["angle"]
|
|
283
|
+
phase = instr.args["phase"]
|
|
284
|
+
circuit.r(angle, phase, locus[0])
|
|
284
285
|
elif instr.name == "cz":
|
|
285
286
|
circuit.cz(*locus)
|
|
286
287
|
elif instr.name == "move":
|
|
@@ -294,11 +295,11 @@ def deserialize_instructions(
|
|
|
294
295
|
duration = instr.args["duration"]
|
|
295
296
|
circuit.delay(duration, locus, unit="s") # native delay instructions always use seconds
|
|
296
297
|
elif instr.name == "cc_prx":
|
|
297
|
-
|
|
298
|
-
|
|
298
|
+
angle = instr.args["angle"]
|
|
299
|
+
phase = instr.args["phase"]
|
|
299
300
|
feedback_key = instr.args["feedback_key"]
|
|
300
301
|
# NOTE: 'feedback_qubit' is not needed, because in Qiskit you only have single-qubit measurements.
|
|
301
|
-
circuit.r(
|
|
302
|
+
circuit.r(angle, phase, locus[0]).c_if(fk_to_clbit[feedback_key], 1)
|
|
302
303
|
elif instr.name == "reset":
|
|
303
304
|
for qubit in locus:
|
|
304
305
|
circuit.reset(qubit)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: iqm-client
|
|
3
|
-
Version:
|
|
3
|
+
Version: 31.1.0
|
|
4
4
|
Summary: Client library for accessing an IQM quantum computer
|
|
5
5
|
Author-email: IQM Finland Oy <developers@meetiqm.com>
|
|
6
6
|
License: Apache License
|
|
@@ -216,30 +216,34 @@ Requires-Python: <3.13,>=3.10
|
|
|
216
216
|
Description-Content-Type: text/x-rst
|
|
217
217
|
License-File: LICENSE.txt
|
|
218
218
|
License-File: AUTHORS.rst
|
|
219
|
-
Requires-Dist: iqm-station-control-client <10
|
|
219
|
+
Requires-Dist: iqm-station-control-client <11,>=10
|
|
220
220
|
Requires-Dist: iqm-exa-common <27,>=26
|
|
221
|
+
Requires-Dist: iqm-pulse <13,>=12
|
|
221
222
|
Requires-Dist: numpy <3.0,>=1.26.4
|
|
222
223
|
Requires-Dist: packaging ==24.1
|
|
223
224
|
Requires-Dist: pydantic <3.0,>=2.9.2
|
|
224
225
|
Requires-Dist: requests ==2.32.3
|
|
225
226
|
Provides-Extra: cirq
|
|
226
|
-
Requires-Dist: iqm-station-control-client <10
|
|
227
|
+
Requires-Dist: iqm-station-control-client <11,>=10 ; extra == 'cirq'
|
|
227
228
|
Requires-Dist: iqm-exa-common <27,>=26 ; extra == 'cirq'
|
|
229
|
+
Requires-Dist: iqm-pulse <13,>=12 ; extra == 'cirq'
|
|
228
230
|
Requires-Dist: cirq-core[contrib] ~=1.2 ; extra == 'cirq'
|
|
229
231
|
Requires-Dist: ply ==3.11 ; extra == 'cirq'
|
|
230
232
|
Requires-Dist: llvmlite >=0.44.0 ; extra == 'cirq'
|
|
231
233
|
Requires-Dist: numba >=0.61.0 ; extra == 'cirq'
|
|
232
234
|
Provides-Extra: cli
|
|
233
|
-
Requires-Dist: iqm-station-control-client <10
|
|
235
|
+
Requires-Dist: iqm-station-control-client <11,>=10 ; extra == 'cli'
|
|
234
236
|
Requires-Dist: iqm-exa-common <27,>=26 ; extra == 'cli'
|
|
237
|
+
Requires-Dist: iqm-pulse <13,>=12 ; extra == 'cli'
|
|
235
238
|
Requires-Dist: click <9,>=8.1.6 ; extra == 'cli'
|
|
236
239
|
Requires-Dist: jsonschema >=4.6.0 ; extra == 'cli'
|
|
237
240
|
Requires-Dist: psutil >=5.9.2 ; extra == 'cli'
|
|
238
241
|
Requires-Dist: types-psutil ; extra == 'cli'
|
|
239
242
|
Requires-Dist: python-daemon >=2.3.0 ; extra == 'cli'
|
|
240
243
|
Provides-Extra: qiskit
|
|
241
|
-
Requires-Dist: iqm-station-control-client <10
|
|
244
|
+
Requires-Dist: iqm-station-control-client <11,>=10 ; extra == 'qiskit'
|
|
242
245
|
Requires-Dist: iqm-exa-common <27,>=26 ; extra == 'qiskit'
|
|
246
|
+
Requires-Dist: iqm-pulse <13,>=12 ; extra == 'qiskit'
|
|
243
247
|
Requires-Dist: qiskit <=1.4.2,>=1.0 ; extra == 'qiskit'
|
|
244
248
|
Requires-Dist: qiskit-aer <0.18,>=0.13.1 ; extra == 'qiskit'
|
|
245
249
|
|
|
@@ -4,7 +4,7 @@ iqm/cirq_iqm/iqm_gates.py,sha256=xnZex5ZfNOk_WSsFjVCRybc14FlGNbmwOs3mIfOE_F8,248
|
|
|
4
4
|
iqm/cirq_iqm/iqm_sampler.py,sha256=uDlxd1cU7yKVYsj-wxfsUDkK9lDdHQzzN4mKonRFow8,11472
|
|
5
5
|
iqm/cirq_iqm/optimizers.py,sha256=Jcb6W7-M9wYa5mZztq33jQpWuIzD5ulE16ZperIc-wU,8566
|
|
6
6
|
iqm/cirq_iqm/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
-
iqm/cirq_iqm/serialize.py,sha256=
|
|
7
|
+
iqm/cirq_iqm/serialize.py,sha256=UwW9plc_qHwxa0DOVB8nSEx7wmzSZ4NgtUTlm77joYc,8516
|
|
8
8
|
iqm/cirq_iqm/transpiler.py,sha256=c3-KE4c-4WenbdOcBMnCS30Ynm4Wt35i-bl4iG8pKAs,2112
|
|
9
9
|
iqm/cirq_iqm/devices/__init__.py,sha256=WxbvNAqmeoV4mNy9M9IpjwmyNDpAP7ec1XFdjN7w_YA,840
|
|
10
10
|
iqm/cirq_iqm/devices/adonis.py,sha256=ZWaA4_OfO6fBcrLYHONTjtW8aSlVq2nwy9IiW529gQw,1390
|
|
@@ -21,12 +21,12 @@ iqm/iqm_client/__init__.py,sha256=D-8W54EcQIxk_1JZo_86GYlR1YitHhPIiFwwLJ2IfGE,14
|
|
|
21
21
|
iqm/iqm_client/api.py,sha256=_c6OVuv2dyzBF7J2XlK_qxisTSPyOiI4gYokZPsuaJY,3083
|
|
22
22
|
iqm/iqm_client/authentication.py,sha256=kHFqPI6w3OAk9k5ioPxi-FrD2EP-vjn8Z_wZYccJVyE,12259
|
|
23
23
|
iqm/iqm_client/errors.py,sha256=ty2P-sg80zlAoL3_kC3PlprgDUv4PI-KFhmmxaaapS0,1429
|
|
24
|
-
iqm/iqm_client/iqm_client.py,sha256=
|
|
25
|
-
iqm/iqm_client/models.py,sha256=
|
|
24
|
+
iqm/iqm_client/iqm_client.py,sha256=LtfznCt9RPvYn45mcVnfIWT8vekoZ548iWwl5iS318U,41798
|
|
25
|
+
iqm/iqm_client/models.py,sha256=hvnJYLOiycaXluWQgsip9CAnyb5l8i4xFYHt2qqUg7A,41831
|
|
26
26
|
iqm/iqm_client/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
27
|
-
iqm/iqm_client/transpile.py,sha256=
|
|
27
|
+
iqm/iqm_client/transpile.py,sha256=2hDxQK8F4eMgpJC06XMIA8YbguFnRVNMlE1iKQNiTZk,37268
|
|
28
28
|
iqm/iqm_client/util.py,sha256=obzh1g6PNEXOj7k3gUkiylNUhyqutbWlxlEpfyyU_fk,1505
|
|
29
|
-
iqm/iqm_client/validation.py,sha256=
|
|
29
|
+
iqm/iqm_client/validation.py,sha256=GAe5GQAEXyplbmS_ioIRLR4fhCYKSranB0vE06qMfe8,12192
|
|
30
30
|
iqm/iqm_client/cli/__init__.py,sha256=zzLDDz5rc3lJke3OKU8zxR5zQyQoM9oI2bLJ2YKk_zQ,692
|
|
31
31
|
iqm/iqm_client/cli/auth.py,sha256=kESEK9-vpEhrjba3Lb6Wqx24yGfbjxUASeCArnVRYrw,6364
|
|
32
32
|
iqm/iqm_client/cli/cli.py,sha256=vdhRJPKbqKRL0D_Z0uc3V73jQKKftAKE5Hx44oOCBwA,28689
|
|
@@ -36,15 +36,15 @@ iqm/qiskit_iqm/__init__.py,sha256=Mv9V_r8ZcmGC8Ke5S8-7yLOx02vjZ1qiVx8mtbOpwnY,14
|
|
|
36
36
|
iqm/qiskit_iqm/iqm_backend.py,sha256=HddizT6yHHq-MOG_U48n6ftE9AqmzaqbXYayEC1ljso,5548
|
|
37
37
|
iqm/qiskit_iqm/iqm_circuit.py,sha256=jaPo3zc5FC0vAIumh5d56fr44fDaJXXwcquBzQEy1Yg,1400
|
|
38
38
|
iqm/qiskit_iqm/iqm_circuit_validation.py,sha256=9pneZKs-KjBDGeDI6RHj6lB-ACqugbnYr1BqkJwLcXg,1737
|
|
39
|
-
iqm/qiskit_iqm/iqm_job.py,sha256=
|
|
39
|
+
iqm/qiskit_iqm/iqm_job.py,sha256=YLkAF4oqQ44dOLWbsqMiYCyj7pd3Rk6NIZ_TI1XtcQ8,11700
|
|
40
40
|
iqm/qiskit_iqm/iqm_move_layout.py,sha256=ECf1BcRmXKeClc7AL0lHedvJbqtwV5rEHcOOFR8shKU,10534
|
|
41
41
|
iqm/qiskit_iqm/iqm_naive_move_pass.py,sha256=jhTfvhrNDKt6NhhJg_3Y-5x6E1HRNzC_n4A27ZQTuvQ,12962
|
|
42
|
-
iqm/qiskit_iqm/iqm_provider.py,sha256=
|
|
42
|
+
iqm/qiskit_iqm/iqm_provider.py,sha256=gpQ8RLdjc0iyp1nSowiu7VoUrH3wZ9AgeJCHFKnu150,18232
|
|
43
43
|
iqm/qiskit_iqm/iqm_target.py,sha256=UyULiGMn6UJsyILBQiriso9KbhlmzP9TZItS2URaXWg,15832
|
|
44
44
|
iqm/qiskit_iqm/iqm_transpilation.py,sha256=6_6Mri01_HQBV_GTX94WSvIbu-pDMLMzEU6zVMEt6Gc,9153
|
|
45
45
|
iqm/qiskit_iqm/move_gate.py,sha256=UbrQSfrpVV3QKGJ93TelxEfZkl1wY4uWL8IH_QDpGUw,2840
|
|
46
46
|
iqm/qiskit_iqm/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
47
|
-
iqm/qiskit_iqm/qiskit_to_iqm.py,sha256=
|
|
47
|
+
iqm/qiskit_iqm/qiskit_to_iqm.py,sha256=3Sm1gLELFLCLf6QiATZSiPzLzkjvzUakhU2N6KoBXC8,14948
|
|
48
48
|
iqm/qiskit_iqm/transpiler_plugins.py,sha256=iuReGL42fCe5aOoH-KMUsb6t7Ok9qmIIj2S4yotJJ-U,8749
|
|
49
49
|
iqm/qiskit_iqm/examples/__init__.py,sha256=M4ElQHCo-WxtVXK39bF3QiFT3IGXPtZ1khqexHiTBEc,20
|
|
50
50
|
iqm/qiskit_iqm/examples/bell_measure.py,sha256=iMZB_MNMf2XP6Eiv2XbhtNs4bXbMGQeMw7ohw2JWKS8,1903
|
|
@@ -57,10 +57,10 @@ iqm/qiskit_iqm/fake_backends/fake_apollo.py,sha256=eT2vd3kQBi1rrvxCpePymBCfFK84d
|
|
|
57
57
|
iqm/qiskit_iqm/fake_backends/fake_deneb.py,sha256=RzQXmLXmBARDiMKVxk5Aw9fVbc6IYlW0A5jibk9iYD0,3156
|
|
58
58
|
iqm/qiskit_iqm/fake_backends/fake_garnet.py,sha256=GI0xafTCj1Um09qVuccO6GPOGBm6ygul_O40Wu220Ys,5555
|
|
59
59
|
iqm/qiskit_iqm/fake_backends/iqm_fake_backend.py,sha256=wJtfsxjPYbDKmzaz5R4AuaXvvPHa21WyPtRgNctL9eY,16785
|
|
60
|
-
iqm_client-
|
|
61
|
-
iqm_client-
|
|
62
|
-
iqm_client-
|
|
63
|
-
iqm_client-
|
|
64
|
-
iqm_client-
|
|
65
|
-
iqm_client-
|
|
66
|
-
iqm_client-
|
|
60
|
+
iqm_client-31.1.0.dist-info/AUTHORS.rst,sha256=qsxeK5A3-B_xK3hNbhFHEIkoHNpo7sdzYyRTs7Bdtm8,795
|
|
61
|
+
iqm_client-31.1.0.dist-info/LICENSE.txt,sha256=2DXrmQtVVUV9Fc9RBFJidMiTEaQlG2oAtlC9PMrEwTk,11333
|
|
62
|
+
iqm_client-31.1.0.dist-info/METADATA,sha256=hEG2wANHjcQrrGk3v9sTahl1E6DguwJozpTok8e7Ts0,17887
|
|
63
|
+
iqm_client-31.1.0.dist-info/WHEEL,sha256=y4mX-SOX4fYIkonsAGA5N0Oy-8_gI4FXw5HNI1xqvWg,91
|
|
64
|
+
iqm_client-31.1.0.dist-info/entry_points.txt,sha256=Kk2qfRwk8vbIJ7qCAvmaUogfRRn6t92_hBFhe6kqAE4,1317
|
|
65
|
+
iqm_client-31.1.0.dist-info/top_level.txt,sha256=NB4XRfyDS6_wG9gMsyX-9LTU7kWnTQxNvkbzIxGv3-c,4
|
|
66
|
+
iqm_client-31.1.0.dist-info/RECORD,,
|
|
File without changes
|