iqm-pulse 9.19.0__tar.gz → 9.21.0__tar.gz

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.
Files changed (82) hide show
  1. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/CHANGELOG.rst +16 -0
  2. {iqm_pulse-9.19.0/src/iqm_pulse.egg-info → iqm_pulse-9.21.0}/PKG-INFO +2 -1
  3. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/pyproject.toml +8 -0
  4. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/requirements/base.in +1 -0
  5. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/requirements/base.txt +2 -1
  6. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/src/iqm/pulse/builder.py +8 -6
  7. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/src/iqm/pulse/circuit_operations.py +1 -1
  8. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/src/iqm/pulse/gate_implementation.py +2 -2
  9. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/src/iqm/pulse/gates/__init__.py +6 -6
  10. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/src/iqm/pulse/gates/conditional.py +2 -2
  11. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/src/iqm/pulse/gates/cz.py +5 -5
  12. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/src/iqm/pulse/gates/default_gates.py +36 -30
  13. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/src/iqm/pulse/gates/flux_multiplexer.py +2 -2
  14. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/src/iqm/pulse/gates/measure.py +17 -15
  15. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/src/iqm/pulse/gates/move.py +1 -1
  16. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/src/iqm/pulse/gates/prx.py +19 -19
  17. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/src/iqm/pulse/gates/reset.py +4 -4
  18. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/src/iqm/pulse/gates/rz.py +6 -6
  19. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/src/iqm/pulse/gates/sx.py +2 -2
  20. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/src/iqm/pulse/gates/u.py +5 -3
  21. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/src/iqm/pulse/quantum_ops.py +1 -1
  22. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/src/iqm/pulse/scheduler.py +3 -3
  23. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/src/iqm/pulse/timebox.py +1 -1
  24. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0/src/iqm_pulse.egg-info}/PKG-INFO +2 -1
  25. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/src/iqm_pulse.egg-info/SOURCES.txt +1 -0
  26. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/src/iqm_pulse.egg-info/requires.txt +1 -0
  27. iqm_pulse-9.21.0/tests/__init__.py +0 -0
  28. iqm_pulse-9.21.0/version.txt +1 -0
  29. iqm_pulse-9.19.0/version.txt +0 -1
  30. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/LICENSE.txt +0 -0
  31. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/MANIFEST.in +0 -0
  32. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/README.rst +0 -0
  33. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/docs/API.rst +0 -0
  34. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/docs/Makefile +0 -0
  35. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/docs/_static/.gitignore +0 -0
  36. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/docs/_static/css/custom.css +0 -0
  37. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/docs/_static/images/favicon.ico +0 -0
  38. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/docs/_static/images/feedback_timing.svg +0 -0
  39. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/docs/_static/images/logo.png +0 -0
  40. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/docs/_static/images/playlist_breakdown.svg +0 -0
  41. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/docs/_static/images/pulse_timing.svg +0 -0
  42. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/docs/_static/images/readout_timing.svg +0 -0
  43. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/docs/_templates/autosummary-class-template.rst +0 -0
  44. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/docs/_templates/autosummary-module-template.rst +0 -0
  45. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/docs/changelog.rst +0 -0
  46. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/docs/concepts.rst +0 -0
  47. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/docs/conf.py +0 -0
  48. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/docs/custom_gates.rst +0 -0
  49. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/docs/index.rst +0 -0
  50. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/docs/license.rst +0 -0
  51. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/docs/pulse_timing.rst +0 -0
  52. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/docs/references.bib +0 -0
  53. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/docs/references.rst +0 -0
  54. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/docs/using_builder.rst +0 -0
  55. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/setup.cfg +0 -0
  56. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/setup.py +0 -0
  57. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/src/iqm/pulse/__init__.py +0 -0
  58. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/src/iqm/pulse/base_utils.py +0 -0
  59. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/src/iqm/pulse/gates/barrier.py +0 -0
  60. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/src/iqm/pulse/gates/delay.py +0 -0
  61. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/src/iqm/pulse/gates/enums.py +0 -0
  62. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/src/iqm/pulse/playlist/__init__.py +0 -0
  63. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/src/iqm/pulse/playlist/channel.py +0 -0
  64. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/src/iqm/pulse/playlist/fast_drag.py +0 -0
  65. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/src/iqm/pulse/playlist/hd_drag.py +0 -0
  66. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/src/iqm/pulse/playlist/instructions.py +0 -0
  67. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/src/iqm/pulse/playlist/playlist.py +0 -0
  68. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/src/iqm/pulse/playlist/schedule.py +0 -0
  69. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/src/iqm/pulse/playlist/visualisation/__init__.py +0 -0
  70. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/src/iqm/pulse/playlist/visualisation/base.py +0 -0
  71. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/src/iqm/pulse/playlist/visualisation/templates/playlist_inspection.jinja2 +0 -0
  72. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/src/iqm/pulse/playlist/visualisation/templates/static/logo.png +0 -0
  73. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/src/iqm/pulse/playlist/visualisation/templates/static/moment.min.js +0 -0
  74. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/src/iqm/pulse/playlist/visualisation/templates/static/vis-timeline-graph2d.min.css +0 -0
  75. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/src/iqm/pulse/playlist/visualisation/templates/static/vis-timeline-graph2d.min.js +0 -0
  76. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/src/iqm/pulse/playlist/waveforms.py +0 -0
  77. /iqm_pulse-9.19.0/tests/__init__.py → /iqm_pulse-9.21.0/src/iqm/pulse/py.typed +0 -0
  78. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/src/iqm/pulse/utils.py +0 -0
  79. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/src/iqm/pulse/validation.py +0 -0
  80. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/src/iqm_pulse.egg-info/dependency_links.txt +0 -0
  81. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/src/iqm_pulse.egg-info/top_level.txt +0 -0
  82. {iqm_pulse-9.19.0 → iqm_pulse-9.21.0}/tests/.pylintrc +0 -0
@@ -2,6 +2,22 @@
2
2
  Changelog
3
3
  =========
4
4
 
5
+ Version 9.21.0 (2025-07-10)
6
+ ===========================
7
+
8
+ Bug fixes
9
+ ---------
10
+
11
+ - Fix instructions with same field names being treated as equal in building the playlist
12
+
13
+ Version 9.20.0 (2025-07-09)
14
+ ===========================
15
+
16
+ Features
17
+ --------
18
+
19
+ - Enable mypy type checking in CI and add temporary type ignores to the source code. :issue:`SW-1615`
20
+
5
21
  Version 9.19.0 (2025-07-08)
6
22
  ===========================
7
23
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: iqm-pulse
3
- Version: 9.19.0
3
+ Version: 9.21.0
4
4
  Summary: A Python-based project for providing interface and implementations for control pulses.
5
5
  Author-email: IQM Finland Oy <info@meetiqm.com>
6
6
  License: Apache License
@@ -219,6 +219,7 @@ Requires-Dist: python-rapidjson==1.20
219
219
  Requires-Dist: jinja2==3.0.3
220
220
  Requires-Dist: numpy<3.0,>=1.26.4
221
221
  Requires-Dist: scipy<1.16,>=1.11.4
222
+ Requires-Dist: scipy-stubs
222
223
 
223
224
  IQM Pulse library
224
225
  =================
@@ -21,6 +21,14 @@ file = "LICENSE.txt"
21
21
  Documentation = "https://iqm-finland.github.io/docs/iqm-pulse/"
22
22
  Homepage = "https://pypi.org/project/iqm-pulse/"
23
23
 
24
+ [tool.mypy]
25
+ explicit_package_bases = true
26
+ mypy_path = "$MYPY_CONFIG_FILE_DIR/src"
27
+ namespace_packages = true
28
+ [[tool.mypy.overrides]]
29
+ module = [ "iqm.data_definitions.*", "iqm.models.*", "daemon.*", "quimb.*", "qiskit.*", "qiskit_aer.*", "grpc.*", "lmfit.*", "emukit.*", "mockito.*", "sklearn.*", "dill.*", "uncertainties.*", "GPy.*", "pythonjsonlogger.*",]
30
+ ignore_missing_imports = true
31
+
24
32
  [tool.ruff]
25
33
  exclude = [ "*.ipynb", "*.rst",]
26
34
  extend-include = [ "sub-setup-py.template",]
@@ -3,3 +3,4 @@ python-rapidjson == 1.20
3
3
  jinja2 == 3.0.3
4
4
  numpy >= 1.26.4,<3.0
5
5
  scipy >= 1.11.4,<1.16
6
+ scipy-stubs
@@ -3,4 +3,5 @@ iqm-data-definitions >= 2.13, < 3.0
3
3
  python-rapidjson == 1.20
4
4
  jinja2 == 3.0.3
5
5
  numpy >= 1.26.4,<3.0
6
- scipy >= 1.11.4,<1.16
6
+ scipy >= 1.11.4,<1.16
7
+ scipy-stubs
@@ -407,7 +407,7 @@ class ScheduleBuilder:
407
407
 
408
408
  """
409
409
  probe_line = self.chip_topology.component_to_probe_line.get(feedback_qubit)
410
- all_virtual_channels = self.get_virtual_feedback_channels(probe_line)
410
+ all_virtual_channels = self.get_virtual_feedback_channels(probe_line) # type: ignore[arg-type]
411
411
  channel = next((c for c in all_virtual_channels if awg_name in c), None)
412
412
  if not channel:
413
413
  raise ValueError(f"AWG node {awg_name} does not support fast feedback from {probe_line}")
@@ -1151,7 +1151,7 @@ class ScheduleBuilder:
1151
1151
  components = box.neighborhood_components.get(0)
1152
1152
  if components is None:
1153
1153
  components = box.locus_components.copy()
1154
- for channel in box.atom:
1154
+ for channel in box.atom: # type: ignore[union-attr]
1155
1155
  components.add(self._channel_to_component.get(channel, channel))
1156
1156
  box.neighborhood_components[0] = components
1157
1157
 
@@ -1311,7 +1311,7 @@ class ScheduleBuilder:
1311
1311
  }
1312
1312
 
1313
1313
  pl = Playlist()
1314
- mapped_instructions: dict[str, dict[int, Any]] = {}
1314
+ mapped_instructions: dict[str, dict[int | Instruction, Any]] = {}
1315
1315
 
1316
1316
  def _append_to_schedule(sc_schedule: SC_Schedule, channel_name: str, instr: Instruction) -> None:
1317
1317
  """Append ``instr`` to ``sc_schedule`` into the channel``channel_name``."""
@@ -1320,10 +1320,12 @@ class ScheduleBuilder:
1320
1320
  # 2 dataclasses can have the same hash if their fields are identical. We must
1321
1321
  # distinguish between different Waveform classes which may have identical fields,
1322
1322
  # so we use the instruction itself as a key, so that the class is checked too.
1323
- instr_id = hash(instr)
1323
+ instr_id = instr # type: ignore[attr-defined]
1324
+ is_mapped = instr_id in mapped_instructions.setdefault(channel_name, {})
1324
1325
  except TypeError:
1325
- instr_id = instr.id
1326
- if instr_id not in mapped_instructions.setdefault(channel_name, {}):
1326
+ instr_id = instr.id # type: ignore[attr-defined]
1327
+ is_mapped = instr_id in mapped_instructions.setdefault(channel_name, {})
1328
+ if not is_mapped:
1327
1329
  mapped = _map_instruction(instr)
1328
1330
  idx = pl.channel_descriptions[channel_name].add_instruction(mapped)
1329
1331
  sc_schedule.instructions.setdefault(channel_name, []).append(idx)
@@ -278,7 +278,7 @@ class CircuitOperationList(list):
278
278
  if self.table[op_name].arity:
279
279
  self._set_specific_operation_shortcut(op_name)
280
280
 
281
- def __getitem__(self, item) -> CircuitOperationList | CircuitOperation:
281
+ def __getitem__(self, item) -> CircuitOperationList | CircuitOperation: # type: ignore[override] # type: ignore[override] # type: ignore[override]
282
282
  """For the builtin list, this method is used both for accessing a single element: ``mylist[0]`` and accessing
283
283
  a slice: ``mylist[1:3]``. The latter should generate a new CircuitOperationList, so we override the method to
284
284
  ensure that it does.
@@ -169,7 +169,7 @@ class GateImplementation(abc.ABC):
169
169
  Inheriting classes may override this method if the default :meth:`__call__` caching (based on the args & kwargs
170
170
  in the signature) is sufficient. Any additional caching may also be implemented inside this function if needed.
171
171
  """
172
- return NotImplementedError
172
+ return NotImplementedError # type: ignore[return-value]
173
173
 
174
174
  def build(
175
175
  self, op_name: str, locus: Locus, impl_name: str | None = None, strict_locus: bool = False
@@ -452,7 +452,7 @@ class CustomIQWaveforms(GateImplementation):
452
452
  if k not in cls.excluded_parameters
453
453
  }
454
454
  if cls.dependent_waves:
455
- cls.parameters = root_parameters | parameters_i
455
+ cls.parameters = root_parameters | parameters_i # type: ignore[assignment]
456
456
  else:
457
457
  parameters_q = {
458
458
  k: v
@@ -184,13 +184,13 @@ def _validate_operation(
184
184
  """
185
185
  if not overwrite and gate_name in operations:
186
186
  old = operations.get(gate_name)
187
- same = _compare_operations(new_op, old)
187
+ same = _compare_operations(new_op, old) # type: ignore[arg-type]
188
188
  if not same:
189
189
  raise ValueError(f"{gate_name} already registered with different parameters")
190
190
 
191
191
  if gate_name in _quantum_ops_library:
192
192
  default = _quantum_ops_library.get(gate_name)
193
- same = _compare_operations(new_op, default)
193
+ same = _compare_operations(new_op, default) # type: ignore[arg-type]
194
194
  if not same:
195
195
  raise ValueError(f"{gate_name} conflicts with a canonical operation in iqm-pulse")
196
196
 
@@ -198,7 +198,7 @@ def _validate_operation(
198
198
  if gate_name in operations and operations[gate_name].unitary is not None:
199
199
  unitary = operations[gate_name].unitary
200
200
  elif gate_name in _quantum_ops_library and _quantum_ops_library[gate_name].unitary is not None:
201
- unitary = default.unitary
201
+ unitary = default.unitary # type: ignore[union-attr]
202
202
  else:
203
203
  unitary = None
204
204
  new_op = replace(new_op, unitary=unitary)
@@ -239,9 +239,9 @@ def _register_gate(
239
239
  }
240
240
  if quantum_op_specs:
241
241
  new_kwargs |= quantum_op_specs
242
- new_kwargs["params"] = tuple(new_kwargs.get("params", ()))
242
+ new_kwargs["params"] = tuple(new_kwargs.get("params", ())) # type: ignore[arg-type]
243
243
 
244
- new_op = QuantumOp(**new_kwargs)
244
+ new_op = QuantumOp(**new_kwargs) # type: ignore[arg-type] # type: ignore[arg-type] # type: ignore[arg-type] # type: ignore[arg-type] # type: ignore[arg-type] # type: ignore[arg-type] # type: ignore[arg-type]
245
245
 
246
246
  return new_op
247
247
 
@@ -275,7 +275,7 @@ def _add_implementation(
275
275
  if not get_implementation_class(impl_class.__name__):
276
276
  expose_implementation(impl_class, overwrite)
277
277
 
278
- return new_op
278
+ return new_op # type: ignore[return-value]
279
279
 
280
280
 
281
281
  def _validate_implementation(
@@ -70,10 +70,10 @@ class CCPRX_Composite(CompositeGate):
70
70
  qubit = self.locus[0]
71
71
  awg_name = self.builder.get_drive_channel(qubit)
72
72
 
73
- prx_gate: PRX_SinglePulse_GateImplementation = self.build("prx", self.locus)
73
+ prx_gate: PRX_SinglePulse_GateImplementation = self.build("prx", self.locus) # type: ignore[assignment]
74
74
  # FIXME assumes PRX gates only use this one implementation as the default,
75
75
  # with just a drive channel and a single IQPulse.
76
- pulse = prx_gate(angle, phase).atom[prx_gate.channel][0]
76
+ pulse = prx_gate(angle, phase).atom[prx_gate.channel][0] # type: ignore[union-attr,index]
77
77
  wait = Wait(pulse.duration) # idling, can be replaced with a DD sequence later on
78
78
 
79
79
  # TODO: use the actual inputted label when the HW supports many labels per drive channel
@@ -81,7 +81,7 @@ class FluxPulseGate(GateImplementation):
81
81
  """Flux pulse Waveform to be played in the qubit flux AWG."""
82
82
  root_parameters: dict[str, Parameter | Setting] = {
83
83
  "duration": Parameter("", "Gate duration", "s"),
84
- "rz": {
84
+ "rz": { # type: ignore[dict-item]
85
85
  "*": Parameter("", "Z rotation angle", "rad"), # wildcard parameter
86
86
  },
87
87
  }
@@ -108,7 +108,7 @@ class FluxPulseGate(GateImplementation):
108
108
  flux_channel = builder.get_flux_channel(component_name)
109
109
  params = self.convert_calibration_data(
110
110
  calibration_data[cal_node_name],
111
- self.parameters[cal_node_name],
111
+ self.parameters[cal_node_name], # type: ignore[arg-type]
112
112
  builder.channels[flux_channel],
113
113
  duration,
114
114
  )
@@ -266,7 +266,7 @@ class CouplerFluxPulseQubitACStarkPulseGate(GateImplementation):
266
266
 
267
267
  root_parameters: dict[str, Parameter | Setting] = {
268
268
  "duration": Parameter("", "Gate duration", "s"),
269
- "rz": {
269
+ "rz": { # type: ignore[dict-item]
270
270
  "*": Parameter("", "Z rotation angle", "rad"),
271
271
  },
272
272
  }
@@ -294,7 +294,7 @@ class CouplerFluxPulseQubitACStarkPulseGate(GateImplementation):
294
294
  flux_channel = builder.get_flux_channel(component_name)
295
295
  params = self.convert_calibration_data(
296
296
  calibration_data[cal_node_name],
297
- self.parameters[cal_node_name],
297
+ self.parameters[cal_node_name], # type: ignore[arg-type]
298
298
  builder.channels[flux_channel],
299
299
  duration,
300
300
  )
@@ -312,7 +312,7 @@ class CouplerFluxPulseQubitACStarkPulseGate(GateImplementation):
312
312
  drive_channel = builder.get_drive_channel(component_name)
313
313
  params = self.convert_calibration_data(
314
314
  calibration_data[cal_node_name],
315
- self.parameters[cal_node_name],
315
+ self.parameters[cal_node_name], # type: ignore[arg-type]
316
316
  builder.channels[drive_channel],
317
317
  duration,
318
318
  )
@@ -115,95 +115,98 @@ _default_operations: QuantumOpTable = {
115
115
  QuantumOp(
116
116
  "barrier",
117
117
  0,
118
- implementations=_default_implementations["barrier"],
118
+ implementations=_default_implementations["barrier"], # type: ignore[arg-type]
119
119
  symmetric=True,
120
120
  ),
121
121
  QuantumOp(
122
122
  "delay",
123
123
  0,
124
124
  ("duration",),
125
- implementations=_default_implementations["delay"],
125
+ implementations=_default_implementations["delay"], # type: ignore[arg-type]
126
126
  symmetric=True,
127
127
  ),
128
128
  QuantumOp(
129
129
  "measure",
130
130
  0,
131
131
  ("key",),
132
- implementations=_default_implementations["measure"],
132
+ implementations=_default_implementations["measure"], # type: ignore[arg-type]
133
133
  factorizable=True,
134
134
  ),
135
135
  QuantumOp(
136
136
  "prx",
137
137
  1,
138
138
  ("angle", "phase"),
139
- implementations=_default_implementations["prx"],
139
+ implementations=_default_implementations["prx"], # type: ignore[arg-type]
140
140
  unitary=get_unitary_prx,
141
141
  ),
142
142
  QuantumOp(
143
143
  "prx_12",
144
144
  1,
145
145
  ("angle", "phase"),
146
- implementations=_default_implementations["prx_12"],
146
+ implementations=_default_implementations["prx_12"], # type: ignore[arg-type]
147
147
  ),
148
148
  QuantumOp(
149
149
  "u",
150
150
  1,
151
151
  ("theta", "phi", "lam"),
152
- implementations=_default_implementations["u"],
152
+ implementations=_default_implementations["u"], # type: ignore[arg-type]
153
153
  unitary=get_unitary_u,
154
154
  ),
155
155
  QuantumOp(
156
- "sx", 1, implementations=_default_implementations["sx"], unitary=lambda: get_unitary_prx(np.pi / 2, 0)
156
+ "sx",
157
+ 1,
158
+ implementations=_default_implementations["sx"], # type: ignore[arg-type]
159
+ unitary=lambda: get_unitary_prx(np.pi / 2, 0),
157
160
  ),
158
161
  QuantumOp(
159
162
  "rz",
160
163
  1,
161
164
  ("angle",),
162
- implementations=_default_implementations["rz"],
165
+ implementations=_default_implementations["rz"], # type: ignore[arg-type]
163
166
  unitary=get_unitary_rz,
164
167
  ),
165
168
  QuantumOp(
166
169
  "rz_physical",
167
170
  1,
168
- implementations=_default_implementations["rz_physical"],
171
+ implementations=_default_implementations["rz_physical"], # type: ignore[arg-type]
169
172
  ),
170
173
  QuantumOp(
171
174
  "cz",
172
175
  2,
173
176
  (),
174
- implementations=_default_implementations["cz"],
177
+ implementations=_default_implementations["cz"], # type: ignore[arg-type]
175
178
  symmetric=True,
176
179
  unitary=lambda: np.diag([1.0, 1.0, 1.0, -1.0]),
177
180
  ),
178
181
  QuantumOp(
179
182
  "move",
180
183
  2,
181
- implementations=_default_implementations["move"],
184
+ implementations=_default_implementations["move"], # type: ignore[arg-type]
182
185
  ),
183
186
  QuantumOp(
184
187
  "cc_prx",
185
188
  1,
186
189
  ("angle", "phase", "feedback_qubit", "feedback_key"),
187
- implementations=_default_implementations["cc_prx"],
190
+ implementations=_default_implementations["cc_prx"], # type: ignore[arg-type]
188
191
  ),
189
192
  QuantumOp(
190
193
  "reset",
191
194
  0,
192
- implementations=_default_implementations["reset"],
195
+ implementations=_default_implementations["reset"], # type: ignore[arg-type]
193
196
  symmetric=True,
194
197
  factorizable=True,
195
198
  ),
196
199
  QuantumOp(
197
200
  "reset_wait",
198
201
  0,
199
- implementations=_default_implementations["reset_wait"],
202
+ implementations=_default_implementations["reset_wait"], # type: ignore[arg-type]
200
203
  symmetric=True,
201
204
  factorizable=True,
202
205
  ),
203
206
  QuantumOp(
204
207
  "flux_multiplexer",
205
208
  0,
206
- implementations=_default_implementations["flux_multiplexer"],
209
+ implementations=_default_implementations["flux_multiplexer"], # type: ignore[arg-type]
207
210
  ),
208
211
  ]
209
212
  }
@@ -215,95 +218,98 @@ _quantum_ops_library = {
215
218
  QuantumOp(
216
219
  "barrier",
217
220
  0,
218
- implementations=_default_implementations["barrier"],
221
+ implementations=_default_implementations["barrier"], # type: ignore[arg-type]
219
222
  symmetric=True,
220
223
  ),
221
224
  QuantumOp(
222
225
  "delay",
223
226
  0,
224
227
  ("duration",),
225
- implementations=_default_implementations["delay"],
228
+ implementations=_default_implementations["delay"], # type: ignore[arg-type]
226
229
  symmetric=True,
227
230
  ),
228
231
  QuantumOp(
229
232
  "measure",
230
233
  0,
231
234
  ("key",),
232
- implementations=_default_implementations["measure"],
235
+ implementations=_default_implementations["measure"], # type: ignore[arg-type]
233
236
  factorizable=True,
234
237
  ),
235
238
  QuantumOp(
236
239
  "prx",
237
240
  1,
238
241
  ("angle", "phase"),
239
- implementations=_default_implementations["prx"],
242
+ implementations=_default_implementations["prx"], # type: ignore[arg-type]
240
243
  unitary=get_unitary_prx,
241
244
  ),
242
245
  QuantumOp(
243
246
  "prx_12",
244
247
  1,
245
248
  ("angle", "phase"),
246
- implementations=_default_implementations["prx_12"],
249
+ implementations=_default_implementations["prx_12"], # type: ignore[arg-type]
247
250
  ),
248
251
  QuantumOp(
249
252
  "u",
250
253
  1,
251
254
  ("theta", "phi", "lam"),
252
- implementations=_default_implementations["u"],
255
+ implementations=_default_implementations["u"], # type: ignore[arg-type]
253
256
  unitary=get_unitary_u,
254
257
  ),
255
258
  QuantumOp(
256
- "sx", 1, implementations=_default_implementations["sx"], unitary=lambda: get_unitary_prx(np.pi / 2, 0)
259
+ "sx",
260
+ 1,
261
+ implementations=_default_implementations["sx"], # type: ignore[arg-type]
262
+ unitary=lambda: get_unitary_prx(np.pi / 2, 0),
257
263
  ),
258
264
  QuantumOp(
259
265
  "rz",
260
266
  1,
261
267
  ("angle",),
262
- implementations=_default_implementations["rz"],
268
+ implementations=_default_implementations["rz"], # type: ignore[arg-type]
263
269
  unitary=get_unitary_rz,
264
270
  ),
265
271
  QuantumOp(
266
272
  "rz_physical",
267
273
  1,
268
- implementations=_default_implementations["rz_physical"],
274
+ implementations=_default_implementations["rz_physical"], # type: ignore[arg-type]
269
275
  ),
270
276
  QuantumOp(
271
277
  "cz",
272
278
  2,
273
279
  (),
274
- implementations=_default_implementations["cz"],
280
+ implementations=_default_implementations["cz"], # type: ignore[arg-type]
275
281
  symmetric=True,
276
282
  unitary=lambda: np.diag([1.0, 1.0, 1.0, -1.0]),
277
283
  ),
278
284
  QuantumOp(
279
285
  "move",
280
286
  2,
281
- implementations=_default_implementations["move"],
287
+ implementations=_default_implementations["move"], # type: ignore[arg-type]
282
288
  ),
283
289
  QuantumOp(
284
290
  "cc_prx",
285
291
  1,
286
292
  ("angle", "phase", "feedback_qubit", "feedback_key"),
287
- implementations=_default_implementations["cc_prx"],
293
+ implementations=_default_implementations["cc_prx"], # type: ignore[arg-type]
288
294
  ),
289
295
  QuantumOp(
290
296
  "reset",
291
297
  0,
292
- implementations=_default_implementations["reset"],
298
+ implementations=_default_implementations["reset"], # type: ignore[arg-type]
293
299
  symmetric=True,
294
300
  factorizable=True,
295
301
  ),
296
302
  QuantumOp(
297
303
  "reset_wait",
298
304
  0,
299
- implementations=_default_implementations["reset_wait"],
305
+ implementations=_default_implementations["reset_wait"], # type: ignore[arg-type]
300
306
  symmetric=True,
301
307
  factorizable=True,
302
308
  ),
303
309
  QuantumOp(
304
310
  "flux_multiplexer",
305
311
  0,
306
- implementations=_default_implementations["flux_multiplexer"],
312
+ implementations=_default_implementations["flux_multiplexer"], # type: ignore[arg-type]
307
313
  ),
308
314
  ]
309
315
  }
@@ -122,7 +122,7 @@ class FluxMultiplexer_SampleLinear(GateImplementation):
122
122
  multiple = int(inst.duration / real_pulse_dict["duration"])
123
123
  chopped_seg.extend(multiple * [Wait(real_pulse_dict["duration"])])
124
124
  else:
125
- chopped_seg.append(inst)
125
+ chopped_seg.append(inst) # type: ignore[arg-type]
126
126
  scheduled_fluxes[channel] = Segment(chopped_seg)
127
127
  max_flux_depth = next(len(seg) for ch, seg in scheduled_fluxes.items() if ch in crosstalk_channels)
128
128
  # pad the relevant flux channels not present in the schedule at all with correct number of wait tiles
@@ -157,7 +157,7 @@ class FluxMultiplexer_SampleLinear(GateImplementation):
157
157
  if max(abs(multiplexed_samples)) < TOLERANCE:
158
158
  multiplexed_pulse = Wait(real_pulse_dict["duration"])
159
159
  else:
160
- multiplexed_pulse = RealPulse(
160
+ multiplexed_pulse = RealPulse( # type: ignore[assignment]
161
161
  scale=1.0, duration=real_pulse_dict["duration"], wave=Samples(multiplexed_samples)
162
162
  )
163
163
  corrected_fluxes[flux_channel]._instructions[layer] = multiplexed_pulse
@@ -106,7 +106,7 @@ class Measure_CustomWaveforms(CustomIQWaveforms):
106
106
  """Cache for :meth:`time_trace`."""
107
107
 
108
108
  if len(locus) == 1: # factorizable gates only need calibration on 1-loci
109
- self._probe_line: ProbeChannelProperties = self.builder.channels[
109
+ self._probe_line: ProbeChannelProperties = self.builder.channels[ # type: ignore[assignment]
110
110
  self.builder.get_probe_channel(self.locus[0])
111
111
  ]
112
112
  c_freq = self._probe_line.center_frequency
@@ -194,7 +194,7 @@ class Measure_CustomWaveforms(CustomIQWaveforms):
194
194
  modulation_frequency=if_freq, # TODO: should be fixed to -if_freq in Programmable RO Phase2?
195
195
  )
196
196
 
197
- acquisition_type = root_params.get("acquisition_type", self.root_parameters["acquisition_type"].value)
197
+ acquisition_type = root_params.get("acquisition_type", self.root_parameters["acquisition_type"].value) # type: ignore[union-attr]
198
198
  acquisition_delay = round(self._probe_line.duration_to_samples(root_params["acquisition_delay"]))
199
199
  acquisition_label = "TO_BE_REPLACED"
200
200
  if acquisition_type == "complex":
@@ -245,7 +245,7 @@ class Measure_CustomWaveforms(CustomIQWaveforms):
245
245
  # TODO: use the actual ``feedback_key`` when AWGs support multiple feedback labels
246
246
  feedback_bit = f"{self.locus[0]}__{FEEDBACK_KEY}"
247
247
  replacements["feedback_signal_label"] = feedback_bit
248
- acquisitions = (replace(self._acquisition_method, **replacements),) if do_acquisition else ()
248
+ acquisitions = (replace(self._acquisition_method, **replacements),) if do_acquisition else () # type: ignore[arg-type]
249
249
  multiplexed_iq = MultiplexedIQPulse(
250
250
  duration=self._probe_instruction.duration + self._probe_line.integration_start_dead_time,
251
251
  entries=((self._probe_instruction, self._probe_line.integration_start_dead_time),),
@@ -289,7 +289,7 @@ class Measure_CustomWaveforms(CustomIQWaveforms):
289
289
  # _skip_override used for child classes build on `Measure_CustomWaveforms` to not call `.probe_timebox`
290
290
  # from the parent class, but from this class instead.
291
291
  probe_timeboxes = [
292
- self.builder.get_implementation(
292
+ self.builder.get_implementation( # type: ignore[attr-defined]
293
293
  self.parent.name, (c,), impl_name=self.name, priority_calibration=self._prio_calibration
294
294
  ).probe_timebox(key, feedback_key, do_acquisition, _skip_override=True)
295
295
  for c in self.locus
@@ -380,7 +380,7 @@ class Measure_CustomWaveforms(CustomIQWaveforms):
380
380
  # additional caching for time traces since the acquisitions differ from the ones in _call
381
381
  if args not in self._time_traces:
382
382
  probe_timebox = deepcopy(self.probe_timebox(key=key, feedback_key=feedback_key, _skip_override=True))
383
- for probe_channel, segment in probe_timebox.atom.items():
383
+ for probe_channel, segment in probe_timebox.atom.items(): # type: ignore[union-attr]
384
384
  readout_trigger = segment[0]
385
385
  # TODO instead of editing the probe_timebox output contents, we should make the function itself do this
386
386
  # so we would not need to blindly search through the channels
@@ -398,7 +398,8 @@ class Measure_CustomWaveforms(CustomIQWaveforms):
398
398
  duration_samples = probe_line.duration_to_int_samples(acquisition_duration)
399
399
  else:
400
400
  duration_samples = max(
401
- acq.weights.duration + acq.delay_samples - delay_samples for acq in readout_trigger.acquisitions
401
+ acq.weights.duration + acq.delay_samples - delay_samples # type: ignore[attr-defined]
402
+ for acq in readout_trigger.acquisitions
402
403
  )
403
404
  label_key = key or DEFAULT_TIME_TRACE_KEY
404
405
  time_trace = TimeTrace(
@@ -417,7 +418,7 @@ class Measure_CustomWaveforms(CustomIQWaveforms):
417
418
  def duration_in_seconds(self) -> float:
418
419
  probe_timebox = self.probe_timebox()
419
420
  readout_schedule = probe_timebox.atom
420
- return readout_schedule.duration_in_seconds(self.builder.channels)
421
+ return readout_schedule.duration_in_seconds(self.builder.channels) # type: ignore[union-attr]
421
422
 
422
423
  @classmethod
423
424
  def get_locus_mapping_name(cls, operation_name: str, implementation_name: str) -> str:
@@ -464,7 +465,7 @@ class ProbePulse_CustomWaveforms(CustomIQWaveforms):
464
465
  self, parent: QuantumOp, name: str, locus: Locus, calibration_data: OILCalibrationData, builder: ScheduleBuilder
465
466
  ):
466
467
  super().__init__(parent, name, locus, calibration_data, builder)
467
- self._probe_line: ProbeChannelProperties = self.builder.channels[
468
+ self._probe_line: ProbeChannelProperties = self.builder.channels[ # type: ignore[assignment]
468
469
  self.builder.component_channels[self.locus[0]]["readout"]
469
470
  ]
470
471
  self._duration = (
@@ -568,9 +569,9 @@ class ProbePulse_CustomWaveforms(CustomIQWaveforms):
568
569
  return final_box
569
570
 
570
571
  def duration_in_seconds(self) -> float:
571
- probe_timebox = self().children[0]
572
+ probe_timebox = self().children[0] # type: ignore[union-attr]
572
573
  probe_channel = self.builder.component_channels[self.locus[0]]["readout"]
573
- return self.builder.channels[probe_channel].duration_to_seconds(probe_timebox.atom.duration)
574
+ return self.builder.channels[probe_channel].duration_to_seconds(probe_timebox.atom.duration) # type: ignore[union-attr]
574
575
 
575
576
  @classmethod
576
577
  def get_locus_mapping_name(cls, operation_name: str, implementation_name: str) -> str:
@@ -601,7 +602,7 @@ class ProbePulse_CustomWaveforms_noIntegration(CustomIQWaveforms):
601
602
  """Cache for :meth:`probe_timebox`."""
602
603
 
603
604
  if len(locus) == 1: # factorizable gates only need calibration on 1-loci
604
- self._probe_line: ProbeChannelProperties = self.builder.channels[
605
+ self._probe_line: ProbeChannelProperties = self.builder.channels[ # type: ignore[assignment]
605
606
  self.builder.get_probe_channel(self.locus[0])
606
607
  ]
607
608
  c_freq = self._probe_line.center_frequency
@@ -723,7 +724,7 @@ class ProbePulse_CustomWaveforms_noIntegration(CustomIQWaveforms):
723
724
  )
724
725
  else:
725
726
  probe_timeboxes = [
726
- self.builder.get_implementation(
727
+ self.builder.get_implementation( # type: ignore[attr-defined]
727
728
  self.parent.name, (c,), impl_name=self.name, priority_calibration=self._prio_calibration
728
729
  ).probe_timebox(key, feedback_key, do_acquisition)
729
730
  for c in self.locus
@@ -775,7 +776,7 @@ class ProbePulse_CustomWaveforms_noIntegration(CustomIQWaveforms):
775
776
  def duration_in_seconds(self) -> float:
776
777
  probe_timebox = self.probe_timebox()
777
778
  readout_schedule = probe_timebox.atom
778
- return readout_schedule.duration_in_seconds(self.builder.channels)
779
+ return readout_schedule.duration_in_seconds(self.builder.channels) # type: ignore[union-attr]
779
780
 
780
781
  @classmethod
781
782
  def get_locus_mapping_name(cls, operation_name: str, implementation_name: str) -> str:
@@ -832,12 +833,13 @@ class Shelved_Measure_CustomWaveforms(Measure_CustomWaveforms):
832
833
  multiplexed_timeboxes = super().probe_timebox(key, feedback_key)
833
834
  prx_12_impl = [self.builder.get_implementation("prx_12", [q])(np.pi) for q in self.locus]
834
835
 
835
- boxes = prx_12_impl + multiplexed_timeboxes + prx_12_impl
836
+ boxes = prx_12_impl + multiplexed_timeboxes + prx_12_impl # type: ignore[operator]
836
837
  return boxes
837
838
 
838
839
  def _call(self, key: str = "", feedback_key: str = "") -> TimeBox: # type: ignore[override]
839
840
  shelved_measure_box = TimeBox.composite(
840
- self.probe_timebox(key=key, feedback_key=feedback_key), label=f"Readout on {self.locus}"
841
+ self.probe_timebox(key=key, feedback_key=feedback_key), # type: ignore[arg-type]
842
+ label=f"Readout on {self.locus}",
841
843
  )
842
844
  shelved_measure_box.neighborhood_components[0] = shelved_measure_box.children[
843
845
  len(self.locus)
@@ -104,7 +104,7 @@ class MOVE_CustomWaveforms(FluxPulseGate):
104
104
 
105
105
  root_parameters: dict[str, Parameter | Setting] = {
106
106
  "duration": Parameter("", "Gate duration", "s"),
107
- "rz": {
107
+ "rz": { # type: ignore[dict-item]
108
108
  "*": Parameter("", "Z rotation angle", "rad"), # wildcard parameter
109
109
  },
110
110
  "detuning": Parameter("", "Qubit - resonator detuning", "Hz"),
@@ -120,8 +120,8 @@ class PrxGateImplementation(GateImplementation):
120
120
 
121
121
  """
122
122
  box = self(angle=angle, phase=0)
123
- box.label = f"Rx on {self.locus[0]}"
124
- return box
123
+ box.label = f"Rx on {self.locus[0]}" # type: ignore[union-attr]
124
+ return box # type: ignore[return-value]
125
125
 
126
126
  def ry(self, angle: float) -> TimeBox:
127
127
  """Y rotation gate.
@@ -134,8 +134,8 @@ class PrxGateImplementation(GateImplementation):
134
134
 
135
135
  """
136
136
  box = self(angle=angle, phase=np.pi / 2)
137
- box.label = f"Ry on {self.locus[0]}"
138
- return box
137
+ box.label = f"Ry on {self.locus[0]}" # type: ignore[union-attr]
138
+ return box # type: ignore[return-value]
139
139
 
140
140
  def clifford(self, xy_gate: XYGate) -> TimeBox:
141
141
  """One-qubit XY Clifford gates.
@@ -197,12 +197,12 @@ class PRX_SinglePulse_GateImplementation(SinglePulseGate, PrxGateImplementation)
197
197
  duration being zero, the gate implementation will apply a ``Block(0)`` instruction to the qubit's drive channel.
198
198
  """
199
199
 
200
- def _call(self, angle: float, phase: float = 0.0) -> TimeBox:
200
+ def _call(self, angle: float, phase: float = 0.0) -> TimeBox: # type: ignore[override]
201
201
  scale, new_phase = _normalize_params(angle, phase)
202
202
  pulse = self.pulse.copy(
203
- scale_i=scale * self.pulse.scale_i,
204
- scale_q=scale * self.pulse.scale_q,
205
- phase=self.pulse.phase + new_phase,
203
+ scale_i=scale * self.pulse.scale_i, # type: ignore[attr-defined]
204
+ scale_q=scale * self.pulse.scale_q, # type: ignore[attr-defined]
205
+ phase=self.pulse.phase + new_phase, # type: ignore[attr-defined]
206
206
  )
207
207
  if self.pulse.duration > TOLERANCE:
208
208
  timebox = self.to_timebox(Schedule({self.channel: [pulse]}))
@@ -214,7 +214,7 @@ class PRX_SinglePulse_GateImplementation(SinglePulseGate, PrxGateImplementation)
214
214
  @property
215
215
  def iq_pulse(self) -> IQPulse:
216
216
  """Alias for ``self.pulse`` for backward compatibility"""
217
- return self.pulse
217
+ return self.pulse # type: ignore[return-value]
218
218
 
219
219
 
220
220
  class PRX_CustomWaveforms(PRX_SinglePulse_GateImplementation, CustomIQWaveforms):
@@ -317,12 +317,12 @@ class PRX_CustomWaveformsSX(PRX_SinglePulse_GateImplementation, CustomIQWaveform
317
317
  timebox = self.to_timebox(Schedule({self.channel: [pulse]}))
318
318
  elif np.isclose(new_angle, np.pi / 2, atol=1e-8):
319
319
  pulse = self.pulse.copy(
320
- phase=self.pulse.phase + new_phase,
320
+ phase=self.pulse.phase + new_phase, # type: ignore[attr-defined]
321
321
  )
322
322
  timebox = self.to_timebox(Schedule({self.channel: [pulse]}))
323
323
  elif np.isclose(new_angle, np.pi, atol=1e-8):
324
324
  pulse = self.pulse.copy(
325
- phase=self.pulse.phase + new_phase,
325
+ phase=self.pulse.phase + new_phase, # type: ignore[attr-defined]
326
326
  )
327
327
  timebox = self.to_timebox(Schedule({self.channel: [pulse, pulse]}))
328
328
  else:
@@ -332,12 +332,12 @@ class PRX_CustomWaveformsSX(PRX_SinglePulse_GateImplementation, CustomIQWaveform
332
332
  phase_1, phase_increment_1 = phase_transformation(rz_a, 0)
333
333
  phase_2, phase_increment_2 = phase_transformation(rz_b, rz_c)
334
334
  pulse_1 = self.pulse.copy(
335
- phase=normalize_angle(self.pulse.phase + phase_1),
336
- phase_increment=normalize_angle(self.pulse.phase_increment + phase_increment_1),
335
+ phase=normalize_angle(self.pulse.phase + phase_1), # type: ignore[attr-defined]
336
+ phase_increment=normalize_angle(self.pulse.phase_increment + phase_increment_1), # type: ignore[attr-defined]
337
337
  )
338
338
  pulse_2 = self.pulse.copy(
339
- phase=normalize_angle(self.pulse.phase + phase_2),
340
- phase_increment=normalize_angle(self.pulse.phase_increment + phase_increment_2),
339
+ phase=normalize_angle(self.pulse.phase + phase_2), # type: ignore[attr-defined]
340
+ phase_increment=normalize_angle(self.pulse.phase_increment + phase_increment_2), # type: ignore[attr-defined]
341
341
  )
342
342
  timebox = self.to_timebox(Schedule({self.channel: [pulse_1, pulse_2]}))
343
343
 
@@ -482,14 +482,14 @@ class ABC_Constant_smooth(PrxGateImplementation):
482
482
  params_for_stark["n_samples"] = max(
483
483
  int(round(params_for_stark["n_samples"] * (1 - 2 * params_for_stark["rise_time"]), 0)), 0
484
484
  )
485
- self.main_waveform = self._main_pulse(**params_for_stark)
485
+ self.main_waveform = self._main_pulse(**params_for_stark) # type: ignore[assignment]
486
486
 
487
487
  params_for_risefall["n_samples"] = int(
488
488
  round(params_for_risefall["n_samples"] * params_for_risefall["rise_time"], 0)
489
489
  )
490
490
 
491
- self.fall_waveform = self._fall_pulse(**params_for_risefall)
492
- self.rise_waveform = self._rise_pulse(**params_for_risefall)
491
+ self.fall_waveform = self._fall_pulse(**params_for_risefall) # type: ignore[assignment]
492
+ self.rise_waveform = self._rise_pulse(**params_for_risefall) # type: ignore[assignment]
493
493
 
494
494
  self.channel = drive_channel
495
495
  self.special_implementation = True # DEBUG
@@ -513,7 +513,7 @@ class ABC_Constant_smooth(PrxGateImplementation):
513
513
 
514
514
  parameters["rise_time"] = Parameter("", "gate rise time", "s")
515
515
 
516
- cls.parameters = {
516
+ cls.parameters = { # type: ignore[assignment]
517
517
  "duration": Parameter("", "Gate duration", "s"),
518
518
  } | parameters
519
519
 
@@ -64,11 +64,11 @@ class Reset_Conditional(CompositeGate):
64
64
  )
65
65
  # try to get a qnd measurement, otherwise use default one
66
66
  try: # TODO: should the QND measurement be its own QuantumOp instead?
67
- probe_timebox = self.build("measure", resettable, impl_name="constant_qnd").probe_timebox(
67
+ probe_timebox = self.build("measure", resettable, impl_name="constant_qnd").probe_timebox( # type: ignore[attr-defined]
68
68
  RESET_MEASUREMENT_KEY, feedback_key=RESET_FEEDBACK_KEY
69
69
  )
70
70
  except (ValueError, KeyError):
71
- probe_timebox = self.build("measure", resettable).probe_timebox(
71
+ probe_timebox = self.build("measure", resettable).probe_timebox( # type: ignore[attr-defined]
72
72
  RESET_MEASUREMENT_KEY, feedback_key=RESET_FEEDBACK_KEY
73
73
  )
74
74
  virtual_channels = set()
@@ -89,7 +89,7 @@ class Reset_Conditional(CompositeGate):
89
89
  probe_timebox.neighborhood_components[0] = blocks
90
90
  measure = TimeBox.composite([probe_timebox])
91
91
  measure.neighborhood_components[0] = blocks
92
- return TimeBox.composite([measure, resets])
92
+ return TimeBox.composite([measure, resets]) # type: ignore[list-item]
93
93
 
94
94
  @classmethod
95
95
  def get_locus_mapping_name(cls, operation_name: str, implementation_name: str) -> str:
@@ -123,7 +123,7 @@ class Reset_Wait(GateImplementation):
123
123
  prio_calibration = self.calibration_data if self.calibration_data else None
124
124
  waits_box = TimeBox.composite(
125
125
  [
126
- self.builder.get_implementation(
126
+ self.builder.get_implementation( # type: ignore[attr-defined]
127
127
  self.parent.name, (q,), impl_name=self.name, priority_calibration=prio_calibration
128
128
  ).wait_box()
129
129
  for q in self.locus
@@ -144,7 +144,7 @@ class RZ_Virtual(GateImplementation):
144
144
  timebox.neighborhood_components[0] = set(self.locus)
145
145
  return timebox
146
146
 
147
- parameters: dict[str, Parameter] = {}
147
+ parameters: dict[str, Parameter] = {} # type: ignore[assignment]
148
148
 
149
149
  def duration_in_seconds(self) -> float:
150
150
  return self.duration
@@ -191,7 +191,7 @@ class RZ_ACStarkShift(GateImplementation):
191
191
  parameters["amplitude"] = Parameter("", "amplitude", "")
192
192
  parameters["phase_increment"] = Parameter("", "phase increment", "rad")
193
193
 
194
- cls.parameters = {
194
+ cls.parameters = { # type: ignore[assignment]
195
195
  "duration": Parameter("", "Gate duration", "s"),
196
196
  } | parameters
197
197
 
@@ -234,7 +234,7 @@ class RZ_ACStarkShift_CosineRiseFall(RZ_ACStarkShift, ac_stark_waveform=Modulate
234
234
  """AC stark pulse implemented as a modulated cosine rise fall pulse."""
235
235
 
236
236
 
237
- class RZ_ACStarkShift_smoothConstant(
237
+ class RZ_ACStarkShift_smoothConstant( # type: ignore[call-arg] # type: ignore[call-arg] # type: ignore[call-arg]
238
238
  Constant_PRX_with_smooth_rise_fall,
239
239
  rise_waveform=CosineRise, # type:ignore[call-arg]
240
240
  main_waveform=Constant, # type:ignore[call-arg]
@@ -260,8 +260,8 @@ class RZ_PRX_Composite(CompositeGate):
260
260
  prx = self.build("prx", self.locus)
261
261
  return TimeBox.composite(
262
262
  [
263
- prx.ry(np.pi / 2),
264
- prx.rx(angle),
265
- prx.ry(-np.pi / 2),
263
+ prx.ry(np.pi / 2), # type: ignore[attr-defined]
264
+ prx.rx(angle), # type: ignore[attr-defined]
265
+ prx.ry(-np.pi / 2), # type: ignore[attr-defined]
266
266
  ]
267
267
  )
@@ -42,6 +42,6 @@ class SXGate(CompositeGate):
42
42
 
43
43
  def _call(self) -> TimeBox: # type: ignore[override]
44
44
  """Call PRX gate with angle equals to pi / 2."""
45
- prx_gate: PRX_SinglePulse_GateImplementation | PRX_CustomWaveformsSX = self.build("prx", self.locus)
46
- pulse = prx_gate(np.pi / 2, 0.0).atom[prx_gate.channel][0]
45
+ prx_gate: PRX_SinglePulse_GateImplementation | PRX_CustomWaveformsSX = self.build("prx", self.locus) # type: ignore[assignment]
46
+ pulse = prx_gate(np.pi / 2, 0.0).atom[prx_gate.channel][0] # type: ignore[union-attr,index]
47
47
  return self.to_timebox(Schedule({prx_gate.channel: [pulse]}))
@@ -103,7 +103,9 @@ class UGate(CompositeGate):
103
103
  # the PRX implementation. This isn't safe in general, can we find a better solution?
104
104
 
105
105
  prx_gate = self.build("prx", self.locus)
106
- pulse_train = prx_gate(theta, np.pi / 2).atom[prx_gate.channel] # RY pulse
106
+ pulse_train = prx_gate(theta, np.pi / 2).atom[ # type: ignore[union-attr]
107
+ prx_gate.channel # type: ignore[index,attr-defined]
108
+ ] # RY pulse
107
109
 
108
110
  # Check if the pulse train have one or several pulses.
109
111
  if len(pulse_train) == 1:
@@ -116,7 +118,7 @@ class UGate(CompositeGate):
116
118
  phase=normalize_angle(pulse.phase + new_phase),
117
119
  phase_increment=normalize_angle(pulse.phase_increment + new_phase_increment),
118
120
  )
119
- timebox = self.to_timebox(Schedule({prx_gate.channel: [new_pulse]}))
121
+ timebox = self.to_timebox(Schedule({prx_gate.channel: [new_pulse]})) # type: ignore[attr-defined]
120
122
 
121
123
  else:
122
124
  # Assumes the PRX pulse train begins and ends with IQPulses.
@@ -139,7 +141,7 @@ class UGate(CompositeGate):
139
141
  )
140
142
  other_pulses = [pulse.copy() for pulse in pulse_train[1:-1]]
141
143
  new_pulses = [new_pulse_a] + other_pulses + [new_pulse_b]
142
- timebox = self.to_timebox(Schedule({prx_gate.channel: new_pulses}))
144
+ timebox = self.to_timebox(Schedule({prx_gate.channel: new_pulses})) # type: ignore[attr-defined]
143
145
 
144
146
  timebox.neighborhood_components[0] = set(self.locus)
145
147
  return timebox
@@ -229,7 +229,7 @@ def validate_op_calibration(calibration: OpCalibrationDataTree, ops: QuantumOpTa
229
229
 
230
230
  default_cal_data = loci.get((), {})
231
231
  for locus, cal_data in loci.items():
232
- validate_locus_calibration(merge_dicts(default_cal_data, cal_data), impl, op, impl_name, locus)
232
+ validate_locus_calibration(merge_dicts(default_cal_data, cal_data), impl, op, impl_name, locus) # type: ignore[arg-type]
233
233
 
234
234
 
235
235
  def validate_locus_calibration(
@@ -266,8 +266,8 @@ def extend_schedule_new( # noqa: PLR0915
266
266
  _ = channels[ch].duration_to_int_samples(duration, message=f"{ch}: {iA} + {iB}: overlap duration")
267
267
 
268
268
  if isinstance(iA, Nothing) and isinstance(iB, Nothing):
269
- return Nothing(duration=duration)
270
- return Block(duration=duration)
269
+ return Nothing(duration=duration) # type: ignore[arg-type]
270
+ return Block(duration=duration) # type: ignore[arg-type]
271
271
 
272
272
  def find_start_time() -> float:
273
273
  """Find the earliest possible start time for schedule B."""
@@ -297,7 +297,7 @@ def extend_schedule_new( # noqa: PLR0915
297
297
  if distance >= -TOL:
298
298
  if distance > TOL:
299
299
  # gap, add a Nothing between A and B
300
- segment_A._instructions.append(Nothing(duration=distance))
300
+ segment_A._instructions.append(Nothing(duration=distance)) # type: ignore[arg-type]
301
301
  # add B
302
302
  segment_A._instructions.extend(pointer_B.tail())
303
303
  else:
@@ -378,7 +378,7 @@ class MultiplexedProbeTimeBox(TimeBox):
378
378
  """
379
379
  schedule = {probe_channel: [readout_trigger]}
380
380
  for channel in block_channels:
381
- schedule[channel] = [Block(block_duration)]
381
+ schedule[channel] = [Block(block_duration)] # type: ignore[list-item]
382
382
  box = MultiplexedProbeTimeBox(
383
383
  label=label,
384
384
  locus_components=set(locus_components),
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: iqm-pulse
3
- Version: 9.19.0
3
+ Version: 9.21.0
4
4
  Summary: A Python-based project for providing interface and implementations for control pulses.
5
5
  Author-email: IQM Finland Oy <info@meetiqm.com>
6
6
  License: Apache License
@@ -219,6 +219,7 @@ Requires-Dist: python-rapidjson==1.20
219
219
  Requires-Dist: jinja2==3.0.3
220
220
  Requires-Dist: numpy<3.0,>=1.26.4
221
221
  Requires-Dist: scipy<1.16,>=1.11.4
222
+ Requires-Dist: scipy-stubs
222
223
 
223
224
  IQM Pulse library
224
225
  =================
@@ -34,6 +34,7 @@ src/iqm/pulse/base_utils.py
34
34
  src/iqm/pulse/builder.py
35
35
  src/iqm/pulse/circuit_operations.py
36
36
  src/iqm/pulse/gate_implementation.py
37
+ src/iqm/pulse/py.typed
37
38
  src/iqm/pulse/quantum_ops.py
38
39
  src/iqm/pulse/scheduler.py
39
40
  src/iqm/pulse/timebox.py
@@ -4,3 +4,4 @@ python-rapidjson==1.20
4
4
  jinja2==3.0.3
5
5
  numpy<3.0,>=1.26.4
6
6
  scipy<1.16,>=1.11.4
7
+ scipy-stubs
File without changes
@@ -0,0 +1 @@
1
+ 9.21.0
@@ -1 +0,0 @@
1
- 9.19.0
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes