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.
@@ -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[Instruction],
96
+ instructions: Iterable[CircuitOperation],
97
97
  qubit_mapping: dict[str, str],
98
98
  inverse: bool = False,
99
- ) -> tuple[Instruction, ...]:
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
- inst.model_copy(update={"qubits": tuple(qubit_mapping[q] for q in inst.qubits)}) for inst in instructions
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[Instruction]) -> _ResonatorStateTracker:
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.qubits
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[Instruction]:
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 Instruction(name=self.move_gate, qubits=locus, args={})
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 Instruction(name=self.move_gate, qubits=locus, args={})
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 Instruction(name=self.move_gate, qubits=locus, args={})
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[Instruction]:
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[Instruction] = []
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(Instruction(name=self.move_gate, qubits=locus, args={}))
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: Instruction) -> list[Resolution]:
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.qubits
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: Instruction, lookahead: Iterable[Instruction]) -> Resolution | None:
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, Instruction] = {}
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.qubits:
427
- if q in inst.qubits:
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.qubits) == 1:
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: Instruction) -> list[Instruction]:
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[Instruction] = []
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.model_copy(update={"qubits": (g, r)}))
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[Instruction],
571
+ instructions: Sequence[CircuitOperation],
566
572
  arch: DynamicQuantumArchitecture,
567
- ) -> list[Instruction]:
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[Instruction] = []
596
+ new_instructions: list[CircuitOperation] = []
591
597
 
592
598
  for idx, inst in enumerate(instructions):
593
- locus = inst.qubits
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.qubits)
837
+ tracker.apply_move(*inst.locus)
832
838
  else:
833
839
  # map the instruction locus
834
- new_qubits = tracker.map_resonators_in_locus(inst.qubits)
840
+ new_locus = tracker.map_resonators_in_locus(inst.locus)
835
841
  new_instructions.append(
836
- Instruction(name=inst.name, implementation=inst.implementation, qubits=new_qubits, args=inst.args)
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)
@@ -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.all_qubits() - set(qubit_mapping)
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: 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.qubits) if qubit_mapping else instruction.qubits
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.qubits, mapped_qubits):
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
- if op_info.no_calibration_needed:
150
- # all QPU loci are allowed
151
- check_locus_components(architecture.components, msg="does not exist on the QPU")
152
- return
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.qubits} = {tuple(mapped_qubits)} is not allowed as locus for '{instruction_name}'"
194
+ f"{instruction.locus} = {tuple(mapped_qubits)} is not allowed as locus for '{instruction_name}'"
191
195
  if qubit_mapping
192
- else f"{instruction.qubits} is not allowed as locus for '{instruction_name}'"
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.qubits
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.qubits}."
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.qubits}: state of {qubit} is "
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.qubits} to an already occupied resonator: {resonator_occupations}."
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.qubits) & moved_qubits:
276
+ if overlap := set(inst.locus) & moved_qubits:
273
277
  raise CircuitValidationError(
274
- f"Instruction {inst.name} acts on {inst.qubits} while the state(s) of {overlap} "
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
 
@@ -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
@@ -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[Instruction]:
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[Instruction] = []
120
+ instructions: list[CircuitOperation] = []
120
121
  # maps clbits to the latest "measure" instruction to store its result there
121
- clbit_to_measure: dict[Clbit, Instruction] = {}
122
+ clbit_to_measure: dict[Clbit, CircuitOperation] = {}
122
123
  for circuit_instruction in circuit.data:
123
124
  instruction = circuit_instruction.operation
124
- qubit_names = [qubit_index_to_name[circuit.find_bit(qubit).index] for qubit in circuit_instruction.qubits]
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
- angle_t = float(instruction.params[0] / (2 * np.pi))
127
- phase_t = float(instruction.params[1] / (2 * np.pi))
128
- native_inst = Instruction(name="prx", qubits=qubit_names, args={"angle_t": angle_t, "phase_t": phase_t})
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 = Instruction(name="prx", qubits=qubit_names, args={"angle_t": 0.5, "phase_t": 0.0})
131
+ native_inst = CircuitOperation(name="prx", locus=qubit_names, args={"angle": pi, "phase": 0.0})
131
132
  elif instruction.name == "rx":
132
- angle_t = float(instruction.params[0] / (2 * np.pi))
133
- native_inst = Instruction(name="prx", qubits=qubit_names, args={"angle_t": angle_t, "phase_t": 0.0})
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 = Instruction(name="prx", qubits=qubit_names, args={"angle_t": 0.5, "phase_t": 0.25})
136
+ native_inst = CircuitOperation(name="prx", locus=qubit_names, args={"angle": pi, "phase": 0.5 * pi})
136
137
  elif instruction.name == "ry":
137
- angle_t = float(instruction.params[0] / (2 * np.pi))
138
- native_inst = Instruction(name="prx", qubits=qubit_names, args={"angle_t": angle_t, "phase_t": 0.25})
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 = Instruction(name="cz", qubits=qubit_names, args={})
141
+ native_inst = CircuitOperation(name="cz", locus=qubit_names, args={})
141
142
  elif instruction.name == "move":
142
- native_inst = Instruction(name="move", qubits=qubit_names, args={})
143
+ native_inst = CircuitOperation(name="move", locus=qubit_names, args={})
143
144
  elif instruction.name == "barrier":
144
- native_inst = Instruction(name="barrier", qubits=qubit_names, args={})
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 = Instruction(name="delay", qubits=qubit_names, args={"duration": duration})
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 = Instruction(name="measure", qubits=qubit_names, args={"key": mk})
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 = Instruction(name="reset", qubits=qubit_names, args={})
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 = Instruction.model_construct(name=instruction.name, qubits=tuple(qubit_names), args=args)
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.qubits[0] # single-qubit measurement
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[Instruction], qubit_name_to_index: dict[str, int], layout: Layout
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.qubits]
280
+ locus = [index_to_qiskit_qubit[qubit_name_to_index[q]] for q in instr.locus]
280
281
  if instr.name == "prx":
281
- angle_t = instr.args["angle_t"] * 2 * np.pi
282
- phase_t = instr.args["phase_t"] * 2 * np.pi
283
- circuit.r(angle_t, phase_t, locus[0])
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
- angle_t = instr.args["angle_t"] * 2 * np.pi
298
- phase_t = instr.args["phase_t"] * 2 * np.pi
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(angle_t, phase_t, locus[0]).c_if(fk_to_clbit[feedback_key], 1)
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: 30.2.0
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,>=9
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,>=9 ; extra == 'cirq'
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,>=9 ; extra == 'cli'
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,>=9 ; extra == 'qiskit'
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=tRkNrzef24va8UMq_Z-TKCsmP7xXtHm3R5qB2VyiIt4,8141
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=oEeosKvXd_lVreB0r7TwHnYs6AAOjJs1sUDNk-AtzAk,41793
25
- iqm/iqm_client/models.py,sha256=DldEBjU3oHG4_1m5IPP_veZP9ZFOnVvmpsPipsSyt78,51693
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=eEv9eY5QG94Lke7Xp6BkQapl1rvlmlVQ_IkQFopPNQ8,36981
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=IKXBxnhLLnEuUCU2J0q6VEYn9k-O9z_MI4ys7mpFZdE,11912
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=A8IvhsU_DTGMG2itBYXRMyQcOZSQ-48IE5ltkzbuc6k,11682
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=56WzHzfa3FeOdMB7RRARgSEc6KRU0I7ozX1iCiTyMFw,18214
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=9JGcR_7K1Y5W6_PBP1bVCZqy7khCOa-BU9m1I9MJB5k,15056
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-30.2.0.dist-info/AUTHORS.rst,sha256=qsxeK5A3-B_xK3hNbhFHEIkoHNpo7sdzYyRTs7Bdtm8,795
61
- iqm_client-30.2.0.dist-info/LICENSE.txt,sha256=2DXrmQtVVUV9Fc9RBFJidMiTEaQlG2oAtlC9PMrEwTk,11333
62
- iqm_client-30.2.0.dist-info/METADATA,sha256=nXvgyDgwhFoCjrAsin1iwwp8ooLY1yM6IVMfEaYLm-4,17692
63
- iqm_client-30.2.0.dist-info/WHEEL,sha256=y4mX-SOX4fYIkonsAGA5N0Oy-8_gI4FXw5HNI1xqvWg,91
64
- iqm_client-30.2.0.dist-info/entry_points.txt,sha256=Kk2qfRwk8vbIJ7qCAvmaUogfRRn6t92_hBFhe6kqAE4,1317
65
- iqm_client-30.2.0.dist-info/top_level.txt,sha256=NB4XRfyDS6_wG9gMsyX-9LTU7kWnTQxNvkbzIxGv3-c,4
66
- iqm_client-30.2.0.dist-info/RECORD,,
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,,