iqm-pulla 11.17.0__py3-none-any.whl → 12.0.1__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.
@@ -15,7 +15,7 @@
15
15
 
16
16
  This is the core module of ``CPC``. It contains the functionality to define a compiler, whose job is to
17
17
  convert quantum circuits and calibration data into configuration settings and instruction schedules that
18
- can be executed by the IQM server on quantum hardware.
18
+ can be executed by the IQM Server on quantum hardware.
19
19
  """
20
20
 
21
21
  from __future__ import annotations
@@ -42,7 +42,7 @@ from iqm.cpc.interface.compiler import (
42
42
  MoveGateFrameTrackingMode,
43
43
  MoveGateValidationMode,
44
44
  )
45
- from iqm.pulla.interface import CalibrationSet
45
+ from iqm.pulla.interface import CalibrationSetValues
46
46
  from iqm.pulla.utils import (
47
47
  _update_channel_props_from_calibration,
48
48
  build_settings,
@@ -181,13 +181,15 @@ class CompilationStage:
181
181
 
182
182
 
183
183
  class Compiler:
184
- """Stateful object that contains a calibration set, a schedule builder, and a set
184
+ """Compiles quantum circuits (via time boxes) into instruction schedules.
185
+
186
+ Stateful object that contains a calibration set, a schedule builder, and a set
185
187
  of compilation stages.
186
188
 
187
189
  The compiler's state does not include the data to be compiled.
188
190
 
189
191
  Args:
190
- calibration_set: Calibration data.
192
+ calibration_set_values: Calibration data.
191
193
  chip_topology: Physical layout and connectivity of the quantum chip.
192
194
  channel_properties: Control channel properties for the station.
193
195
  component_channels: Mapping from QPU component name to a mapping from ``('drive', 'flux', 'readout')``
@@ -210,7 +212,7 @@ class Compiler:
210
212
  def __init__( # noqa: PLR0913
211
213
  self,
212
214
  *,
213
- calibration_set: CalibrationSet,
215
+ calibration_set_values: CalibrationSetValues,
214
216
  chip_topology: ChipTopology,
215
217
  channel_properties: dict[str, ChannelProperties],
216
218
  component_channels: dict[str, dict[str, str]],
@@ -220,14 +222,14 @@ class Compiler:
220
222
  pp_stages: Collection[CompilationStage] | None = None,
221
223
  strict: bool = False, # consider extending to e.g. errors: Literal["raise", "warning", "ignore"] = "warning"
222
224
  ):
223
- self._calibration_set = calibration_set
225
+ self._calibration_set_values = calibration_set_values
224
226
  self.component_mapping = component_mapping
225
227
  self.options = options
226
228
  self.stages = stages or []
227
229
  self.pp_stages = pp_stages or []
228
230
 
229
231
  self.builder: ScheduleBuilder = initialize_schedule_builder(
230
- calibration_set, chip_topology, channel_properties, component_channels
232
+ calibration_set_values, chip_topology, channel_properties, component_channels
231
233
  )
232
234
  try:
233
235
  self.builder.validate_calibration()
@@ -241,11 +243,11 @@ class Compiler:
241
243
  Must be called automatically by any method that modifies the calibration set, or the op_table
242
244
  """
243
245
  _updated_channel_properties = _update_channel_props_from_calibration(
244
- self.builder.channels, self.builder.component_channels, self._calibration_set
246
+ self.builder.channels, self.builder.component_channels, self._calibration_set_values
245
247
  )
246
248
  self.builder = ScheduleBuilder(
247
249
  self.builder.op_table,
248
- calset_to_cal_data_tree(self._calibration_set),
250
+ calset_to_cal_data_tree(self._calibration_set_values),
249
251
  self.builder.chip_topology,
250
252
  _updated_channel_properties,
251
253
  self.builder.component_channels,
@@ -255,18 +257,18 @@ class Compiler:
255
257
  except ValueError as exc:
256
258
  raise CalibrationError(f"{exc}") from exc
257
259
 
258
- def get_calibration(self) -> CalibrationSet:
260
+ def get_calibration_set_values(self) -> CalibrationSetValues:
259
261
  """Returns a copy of the current local calibration set."""
260
- return deepcopy(self._calibration_set)
262
+ return deepcopy(self._calibration_set_values)
261
263
 
262
- def set_calibration(self, calibration: CalibrationSet) -> None:
264
+ def set_calibration_set_values(self, calibration_set_values: CalibrationSetValues) -> None:
263
265
  """Sets the current calibration set to a given calibration set, then refreshes the compiler.
264
266
 
265
267
  Args:
266
- calibration: The calibration set to be set as the current calibration set.
268
+ calibration_set_values: The calibration set to be set as the current calibration set.
267
269
 
268
270
  """
269
- self._calibration_set = calibration
271
+ self._calibration_set_values = calibration_set_values
270
272
  self._refresh()
271
273
 
272
274
  @property
@@ -323,7 +325,7 @@ class Compiler:
323
325
  locus_str = "__".join(locus)
324
326
  for param, value in params.items():
325
327
  path = f"gates.{gate_name}.{impl_name}.{locus_str}.{param}"
326
- self._calibration_set[path] = value
328
+ self._calibration_set_values[path] = value
327
329
 
328
330
  self._refresh()
329
331
 
@@ -422,7 +424,7 @@ class Compiler:
422
424
  Used automatically by :meth:`compile`.
423
425
  """
424
426
  return {
425
- "calibration_set": self._calibration_set,
427
+ "calibration_set": self._calibration_set_values,
426
428
  "builder": self.builder,
427
429
  "component_mapping": self.component_mapping,
428
430
  "options": self.options,
@@ -430,9 +432,7 @@ class Compiler:
430
432
  "chip_topology": self.builder.chip_topology,
431
433
  }
432
434
 
433
- def compile(
434
- self, data: Iterable[Any], context: dict[str, Any] | None = None
435
- ) -> tuple[Iterable[Any], dict[str, Any]]:
435
+ def compile(self, data: Iterable[Any], context: dict[str, Any] | None = None) -> tuple[Any, dict[str, Any]]:
436
436
  """Run all compiler stages.
437
437
 
438
438
  Initial context will be derived using :meth:`compiler_context` unless a custom
@@ -470,7 +470,7 @@ class Compiler:
470
470
 
471
471
  def run_stages(
472
472
  self, stages: Collection[CompilationStage], data: Iterable[Any], context: dict[str, Any]
473
- ) -> tuple[Iterable[Any], dict[str, Any]]:
473
+ ) -> tuple[Any, dict[str, Any]]:
474
474
  """Run the given stages in given order on the given data.
475
475
 
476
476
  Args:
@@ -514,20 +514,16 @@ class Compiler:
514
514
  calibration_set = context["calibration_set"]
515
515
  circuit_metrics = context["circuit_metrics"]
516
516
  options = context["options"]
517
- custom_settings = context.get("custom_settings")
518
517
  except Exception as exc:
519
518
  raise InsufficientContextError(f"Missing context data for building settings: {exc}") from exc
520
519
 
521
520
  settings = build_settings(
522
521
  shots=shots,
523
- calibration_set=calibration_set,
522
+ calibration_set_values=calibration_set,
524
523
  builder=builder,
525
524
  circuit_metrics=circuit_metrics,
526
525
  options=options,
527
526
  )
528
- # if custom_settings are given, use them to override similarly named generated settings
529
- if custom_settings is not None:
530
- settings = SettingNode.merge(custom_settings, settings)
531
527
 
532
528
  # fill in the schedule durations to the metrics
533
529
  end_delay = calibration_set["controllers.options.end_delay"]
iqm/cpc/compiler/dd.py CHANGED
@@ -18,7 +18,6 @@ import logging
18
18
  import math
19
19
 
20
20
  from iqm.cpc.compiler.errors import ClientError
21
- from iqm.cpc.interface.compiler import DDStrategy, PRXSequence
22
21
  from iqm.pulla.utils import (
23
22
  InstructionLocation,
24
23
  locate_instructions,
@@ -30,6 +29,7 @@ from iqm.pulse.playlist.instructions import Instruction, Wait
30
29
  from iqm.pulse.playlist.schedule import Schedule, Segment
31
30
  from iqm.pulse.scheduler import Block
32
31
  from iqm.pulse.timebox import TimeBox
32
+ from iqm.station_control.interface.models.circuit import DDStrategy, PRXSequence
33
33
 
34
34
  cpc_logger = logging.getLogger("cpc")
35
35
 
@@ -69,9 +69,11 @@ Breakdown of compiler passes of each stage:
69
69
  """
70
70
 
71
71
  from collections import Counter
72
- from collections.abc import Iterable
72
+ from collections.abc import Iterable, Sequence
73
73
  from copy import copy, deepcopy
74
- from typing import Any
74
+ from dataclasses import replace
75
+ import logging
76
+ from typing import Any, TypeAlias
75
77
 
76
78
  import numpy as np
77
79
 
@@ -105,7 +107,6 @@ from iqm.pulla.interface import (
105
107
  HERALDING_KEY,
106
108
  MEASUREMENT_MODE_KEY,
107
109
  RESTRICTED_MEASUREMENT_KEYS,
108
- CalibrationErrors,
109
110
  )
110
111
  from iqm.pulse.base_utils import merge_dicts
111
112
  from iqm.pulse.builder import CircuitOperation, ScheduleBuilder, validate_quantum_circuit
@@ -116,6 +117,12 @@ from iqm.pulse.playlist.playlist import Playlist
116
117
  from iqm.pulse.quantum_ops import QuantumOpTable
117
118
  from iqm.pulse.timebox import MultiplexedProbeTimeBox, TimeBox
118
119
 
120
+ CalibrationErrors: TypeAlias = dict[tuple[str, str, Locus], str]
121
+ """Map from OIL tuple to a CalibrationError message."""
122
+
123
+
124
+ logger = logging.getLogger(__name__)
125
+
119
126
 
120
127
  # Compilation functions
121
128
  def _map_old_operation_names(instructions: Iterable[CircuitOperation]) -> None:
@@ -519,8 +526,15 @@ def validate_execution_options(circuits: Iterable[Circuit_], options: CircuitExe
519
526
  MoveGateValidationMode.ALLOW_PRX,
520
527
  ]:
521
528
  raise CircuitError("Full MOVE gate frame tracking requires MOVE gate validation to be 'strict' or 'allow_prx'.")
529
+ if options.convert_terminal_measurements and options.active_reset_cycles is not None:
530
+ options = replace(options, convert_terminal_measurements=False)
531
+ logger.warning(
532
+ "When using active reset, the terminal measurements must also be calibrated to minimize leakage."
533
+ " Thus the terminal measurements cannot be converted to use the fidelity-optimized"
534
+ " `measure_fidelity` operation."
535
+ )
522
536
 
523
- return circuits
537
+ return circuits, {"options": options}
524
538
 
525
539
 
526
540
  @compiler_pass
@@ -764,7 +778,7 @@ def clean_schedule(schedules: Iterable[Schedule], builder: ScheduleBuilder) -> l
764
778
 
765
779
 
766
780
  @compiler_pass
767
- def build_playlist(schedules: Iterable[Schedule], builder: ScheduleBuilder) -> tuple[Playlist, dict[str, Any]]:
781
+ def build_playlist(schedules: Sequence[Schedule], builder: ScheduleBuilder) -> tuple[Playlist, dict[str, Any]]:
768
782
  """Build the playlist from the schedules."""
769
783
  playlist = builder.build_playlist(schedules)[0]
770
784
  return playlist, {"schedules": schedules} # save the schedules for building settings, debugging, etc
@@ -77,7 +77,7 @@ from typing import TYPE_CHECKING
77
77
  from exa.common.data.parameter import CollectionType, DataType, Parameter, Setting
78
78
  from exa.common.data.setting_node import SettingNode
79
79
  from iqm.cpc.compiler.errors import CalibrationError
80
- from iqm.pulla.interface import CalibrationSet
80
+ from iqm.pulla.interface import CalibrationSetValues
81
81
 
82
82
  if TYPE_CHECKING:
83
83
  from exa.common.data.value import ObservationValue
@@ -118,7 +118,7 @@ class Map:
118
118
 
119
119
  def find_observation(
120
120
  observation_path: str,
121
- calibration_set: CalibrationSet,
121
+ calibration_set_values: CalibrationSetValues,
122
122
  *,
123
123
  required: bool = True,
124
124
  ) -> ObservationValue:
@@ -126,7 +126,7 @@ def find_observation(
126
126
 
127
127
  Args:
128
128
  observation_path: observation we want to find in ``calibration_set``
129
- calibration_set: mapping of observation paths to observation values
129
+ calibration_set_values: mapping of observation paths to observation values
130
130
  required: iff ``True`` and the observation cannot be found, raise an error
131
131
 
132
132
  Returns:
@@ -137,7 +137,7 @@ def find_observation(
137
137
  found in ``calibration_set``
138
138
 
139
139
  """
140
- obs_value = calibration_set.get(observation_path)
140
+ obs_value = calibration_set_values.get(observation_path)
141
141
  if obs_value is None and required:
142
142
  raise CalibrationError(f"Missing calibration observation: {observation_path}")
143
143
  return obs_value # type: ignore[return-value]
@@ -371,7 +371,7 @@ def build_station_settings(
371
371
  circuit_couplers: Iterable[str],
372
372
  measured_probe_lines: Iterable[str],
373
373
  shots: int,
374
- calibration_set: CalibrationSet,
374
+ calibration_set_values: CalibrationSetValues,
375
375
  boundary_qubits: Iterable[str],
376
376
  boundary_couplers: Iterable[str],
377
377
  flux_pulsed_qubits: Collection[str],
@@ -384,7 +384,7 @@ def build_station_settings(
384
384
  circuit_couplers: coupler names used in the circuit
385
385
  measured_probe_lines: probe line names used in the measurements
386
386
  shots: number of times to repeat each circuit's execution
387
- calibration_set: calibration set as a mapping from observation paths to observation values
387
+ calibration_set_values: calibration set as a mapping from observation paths to observation values
388
388
  boundary_qubits: physical qubits connected to the boundary_couplers but not in circuit_qubits
389
389
  boundary_couplers: coupler names of couplers connected to the circuit boundary but not in circuit_couplers
390
390
  flux_pulsed_qubits: names of qubits that have flux pulse capability
@@ -406,7 +406,9 @@ def build_station_settings(
406
406
 
407
407
  """
408
408
  for obs_map in observation_maps:
409
- value = find_observation(obs_map.observation_path(component), calibration_set, required=obs_map.required)
409
+ value = find_observation(
410
+ obs_map.observation_path(component), calibration_set_values, required=obs_map.required
411
+ )
410
412
  if value is not None:
411
413
  _create_and_add_setting(obs_map.settings_path(component), obs_map.parameter, value, root)
412
414
 
@@ -22,6 +22,13 @@ from exa.common.data.setting_node import SettingNode
22
22
  from iqm.pulse import Circuit
23
23
  from iqm.pulse.builder import Locus
24
24
  from iqm.pulse.playlist.playlist import Playlist
25
+ from iqm.station_control.interface.models import (
26
+ DDMode,
27
+ DDStrategy,
28
+ HeraldingMode,
29
+ MoveGateFrameTrackingMode,
30
+ MoveGateValidationMode,
31
+ )
25
32
 
26
33
  CircuitBatch: TypeAlias = list[Circuit]
27
34
  """Type that represents a list of quantum circuits to be executed together in a single batch."""
@@ -42,78 +49,6 @@ class MeasurementMode(StrEnum):
42
49
  This is typically how measurement is calibrated."""
43
50
 
44
51
 
45
- class HeraldingMode(StrEnum):
46
- """Heralding mode for circuit execution."""
47
-
48
- NONE = "none"
49
- """Do not do any heralding."""
50
- ZEROS = "zeros"
51
- """Perform a heralding measurement on all the components used in each circuit (if they have
52
- measurement data available in the calset), only retain shots where all the components
53
- are measured to be in the zero state."""
54
-
55
-
56
- class DDMode(StrEnum):
57
- """Dynamical Decoupling (DD) mode for circuit execution."""
58
-
59
- DISABLED = "disabled"
60
- """Do not apply dynamical decoupling."""
61
- ENABLED = "enabled"
62
- """Apply dynamical decoupling."""
63
-
64
-
65
- PRXSequence: TypeAlias = list[tuple[float, float]]
66
- """A sequence of PRX gates. A generic PRX gate is defined by rotation angle and phase angle, Theta and Phi
67
- respectively."""
68
-
69
-
70
- @dataclass
71
- class DDStrategy:
72
- """Describes a particular dynamical decoupling strategy.
73
-
74
- The current standard DD stategy can be found in :attr:`.STANDARD_DD_STRATEGY`,
75
- but users can use this class to provide their own dynamical decoupling strategies.
76
-
77
- See :cite:`Ezzell_2022` for information on DD sequences.
78
- """
79
-
80
- merge_contiguous_waits: bool = True
81
- """Merge contiguous ``Wait`` instructions into one if they are separated only by ``Block`` instructions."""
82
-
83
- target_qubits: frozenset[str] | None = None
84
- """Qubits on which dynamical decoupling should be applied. If ``None``, all qubits are targeted."""
85
-
86
- skip_leading_wait: bool = True
87
- """Skip processing leading ``Wait`` instructions."""
88
-
89
- skip_trailing_wait: bool = True
90
- """Skip processing trailing ``Wait`` instructions."""
91
-
92
- gate_sequences: list[tuple[int, str | PRXSequence, str]] = field(default_factory=list)
93
- """Available decoupling gate sequences to chose from in this strategy.
94
-
95
- Each sequence is defined by a tuple of ``(ratio, gate pattern, align)``:
96
-
97
- * ratio: Minimal duration for the sequence (in PRX gate durations).
98
-
99
- * gate pattern: Gate pattern can be defined in two ways. It can be a string containing "X" and "Y" characters,
100
- encoding a PRX gate sequence. For example, "YXYX" corresponds to the
101
- XY4 sequence, "XYXYYXYX" to the EDD sequence, etc. If more flexibility is needed, a gate pattern can be
102
- defined as a sequence of PRX gate argument tuples (that contain a rotation angle and a phase angle). For
103
- example, sequence "YX" could be written as ``[(math.pi, math.pi / 2), (math.pi, 0)]``.
104
-
105
- * align: Controls the alignment of the sequence within the time window it is inserted in. Supported values:
106
-
107
- - "asap": Corresponds to a ASAP-aligned sequence with no waiting time before the first pulse.
108
- - "center": Corresponds to a symmetric sequence.
109
- - "alap": Corresponds to a ALAP-aligned sequence.
110
-
111
- The Dynamical Decoupling algorithm uses the best fitting gate sequence by first sorting them
112
- by ``ratio`` in descending order. Then the longest fitting pattern is determined by comparing ``ratio``
113
- with the duration of the time window divided by the PRX gate duration.
114
- """
115
-
116
-
117
52
  class CircuitBoundaryMode(StrEnum):
118
53
  """Circuit boundary mode for circuit compilation."""
119
54
 
@@ -130,32 +65,6 @@ class CircuitBoundaryMode(StrEnum):
130
65
  """Circuit boundary consists of all the QPU elements that are not used in the circuit."""
131
66
 
132
67
 
133
- class MoveGateValidationMode(StrEnum):
134
- """MOVE gate validation mode for circuit compilation."""
135
-
136
- STRICT = "strict"
137
- """Perform standard MOVE gate validation: MOVE(qubit, resonator) gates must only
138
- appear in sandwiches (pairs). Inside a sandwich there must be no gates acting on the
139
- MOVE qubit, and no other MOVE gates acting on the resonator."""
140
- ALLOW_PRX = "allow_prx"
141
- """Allow PRX gates on the MOVE qubit inside MOVE sandwiches during validation."""
142
- NONE = "none"
143
- """Do not perform any MOVE gate validation."""
144
-
145
-
146
- class MoveGateFrameTrackingMode(StrEnum):
147
- """MOVE gate frame tracking mode for circuit compilation."""
148
-
149
- FULL = "full"
150
- """Perform complete MOVE gate frame tracking, applying both the explicit z rotations
151
- on the resonator and the dynamic phase correction due to qubit-resonator detuning to
152
- the qubit at the end of a MOVE sandwich."""
153
- NO_DETUNING_CORRECTION = "no_detuning_correction"
154
- """Do not apply the detuning correction at the end of a MOVE sandwich."""
155
- NONE = "none"
156
- """Do not perform any MOVE gate frame tracking."""
157
-
158
-
159
68
  @dataclass(frozen=True)
160
69
  class CircuitExecutionOptions:
161
70
  """Various discrete options for quantum circuit execution."""
iqm/pulla/calibration.py CHANGED
@@ -16,13 +16,13 @@
16
16
 
17
17
  from copy import deepcopy
18
18
  import logging
19
- import uuid
19
+ from uuid import UUID
20
20
 
21
- from iqm.pulla.interface import CalibrationSet, CalibrationSetId
21
+ from iqm.iqm_server_client.iqm_server_client import _IQMServerClient
22
+
23
+ from iqm.pulla.interface import CalibrationSetValues
22
24
  from iqm.pulla.utils import calset_from_observations
23
- from iqm.station_control.client.iqm_server.iqm_server_client import IqmServerClient
24
- from iqm.station_control.interface.models import ObservationSetData
25
- from iqm.station_control.interface.station_control import StationControlInterface
25
+ from iqm.station_control.interface.models import StrUUID
26
26
 
27
27
  logger = logging.getLogger(__name__)
28
28
 
@@ -30,66 +30,45 @@ CalibrationDataFetchException = RuntimeError
30
30
 
31
31
 
32
32
  class CalibrationDataProvider:
33
- """Access calibration info via station control client and cache data in memory."""
33
+ """Access calibration info via IQM Server and cache data in memory."""
34
34
 
35
- def __init__(self, station_control: StationControlInterface):
36
- self._station_control = station_control
37
- self._calibration_sets: dict[CalibrationSetId, CalibrationSet] = {}
35
+ def __init__(self, iqm_server_client: _IQMServerClient):
36
+ self._iqm_server_client = iqm_server_client
37
+ self._calibration_sets: dict[UUID, CalibrationSetValues] = {}
38
38
 
39
- def get_calibration_set(self, cal_set_id: CalibrationSetId) -> CalibrationSet:
39
+ def get_calibration_set_values(self, calibration_set_id: UUID) -> CalibrationSetValues:
40
40
  """Get the calibration set contents from the database and cache it."""
41
- logger.debug("Get the calibration set from the database: cal_set_id=%s", cal_set_id)
41
+ logger.debug("Get the calibration set from the database: cal_set_id=%s", calibration_set_id)
42
42
  try:
43
- if cal_set_id not in self._calibration_sets:
44
- cal_set_values = self.get_calibration_set_values(cal_set_id)
45
- self._calibration_sets[cal_set_id] = cal_set_values
46
- return deepcopy(self._calibration_sets[cal_set_id])
43
+ if calibration_set_id not in self._calibration_sets:
44
+ cal_set_values = self._get_calibration_set_values(calibration_set_id)
45
+ self._calibration_sets[calibration_set_id] = cal_set_values
46
+ return deepcopy(self._calibration_sets[calibration_set_id])
47
47
  except Exception as e:
48
48
  raise CalibrationDataFetchException("Could not fetch calibration set from the database.") from e
49
49
 
50
- def get_latest_calibration_set(self, chip_label: str) -> tuple[CalibrationSet, CalibrationSetId]:
51
- """Get the latest calibration set id for ``chip_label`` from the database, return it and the set contents."""
52
- logger.debug("Get the latest calibration set for chip label: chip_label=%s", chip_label)
50
+ def get_default_calibration_set(self) -> tuple[CalibrationSetValues, UUID]:
51
+ """Get the default calibration set id from the database, return it and the set contents."""
52
+ logger.debug("Get the default calibration set")
53
53
  try:
54
- if isinstance(self._station_control, IqmServerClient):
55
- latest_cal_set_id = self._station_control.get_latest_calibration_set_id(chip_label)
56
- else:
57
- latest_calibration_set = self._get_latest_calibration_set(chip_label)
58
- latest_cal_set_id = latest_calibration_set.observation_set_id
59
- calset = self.get_calibration_set(latest_cal_set_id)
54
+ default_calibration_set = self._iqm_server_client.get_calibration_set("default")
55
+ default_calibration_set_values = self.get_calibration_set_values(default_calibration_set.observation_set_id)
60
56
  except Exception as e:
61
57
  raise CalibrationDataFetchException(
62
- f"Could not fetch latest calibration set id from the database: {e}"
58
+ f"Could not fetch default calibration set id from the database: {e}"
63
59
  ) from e
64
- return calset, latest_cal_set_id
65
-
66
- def _get_latest_calibration_set(self, dut_label: str) -> ObservationSetData:
67
- observation_sets = self._station_control.query_observation_sets(
68
- observation_set_type="calibration-set",
69
- dut_label=dut_label,
70
- invalid=False,
71
- end_timestamp__isnull=False, # Finalized
72
- order_by="-end_timestamp",
73
- limit=1,
74
- )
75
- return observation_sets[0]
60
+ return default_calibration_set_values, default_calibration_set.observation_set_id
76
61
 
77
- def get_calibration_set_values(self, calibration_set_id: uuid.UUID) -> CalibrationSet:
62
+ def _get_calibration_set_values(self, calibration_set_id: StrUUID) -> CalibrationSetValues:
78
63
  """Get saved calibration set observations by UUID.
79
64
 
80
65
  Args:
81
- calibration_set_id: UUID of the calibration set to retrieve.
66
+ calibration_set_id: UUID of the calibration set to retrieve or "default".
82
67
 
83
68
  Returns:
84
69
  Dictionary of observations belonging to the given calibration set.
85
70
 
86
71
  """
87
- if isinstance(self._station_control, IqmServerClient):
88
- calibration_set_values = self._station_control.get_calibration_set_values(calibration_set_id)
89
- else:
90
- observation_set = self._station_control.get_observation_set(calibration_set_id)
91
- if observation_set.observation_set_type != "calibration-set":
92
- raise ValueError("Observation set type is not 'calibration-set'")
93
- observations = self._station_control.get_observation_set_observations(calibration_set_id)
94
- calibration_set_values = calset_from_observations(observations)
72
+ calibration_set = self._iqm_server_client.get_calibration_set(calibration_set_id)
73
+ calibration_set_values = calset_from_observations(calibration_set.observations)
95
74
  return calibration_set_values
iqm/pulla/interface.py CHANGED
@@ -12,18 +12,11 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- """Common data types and exceptions for the IQM Pulla interface.
15
+ """Common data types and exceptions for the IQM Pulla interface."""
16
16
 
17
- Many of these must be identical to those in iqm-client.
18
- """
19
-
20
- from dataclasses import dataclass
21
- from enum import StrEnum
22
17
  from typing import TypeAlias
23
- from uuid import UUID
24
18
 
25
19
  from exa.common.data.value import ObservationValue
26
- from iqm.cpc.interface.compiler import Locus
27
20
 
28
21
 
29
22
  class CHADRetrievalException(Exception):
@@ -38,56 +31,8 @@ class ChipLabelRetrievalException(Exception):
38
31
  """Exception for chip label retrieval failures."""
39
32
 
40
33
 
41
- # Map from OIL tuple to a CalibrationError.
42
- CalibrationErrors: TypeAlias = dict[tuple[str, str, Locus], str]
43
-
44
-
45
- class TaskStatus(StrEnum):
46
- """Status of a Station Control task."""
47
-
48
- READY = "READY"
49
- """Task has completed successfully"""
50
-
51
- FAILED = "FAILED"
52
- """Task has failed"""
53
-
54
- PROGRESS = "PROGRESS"
55
- """Task is being executed"""
56
-
57
- PENDING = "PENDING"
58
- """Task is waiting to be executed"""
59
-
60
-
61
- CalibrationSet: TypeAlias = dict[str, ObservationValue]
62
- CalibrationSetId: TypeAlias = UUID
63
-
64
- CircuitMeasurementResults: TypeAlias = dict[str, list[list[int]]]
65
- """Measurement results from a single circuit/schedule. For each measurement operation in the circuit,
66
- maps the measurement key to the corresponding results. The outer list elements correspond to shots,
67
- and the inner list elements to the qubits measured in the measurement operation."""
68
-
69
- CircuitMeasurementResultsBatch: TypeAlias = list[CircuitMeasurementResults]
70
- """Type that represents measurement results for a batch of circuits."""
71
-
72
-
73
- @dataclass
74
- class StationControlResult:
75
- """Result of a station control task"""
76
-
77
- sweep_id: UUID
78
- """ID of the executed sweep"""
79
- task_id: UUID # TODO? Rename to job_id
80
- """ID of the station control task"""
81
- status: TaskStatus
82
- """Status of the station control task"""
83
- start_time: str | None = None
84
- """Time when the sweep began in the station control"""
85
- end_time: str | None = None
86
- """Time when the sweep ended in the station control"""
87
- result: CircuitMeasurementResultsBatch | None = None
88
- """Sweep results converted to the circuit measurement results expected by the client"""
89
- message: str | None = None
90
- """Information about task failure"""
34
+ CalibrationSetValues: TypeAlias = dict[str, ObservationValue]
35
+ """Map from observation name to its value."""
91
36
 
92
37
 
93
38
  ACQUISITION_LABEL_KEY = "m{idx}"