iqm-client 32.1.1__py3-none-any.whl → 33.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.
Files changed (43) hide show
  1. iqm/cirq_iqm/devices/iqm_device_metadata.py +2 -1
  2. iqm/cirq_iqm/examples/demo_common.py +1 -1
  3. iqm/cirq_iqm/examples/demo_iqm_execution.py +3 -3
  4. iqm/cirq_iqm/iqm_sampler.py +47 -29
  5. iqm/cirq_iqm/serialize.py +1 -1
  6. iqm/cirq_iqm/transpiler.py +3 -1
  7. iqm/iqm_client/__init__.py +0 -2
  8. iqm/iqm_client/errors.py +6 -17
  9. iqm/iqm_client/iqm_client.py +199 -602
  10. iqm/iqm_client/models.py +20 -611
  11. iqm/iqm_client/transpile.py +11 -8
  12. iqm/iqm_client/validation.py +18 -9
  13. iqm/iqm_server_client/__init__.py +14 -0
  14. iqm/iqm_server_client/errors.py +6 -0
  15. iqm/iqm_server_client/iqm_server_client.py +755 -0
  16. iqm/iqm_server_client/models.py +179 -0
  17. iqm/iqm_server_client/py.typed +0 -0
  18. iqm/qiskit_iqm/__init__.py +8 -0
  19. iqm/qiskit_iqm/examples/bell_measure.py +5 -5
  20. iqm/qiskit_iqm/examples/transpile_example.py +13 -6
  21. iqm/qiskit_iqm/fake_backends/fake_adonis.py +2 -1
  22. iqm/qiskit_iqm/fake_backends/fake_aphrodite.py +2 -1
  23. iqm/qiskit_iqm/fake_backends/fake_apollo.py +2 -1
  24. iqm/qiskit_iqm/fake_backends/fake_deneb.py +2 -1
  25. iqm/qiskit_iqm/fake_backends/iqm_fake_backend.py +8 -7
  26. iqm/qiskit_iqm/iqm_backend.py +3 -4
  27. iqm/qiskit_iqm/iqm_circuit_validation.py +8 -7
  28. iqm/qiskit_iqm/iqm_job.py +106 -88
  29. iqm/qiskit_iqm/iqm_move_layout.py +2 -1
  30. iqm/qiskit_iqm/iqm_naive_move_pass.py +114 -55
  31. iqm/qiskit_iqm/iqm_provider.py +49 -36
  32. iqm/qiskit_iqm/iqm_target.py +4 -6
  33. iqm/qiskit_iqm/qiskit_to_iqm.py +62 -25
  34. {iqm_client-32.1.1.dist-info → iqm_client-33.0.1.dist-info}/METADATA +17 -24
  35. iqm_client-33.0.1.dist-info/RECORD +63 -0
  36. iqm/iqm_client/api.py +0 -90
  37. iqm/iqm_client/authentication.py +0 -206
  38. iqm_client-32.1.1.dist-info/RECORD +0 -60
  39. {iqm_client-32.1.1.dist-info → iqm_client-33.0.1.dist-info}/AUTHORS.rst +0 -0
  40. {iqm_client-32.1.1.dist-info → iqm_client-33.0.1.dist-info}/LICENSE.txt +0 -0
  41. {iqm_client-32.1.1.dist-info → iqm_client-33.0.1.dist-info}/WHEEL +0 -0
  42. {iqm_client-32.1.1.dist-info → iqm_client-33.0.1.dist-info}/entry_points.txt +0 -0
  43. {iqm_client-32.1.1.dist-info → iqm_client-33.0.1.dist-info}/top_level.txt +0 -0
@@ -21,13 +21,7 @@ from typing import Any
21
21
  from uuid import UUID
22
22
  import warnings
23
23
 
24
- from iqm.iqm_client import (
25
- CircuitBatch,
26
- CircuitCompilationOptions,
27
- CircuitValidationError,
28
- IQMClient,
29
- RunRequest,
30
- )
24
+ from iqm.iqm_client import CircuitCompilationOptions, CircuitValidationError, IQMClient
31
25
  from iqm.iqm_client.util import to_json_dict
32
26
  from iqm.qiskit_iqm import IQMFakeAphrodite, IQMFakeApollo, IQMFakeBackend, IQMFakeDeneb
33
27
  from iqm.qiskit_iqm.fake_backends import IQMFakeAdonis
@@ -39,6 +33,7 @@ from qiskit import QuantumCircuit
39
33
  from qiskit.providers import JobStatus, JobV1, Options
40
34
 
41
35
  from iqm.pulse import Circuit
36
+ from iqm.station_control.interface.models import CircuitBatch, RunRequest
42
37
 
43
38
  try:
44
39
  __version__ = version("qiskit-iqm")
@@ -52,9 +47,9 @@ class IQMBackend(IQMBackendBase):
52
47
  """Backend for executing quantum circuits on IQM quantum computers.
53
48
 
54
49
  Args:
55
- client: Client instance for communicating with an IQM server.
50
+ client: Client instance for communicating with an IQM Server.
56
51
  calibration_set_id: ID of the calibration set the backend will use.
57
- ``None`` means the IQM server will be queried for the current default
52
+ ``None`` means the IQM Server will be queried for the current default
58
53
  calibration set.
59
54
  use_metrics: If True, the backend will query the server for calibration data and related
60
55
  quality metrics, and pass these to the transpilation target(s). The default value is set
@@ -80,7 +75,6 @@ class IQMBackend(IQMBackendBase):
80
75
  super().__init__(architecture, metrics=metrics, **kwargs)
81
76
  self.client: IQMClient = client
82
77
  self._max_circuits: int | None = None
83
- self.name = "IQM Backend"
84
78
  self._calibration_set_id = architecture.calibration_set_id
85
79
 
86
80
  @classmethod
@@ -110,12 +104,16 @@ class IQMBackend(IQMBackendBase):
110
104
  def run(
111
105
  self,
112
106
  run_input: QuantumCircuit | list[QuantumCircuit],
107
+ *,
108
+ use_timeslot: bool = False,
113
109
  **options,
114
110
  ) -> IQMJob:
115
111
  """Run a quantum circuit or a list of quantum circuits on the IQM quantum computer represented by this backend.
116
112
 
117
113
  Args:
118
114
  run_input: The circuits to run.
115
+ use_timeslot: Submits the job to the timeslot queue if set to ``True``. If set to ``False``,
116
+ the job is submitted to the normal on-demand queue.
119
117
  options: Keyword arguments passed on to :meth:`create_run_request`, and documented there.
120
118
 
121
119
  Returns:
@@ -123,8 +121,8 @@ class IQMBackend(IQMBackendBase):
123
121
 
124
122
  """
125
123
  run_request = self.create_run_request(run_input, **options)
126
- job_id = self.client.submit_run_request(run_request)
127
- job = IQMJob(self, str(job_id), shots=run_request.shots)
124
+ circuit_job = self.client.submit_run_request(run_request, use_timeslot=use_timeslot)
125
+ job = IQMJob(self, circuit_job)
128
126
  job.circuit_metadata = [c.metadata if isinstance(c, Circuit) else {} for c in run_request.circuits]
129
127
  return job
130
128
 
@@ -134,7 +132,7 @@ class IQMBackend(IQMBackendBase):
134
132
  shots: int = 1024,
135
133
  circuit_compilation_options: CircuitCompilationOptions | None = None,
136
134
  circuit_callback: Callable[[list[QuantumCircuit]], Any] | None = None,
137
- qubit_mapping: dict[int, str] | None = None,
135
+ qubit_index_to_name: dict[int, str] | None = None,
138
136
  **unknown_options,
139
137
  ) -> RunRequest:
140
138
  """Creates a run request without submitting it for execution.
@@ -161,8 +159,8 @@ class IQMBackend(IQMBackendBase):
161
159
  As a side effect, you can also use this callback to modify the transpiled circuits
162
160
  in-place, just before execution; however, we do not recommend to use it for this
163
161
  purpose.
164
- qubit_mapping: Mapping from qubit indices in the circuit to qubit names on the device. If ``None``,
165
- :attr:`.IQMBackendBase.index_to_qubit_name` will be used.
162
+ qubit_index_to_name: Mapping from qubit indices in the circuit to qubit names on the device.
163
+ If ``None``, :attr:`.IQMBackendBase.index_to_qubit_name` will be used.
166
164
 
167
165
  Returns:
168
166
  The created run request object
@@ -195,7 +193,9 @@ class IQMBackend(IQMBackendBase):
195
193
  if circuit_callback:
196
194
  circuit_callback(circuits)
197
195
 
198
- circuits_serialized: CircuitBatch = [self.serialize_circuit(circuit, qubit_mapping) for circuit in circuits]
196
+ circuits_serialized: CircuitBatch = [
197
+ self.serialize_circuit(circuit, qubit_index_to_name) for circuit in circuits
198
+ ]
199
199
 
200
200
  if self._use_default_calibration_set:
201
201
  default_calset_id = self.client.get_dynamic_quantum_architecture(None).calibration_set_id
@@ -230,13 +230,10 @@ class IQMBackend(IQMBackendBase):
230
230
  corresponding job
231
231
 
232
232
  """
233
- return IQMJob(self, job_id)
234
-
235
- def close_client(self) -> None:
236
- """Close IQMClient's session with the authentication server."""
237
- self.client.close_auth_session()
233
+ circuit_job = self.client.get_job(UUID(job_id))
234
+ return IQMJob(self, circuit_job)
238
235
 
239
- def serialize_circuit(self, circuit: QuantumCircuit, qubit_mapping: dict[int, str] | None = None) -> Circuit:
236
+ def serialize_circuit(self, circuit: QuantumCircuit, qubit_index_to_name: dict[int, str] | None = None) -> Circuit:
240
237
  """Serialize a quantum circuit into the IQM data transfer format.
241
238
 
242
239
  Serializing is not strictly bound to the native gateset, i.e. some gates that are not explicitly mentioned in
@@ -254,8 +251,8 @@ class IQMBackend(IQMBackendBase):
254
251
 
255
252
  Args:
256
253
  circuit: quantum circuit to serialize
257
- qubit_mapping: Mapping from qubit indices in the circuit to qubit names on the device. If not provided,
258
- :attr:`.IQMBackendBase.index_to_qubit_name` will be used.
254
+ qubit_index_to_name: Mapping from qubit indices in the circuit to qubit names on the device.
255
+ If ``None``, :attr:`.IQMBackendBase.index_to_qubit_name` will be used.
259
256
 
260
257
  Returns:
261
258
  data transfer object representing the circuit
@@ -264,9 +261,9 @@ class IQMBackend(IQMBackendBase):
264
261
  ValueError: circuit contains an unsupported instruction or is not transpiled in general
265
262
 
266
263
  """
267
- if qubit_mapping is None:
268
- qubit_mapping = self._idx_to_qb
269
- instructions = tuple(serialize_instructions(circuit, qubit_index_to_name=qubit_mapping))
264
+ if qubit_index_to_name is None:
265
+ qubit_index_to_name = self._idx_to_qb
266
+ instructions = tuple(serialize_instructions(circuit, qubit_index_to_name=qubit_index_to_name))
270
267
 
271
268
  try:
272
269
  metadata = to_json_dict(circuit.metadata)
@@ -291,7 +288,7 @@ facade_names: dict[str, IQMFakeBackend] = {
291
288
  class IQMFacadeBackend(IQMBackend):
292
289
  """Simulates locally the execution of quantum circuits on a remote mock IQM quantum computer.
293
290
 
294
- This backend is meant to be used to run circuits on a mock IQM server that has no real quantum hardware,
291
+ This backend is meant to be used to run circuits on a mock IQM Server that has no real quantum hardware,
295
292
  and if the mock execution is successful, simulate the circuits locally using an error model that
296
293
  is broadly representative of the mocked QPU. Finally it returns the *simulated results*.
297
294
 
@@ -299,13 +296,13 @@ class IQMFacadeBackend(IQMBackend):
299
296
 
300
297
  .. important::
301
298
 
302
- When using a facade backend, the IQM server URL of :class:`IQMProvider` should always point to a mock environment
299
+ When using a facade backend, the IQM Server URL of :class:`IQMProvider` should always point to a mock environment
303
300
  rather than a real quantum computer, as the execution results from the server will be discarded and replaced by
304
301
  a locally simulated result generated by Qiskit Aer. If you use a real quantum computer with a facade backend,
305
302
  you will just waste your credits and/or computation time.
306
303
 
307
304
  Args:
308
- client: Client instance for communicating with an IQM server.
305
+ client: Client instance for communicating with an IQM Server.
309
306
  name: Name of the fake backend (simulator instance) to use. If None, will be determined automatically based
310
307
  on the static quantum architecture of the server.
311
308
  kwargs: Optional arguments to be passed to the parent class.
@@ -341,7 +338,13 @@ class IQMFacadeBackend(IQMBackend):
341
338
  return False
342
339
  return True
343
340
 
344
- def run(self, run_input: QuantumCircuit | list[QuantumCircuit], **options) -> JobV1:
341
+ def run(
342
+ self,
343
+ run_input: QuantumCircuit | list[QuantumCircuit],
344
+ *,
345
+ use_timeslot: bool = False,
346
+ **options,
347
+ ) -> JobV1:
345
348
  circuits = [run_input] if isinstance(run_input, QuantumCircuit) else run_input
346
349
  circuits_validated_cregs: list[bool] = [self._validate_no_empty_cregs(circuit) for circuit in circuits]
347
350
  if not all(circuits_validated_cregs):
@@ -350,7 +353,7 @@ class IQMFacadeBackend(IQMBackend):
350
353
  "see the user guide."
351
354
  )
352
355
 
353
- iqm_backend_job = super().run(run_input, **options)
356
+ iqm_backend_job = super().run(run_input, use_timeslot=use_timeslot, **options)
354
357
  iqm_backend_job.result() # get and discard results
355
358
  if iqm_backend_job.status() == JobStatus.ERROR:
356
359
  raise RuntimeError("Remote execution did not succeed.")
@@ -360,18 +363,28 @@ class IQMFacadeBackend(IQMBackend):
360
363
  class IQMProvider:
361
364
  """Provider for IQM backends.
362
365
 
363
- IQMProvider connects to a quantum computer through an IQM server.
366
+ IQMProvider connects to a quantum computer through an IQM Server.
364
367
  If the server requires user authentication, you can provide it either using environment
365
368
  variables, or as keyword arguments to IQMProvider. The user authentication kwargs are passed
366
369
  through to :class:`~iqm.iqm_client.iqm_client.IQMClient` as is, and are documented there.
367
370
 
368
371
  Args:
369
- url: URL of the IQM server (e.g. "https://cocos.resonance.meetiqm.com/garnet")
372
+ url: URL of the IQM Server (e.g. "https://resonance.meetiqm.com/").
373
+ quantum_computer: ID or alias of the quantum computer to connect to, if the IQM Server
374
+ instance controls more than one (e.g. "garnet"). ``None`` means connect to the
375
+ default one.
370
376
 
371
377
  """
372
378
 
373
- def __init__(self, url: str, **user_auth_args): # contains keyword args token or tokens_file
379
+ def __init__(
380
+ self,
381
+ url: str,
382
+ *,
383
+ quantum_computer: str | None = None,
384
+ **user_auth_args, # contains keyword args token or tokens_file
385
+ ):
374
386
  self.url = url
387
+ self.quantum_computer = quantum_computer
375
388
  self.user_auth_args = user_auth_args
376
389
 
377
390
  def get_backend(
@@ -396,7 +409,7 @@ class IQMProvider:
396
409
  Backend instance for connecting to a quantum computer.
397
410
 
398
411
  """
399
- client = IQMClient(self.url, **self.user_auth_args)
412
+ client = IQMClient(self.url, quantum_computer=self.quantum_computer, **self.user_auth_args)
400
413
 
401
414
  if name and name.startswith("facade_"):
402
415
  return IQMFacadeBackend(client, name=name, calibration_set_id=calibration_set_id, use_metrics=use_metrics)
@@ -19,18 +19,15 @@ from collections.abc import Iterable
19
19
  import logging
20
20
  from typing import TypeAlias
21
21
 
22
- from iqm.iqm_client import (
23
- DynamicQuantumArchitecture,
24
- GateImplementationInfo,
25
- GateInfo,
26
- ObservationFinder,
27
- )
22
+ from iqm.iqm_client import ObservationFinder
28
23
  from iqm.qiskit_iqm.move_gate import MoveGate
29
24
  from qiskit.circuit import Delay, Gate, IfElseOp, Parameter, Reset
30
25
  from qiskit.circuit.library import CZGate, IGate, Measure, RGate
31
26
  from qiskit.providers import QubitProperties
32
27
  from qiskit.transpiler import InstructionProperties, Target
33
28
 
29
+ from iqm.station_control.interface.models import DynamicQuantumArchitecture, GateImplementationInfo, GateInfo
30
+
34
31
  Locus: TypeAlias = tuple[str, ...]
35
32
  """Sequence of QPU component names on which a gate acts."""
36
33
  LocusIdx: TypeAlias = tuple[int, ...]
@@ -83,6 +80,7 @@ class IQMTarget(Target):
83
80
  # (2) make all the IQMTarget.__init__ args keyword-only with non-colliding names, and init
84
81
  # the necessary superclass attributes ourselves.
85
82
  # (1) seems to break pickling during concurrent transpilation using Qiskit's ``transpile``, so we use (2).
83
+ # (2) is broken from Qiskit 2.2 onwards because Target.__init__ no longer has **kwargs nor *args.
86
84
  self.qubit_properties = self._create_qubit_properties(architecture.qubits, metrics)
87
85
 
88
86
  # Using iqm_ as a prefix to avoid name clashes with the base class.
@@ -25,14 +25,14 @@ from iqm.qiskit_iqm.move_gate import MoveGate
25
25
  from packaging.version import Version
26
26
  from qiskit import QuantumCircuit as QiskitQuantumCircuit
27
27
  from qiskit import __version__ as qiskit_version
28
- from qiskit.circuit import ClassicalRegister, Clbit, Operation, QuantumRegister
28
+ from qiskit.circuit import ClassicalRegister, Clbit, Operation, QuantumRegister, Qubit
29
29
  from qiskit.transpiler.layout import Layout
30
30
 
31
31
  from iqm.pulse import CircuitOperation
32
32
 
33
33
 
34
34
  class InstructionNotSupportedError(RuntimeError):
35
- """Raised when a given instruction is not supported by the IQM server."""
35
+ """Raised when a given instruction is not supported by the IQM Server."""
36
36
 
37
37
 
38
38
  @dataclass(frozen=True)
@@ -55,7 +55,7 @@ class MeasurementKey:
55
55
  ``IQMJob``, since otherwise users will not be able to retrieve results from a detached Python
56
56
  environment solely based on the job id. Another option is to use measurement key strings to
57
57
  store the required info. Qiskit does not use measurement keys, so we are free to use them
58
- internally in the communication with the IQM server, and can encode the necessary information in
58
+ internally in the communication with the IQM Server, and can encode the necessary information in
59
59
  them.
60
60
 
61
61
  This class encapsulates the necessary info, and provides methods to transform between this
@@ -142,6 +142,54 @@ def _apply_condition(
142
142
  inst.args["feedback_qubit"] = physical_qubit_name
143
143
 
144
144
 
145
+ def _calculate_ifblock_idx2name_mapping(
146
+ circuit: QiskitQuantumCircuit,
147
+ if_block_qubits: list[Qubit],
148
+ overwrite_layout: Layout | None,
149
+ qubit_index_to_name: dict[int, str],
150
+ ) -> dict[int, str]:
151
+ """Calculate mapping from if-block qubit registers to physical qubit names.
152
+
153
+ The if-block circuit has no qregs of its own, just references to the parent circuit qubits.
154
+ Depending on how the circuit was transpiled, the if-block Qubit Objects might
155
+ not be present in the parent Circuit. This method finds the appropriate physical qubits
156
+ to make a new qubit index to name mapping for it.
157
+
158
+ Args:
159
+ circuit: The parent quantum circuit containing the if-block.
160
+ if_block_qubits: The qubits used in the if-block.
161
+ overwrite_layout: An alternative layout indicating the physical qubit mapping to use for the serialized
162
+ instructions, this overwrites the circuit's layout.
163
+ qubit_index_to_name: Mapping from qubit indices to the corresponding qubit names as obtained from a backend.
164
+
165
+ """
166
+ # Check if we can use the overwrite layout
167
+ use_overwrite_layout = overwrite_layout is not None and all(
168
+ qb in overwrite_layout.get_physical_bits().values() for qb in if_block_qubits
169
+ )
170
+ if use_overwrite_layout:
171
+ physical_qubits = {q: i for i, q in overwrite_layout.get_physical_bits().items()} # type: ignore[union-attr]
172
+ return {k: qubit_index_to_name[physical_qubits[q]] for k, q in enumerate(if_block_qubits)}
173
+ # The if-block qubits are not in the circuit, so we hope they are in the layout
174
+ use_circuit_layout_guess = circuit.layout is not None and all(
175
+ qb in circuit.layout.initial_layout.get_physical_bits().values() for qb in if_block_qubits
176
+ )
177
+ if use_circuit_layout_guess:
178
+ physical_qubits = {q: i for i, q in circuit.layout.initial_layout.get_physical_bits().items()}
179
+ return {k: qubit_index_to_name[physical_qubits[q]] for k, q in enumerate(if_block_qubits)}
180
+
181
+ # Hope that we can find the qubits in the circuit - can be wrong.
182
+ use_circuit_find_bit = all(qb in circuit.qubits for qb in if_block_qubits)
183
+ if use_circuit_find_bit:
184
+ return {k: qubit_index_to_name[circuit.find_bit(q).index] for k, q in enumerate(if_block_qubits)}
185
+ # Catch-all: we cannot determine the mapping, this should never happen.
186
+ raise ValueError(
187
+ "Could not determine the physical locations for if-block qubits. "
188
+ "The if-block uses {if_block_qubits} qubits, but the parent circuit has qubits {circuit.qubits}, "
189
+ "the circuit layout is {circuit.layout.initial_layout}, and the overwrite layout is {overwrite_layout}."
190
+ )
191
+
192
+
145
193
  def serialize_instructions( # noqa: PLR0912, PLR0915
146
194
  circuit: QiskitQuantumCircuit,
147
195
  qubit_index_to_name: dict[int, str],
@@ -179,9 +227,16 @@ def serialize_instructions( # noqa: PLR0912, PLR0915
179
227
  # maps clbits to the latest "measure" instruction to store its result there
180
228
  if clbit_to_measure is None:
181
229
  clbit_to_measure = {}
230
+ invalid_layout = circuit.layout is None or circuit.layout.initial_layout.get_registers() != set(circuit.qregs)
182
231
  for circuit_instruction in circuit.data:
183
232
  instruction = circuit_instruction.operation
184
- qubit_names = tuple(qubit_index_to_name[circuit.find_bit(qubit).index] for qubit in circuit_instruction.qubits)
233
+ if invalid_layout:
234
+ qubit_names = tuple(
235
+ qubit_index_to_name[circuit.find_bit(qubit).index] for qubit in circuit_instruction.qubits
236
+ )
237
+ else:
238
+ physical_qubits = {q: i for i, q in circuit.layout.initial_layout.get_physical_bits().items()}
239
+ qubit_names = tuple(qubit_index_to_name[physical_qubits[qubit]] for qubit in circuit_instruction.qubits)
185
240
  if instruction.name == "r":
186
241
  angle = float(instruction.params[0])
187
242
  phase = float(instruction.params[1])
@@ -241,28 +296,10 @@ def serialize_instructions( # noqa: PLR0912, PLR0915
241
296
  if_block, else_block = instruction.params
242
297
  if else_block is not None and len(else_block) > 0: # Non-empty circuit in else-block
243
298
  raise ValueError("The use of an else-block with if_test is not supported.")
244
- # Serialize the if-block.
245
- # NOTE The if-block circuit has no qregs of its own, just references to the parent circuit qregs,
246
- # and it does not always have every parent circuit qubit.
247
- # Hence we need to make a new qubit index to name mapping for it.
248
-
249
- # NOTE Sometimes the qubits in the if-block are in the circuit and
250
- # sometimes they are in the layout depending on how the circuit was transpiled.
251
- # So let's find out where to get the physical qubits from.
252
- use_overwrite_layout = overwrite_layout is not None and all(
253
- qb in overwrite_layout.get_physical_bits().values() for qb in if_block.qubits
299
+ # Recursively serialize the if-block.
300
+ q_index_to_name = _calculate_ifblock_idx2name_mapping(
301
+ circuit, if_block.qubits, overwrite_layout, qubit_index_to_name
254
302
  )
255
- if use_overwrite_layout:
256
- physical_qubits = {q: i for i, q in overwrite_layout.get_physical_bits().items()} # type: ignore[union-attr]
257
- q_index_to_name = {k: qubit_index_to_name[physical_qubits[q]] for k, q in enumerate(if_block.qubits)}
258
- elif circuit.layout is None or all(qb in circuit.qubits for qb in if_block.qubits):
259
- q_index_to_name = {
260
- k: qubit_index_to_name[circuit.find_bit(q).index] for k, q in enumerate(if_block.qubits)
261
- }
262
- else:
263
- physical_qubits = {q: i for i, q in circuit.layout.initial_layout.get_physical_bits().items()}
264
- q_index_to_name = {k: qubit_index_to_name[physical_qubits[q]] for k, q in enumerate(if_block.qubits)}
265
-
266
303
  if_instructions = serialize_instructions(
267
304
  if_block, q_index_to_name, allowed_nonnative_gates, clbit_to_measure=clbit_to_measure
268
305
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: iqm-client
3
- Version: 32.1.1
3
+ Version: 33.0.1
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,35 +216,25 @@ 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 <12,>=11
220
- Requires-Dist: iqm-exa-common <28,>=27
221
- Requires-Dist: iqm-pulse <13,>=12
222
219
  Requires-Dist: numpy <3.0,>=1.26.4
223
220
  Requires-Dist: packaging ==24.1
224
221
  Requires-Dist: pydantic <3.0,>=2.9.2
225
222
  Requires-Dist: requests ==2.32.3
223
+ Requires-Dist: iqm-pulse <13,>=12.7.1
224
+ Requires-Dist: iqm-station-control-client <13,>=12.0.1
226
225
  Provides-Extra: cirq
227
- Requires-Dist: iqm-station-control-client <12,>=11 ; extra == 'cirq'
228
- Requires-Dist: iqm-exa-common <28,>=27 ; extra == 'cirq'
229
- Requires-Dist: iqm-pulse <13,>=12 ; extra == 'cirq'
230
226
  Requires-Dist: cirq-core[contrib] ~=1.2 ; extra == 'cirq'
231
227
  Requires-Dist: ply ==3.11 ; extra == 'cirq'
232
228
  Requires-Dist: llvmlite >=0.44.0 ; extra == 'cirq'
233
229
  Requires-Dist: numba >=0.61.0 ; extra == 'cirq'
234
230
  Provides-Extra: cli
235
- Requires-Dist: iqm-station-control-client <12,>=11 ; extra == 'cli'
236
- Requires-Dist: iqm-exa-common <28,>=27 ; extra == 'cli'
237
- Requires-Dist: iqm-pulse <13,>=12 ; extra == 'cli'
238
231
  Requires-Dist: click <9,>=8.1.6 ; extra == 'cli'
239
232
  Requires-Dist: jsonschema >=4.6.0 ; extra == 'cli'
240
233
  Requires-Dist: psutil >=5.9.2 ; extra == 'cli'
241
234
  Requires-Dist: types-psutil ; extra == 'cli'
242
235
  Requires-Dist: python-daemon >=2.3.0 ; extra == 'cli'
243
236
  Provides-Extra: qiskit
244
- Requires-Dist: iqm-station-control-client <12,>=11 ; extra == 'qiskit'
245
- Requires-Dist: iqm-exa-common <28,>=27 ; extra == 'qiskit'
246
- Requires-Dist: iqm-pulse <13,>=12 ; extra == 'qiskit'
247
- Requires-Dist: qiskit <=1.4.2,>=1.0 ; extra == 'qiskit'
237
+ Requires-Dist: qiskit <2.2,>=1.0 ; extra == 'qiskit'
248
238
  Requires-Dist: qiskit-aer <0.18,>=0.13.1 ; extra == 'qiskit'
249
239
 
250
240
  IQM Client
@@ -260,16 +250,15 @@ adapters for `IQM's <https://www.meetiqm.com>`_ quantum computers, which allow y
260
250
  (currently only the Qiskit adapter contains IQM noise models)
261
251
  * Run quantum circuits on an IQM quantum computer
262
252
 
263
- Also includes a `CLI utility <https://docs.meetiqm.com/iqm-client/user_guide_cli.html>`_ for managing user
264
- authentication when using IQM quantum computers.
253
+ Also includes a CLI utility for managing user authentication when using IQM quantum computers.
265
254
 
266
255
  Installation
267
256
  ============
268
257
 
269
- For executing code on an IQM quantum computer, you can use for example
270
- `Qiskit on IQM <https://docs.meetiqm.com/iqm-client/user_guide_qiskit.html>`_ or
271
- `Cirq on IQM <https://docs.meetiqm.com/iqm-client/user_guide_cirq.html>`_, which can be installed as optional
272
- features of IQM Client from the Python Package Index (PyPI), e.g.:
258
+ For executing code on an IQM quantum computer, you can use for example the
259
+ Qiskit on IQM or Cirq on IQM user guides found in the documentation.
260
+ Qiskit on IQM and Cirq on IQM are optional features that can be installed alongside the base IQM Client library.
261
+ An example showing how to install from the public Python Package Index (PyPI):
273
262
 
274
263
  .. code-block:: bash
275
264
 
@@ -281,8 +270,7 @@ features of IQM Client from the Python Package Index (PyPI), e.g.:
281
270
  Python environment, you should first uninstall them with ``$ pip uninstall qiskit-iqm cirq-iqm``.
282
271
  In this case, you should also include the ``--force-reinstall`` option in the ``iqm-client`` installation command.
283
272
 
284
- The `CLI utility <https://docs.meetiqm.com/iqm-client/user_guide_cli.html>`_ for managing user authentication can also
285
- be installed as an optional feature:
273
+ The CLI utility for managing user authentication can also be installed as an optional feature:
286
274
 
287
275
  .. code-block:: bash
288
276
 
@@ -304,10 +292,15 @@ Documentation
304
292
 
305
293
  Documentation for the latest version is `available online <https://docs.meetiqm.com/iqm-client/>`_.
306
294
  You can build documentation for any older version locally by downloading the corresponding package from PyPI,
307
- and running the docs builder. For versions 20.12 and later this is done by running ``./docbuild`` in the
308
- ``iqm-client`` root directory, and for earlier versions by running ``tox run -e docs``.
295
+ and running the docs builder. For versions greater than equal to 20.12 but less than 33.0.0 this is done by
296
+ running ``./docbuild`` in the ``iqm-client`` root directory, and for earlier versions by running ``tox run -e docs``.
309
297
 
310
298
  ``./docbuild`` or ``tox run -e docs`` will build the documentation at ``./build/sphinx/html``.
299
+
300
+ Versions greater than or equal to 33.0.0 use the command:
301
+ ``sphinx-build -q -d build/.doctrees/iqm-client iqm-client/docs build/docs/iqm-client``
302
+ (``build/docs/`` directory has to be created first).
303
+
311
304
  These commands require installing the ``sphinx`` and ``sphinx-book-theme`` Python packages and
312
305
  `graphviz <https://graphviz.org/>`_.
313
306
 
@@ -0,0 +1,63 @@
1
+ iqm/cirq_iqm/__init__.py,sha256=1zTyxtF39OD11D00ZujgqciJ_9GBDYe-PVBLqZp4NaA,831
2
+ iqm/cirq_iqm/extended_qasm_parser.py,sha256=csDzfHLhy_9maGbappLbnFo2NHQjQeyd-1F8P380Mbk,1917
3
+ iqm/cirq_iqm/iqm_gates.py,sha256=xnZex5ZfNOk_WSsFjVCRybc14FlGNbmwOs3mIfOE_F8,2488
4
+ iqm/cirq_iqm/iqm_sampler.py,sha256=b5CpcD7tgUAWKYdAKcFrshfv1I1GN5eC7mfMyXoGF_8,12606
5
+ iqm/cirq_iqm/optimizers.py,sha256=Jcb6W7-M9wYa5mZztq33jQpWuIzD5ulE16ZperIc-wU,8566
6
+ iqm/cirq_iqm/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ iqm/cirq_iqm/serialize.py,sha256=2w_8sk-X18qWb2FFocBP_MXJALoNmosZIs5Ilp7ueR8,8516
8
+ iqm/cirq_iqm/transpiler.py,sha256=zjJzZb5HyjTfqhL0rMI3dt_3GPGLdVYah9UlGoC8hcc,2173
9
+ iqm/cirq_iqm/devices/__init__.py,sha256=WxbvNAqmeoV4mNy9M9IpjwmyNDpAP7ec1XFdjN7w_YA,840
10
+ iqm/cirq_iqm/devices/adonis.py,sha256=ZWaA4_OfO6fBcrLYHONTjtW8aSlVq2nwy9IiW529gQw,1390
11
+ iqm/cirq_iqm/devices/aphrodite.py,sha256=0K8zRo6gVVadQo7LJfs6laUVLzS3fNkrXMrPlwieyJ8,4018
12
+ iqm/cirq_iqm/devices/apollo.py,sha256=_k7L45zyHZnpdZWMMXO6zci3XY3UEnBj2RmvxM6D7Mg,2238
13
+ iqm/cirq_iqm/devices/iqm_device.py,sha256=CyAP0kU9vfovNWvF4CVX8Wp6_QLxLN8fy9I2YZkHHwM,15425
14
+ iqm/cirq_iqm/devices/iqm_device_metadata.py,sha256=3ONFzPa1G_nE4nSAldsd_Ly8i1__n2Sll43qN9yNQUw,7161
15
+ iqm/cirq_iqm/examples/demo_adonis.py,sha256=rPd8VYqccYijG6t92hfvKsk8RAaMGHOmm4JelYjt3rw,1728
16
+ iqm/cirq_iqm/examples/demo_apollo.py,sha256=vnDwcEXqEyew0cf4SDDQ2budP-giizSNJU3dgOw15H0,1751
17
+ iqm/cirq_iqm/examples/demo_common.py,sha256=k0MAeLDNxZJGv8FNrgMEwaQZsuXeDY5NBM2rLSIQxvU,8745
18
+ iqm/cirq_iqm/examples/demo_iqm_execution.py,sha256=_Wf6rsL_q93M5Ww6eurbXL8VPngGkxtpZBAbNpnn2ek,2440
19
+ iqm/cirq_iqm/examples/usage.ipynb,sha256=Kyfyu_MwqzTavHVNjgrWJo1tZPeJwTw7ExcU0MFYNRk,34208
20
+ iqm/iqm_client/__init__.py,sha256=43TjxuxX_iFl5h2GbQqyIkxmuraWkX4OyvF444Hlyvk,1306
21
+ iqm/iqm_client/errors.py,sha256=weKTljBvDzJxK58NHCXaZXoITVMBvsOkvYP0abs5Dfc,1226
22
+ iqm/iqm_client/iqm_client.py,sha256=OGKp6QDwtIY2RjJ8_uG44UJlMpm7VH_HTgqZFPPN2YA,22632
23
+ iqm/iqm_client/models.py,sha256=UHlkdG0bjaksvyARYB4RZYx1EJ4nLMyHiPS_7-Bwr14,15888
24
+ iqm/iqm_client/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
+ iqm/iqm_client/transpile.py,sha256=ZicsOJiT5zEaaYMWuFddnJT2e4e9Oyu7B5pO8Z_ah7M,37334
26
+ iqm/iqm_client/util.py,sha256=obzh1g6PNEXOj7k3gUkiylNUhyqutbWlxlEpfyyU_fk,1505
27
+ iqm/iqm_client/validation.py,sha256=SyshUOxmnlwb0haWEAVkgynGOYUMV7Os281sUe29tpE,12621
28
+ iqm/iqm_server_client/__init__.py,sha256=hqfe65DmdYu5qDbYsg3Uq94DRe2zkbMJP_XFYsMbGSQ,645
29
+ iqm/iqm_server_client/errors.py,sha256=I48dRY_Sies2vQzyjzQxhHm_J5RopyL5hqAmlnBy67w,192
30
+ iqm/iqm_server_client/iqm_server_client.py,sha256=en2C4hZ4VzHtg07EtxpuC-cFEw2kzTi1_GzfYKk-NWY,31365
31
+ iqm/iqm_server_client/models.py,sha256=YpyppxEu2obSIaUvuc3_mRKcGscs8YLrcWrTk7EHKzM,5597
32
+ iqm/iqm_server_client/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
33
+ iqm/qiskit_iqm/__init__.py,sha256=VPmcMV5nBOEIhVsOceIUkdZtWJ3tGlaKrDFP-FRuOCs,1667
34
+ iqm/qiskit_iqm/iqm_backend.py,sha256=3ydSroylh_Us-5RJfOQgOkL7eqF4fWe5ejXueDTcAj8,5584
35
+ iqm/qiskit_iqm/iqm_circuit.py,sha256=jaPo3zc5FC0vAIumh5d56fr44fDaJXXwcquBzQEy1Yg,1400
36
+ iqm/qiskit_iqm/iqm_circuit_validation.py,sha256=SzfCJclUh1kK6Kcz5puKlCtE0hZfWxYYKF35EagU204,1750
37
+ iqm/qiskit_iqm/iqm_job.py,sha256=SdyZeQi5CoGxBeJyfIvOM7DA0ONJ5tbTHpk4hajpUFk,12395
38
+ iqm/qiskit_iqm/iqm_move_layout.py,sha256=vFdba0DStu8Pebi3qxZBYOLCYC2Atl28YlSzbSGER4Y,10557
39
+ iqm/qiskit_iqm/iqm_naive_move_pass.py,sha256=GcS2DyYWwG-qMyczXqTXxMxNqRB4IVeWNP3CzguKpBw,15639
40
+ iqm/qiskit_iqm/iqm_provider.py,sha256=pK1CcAOQKG5jXYfjxshuVSmMLWQG1-k0cyghRmGpWN8,19273
41
+ iqm/qiskit_iqm/iqm_target.py,sha256=nqurRgM_p2nOw8ty8ncw38vSErEdXpHSlbp7Rpavnc4,16232
42
+ iqm/qiskit_iqm/iqm_transpilation.py,sha256=RccAHY7lNXvg7TWKG7fBNBCgFu5Xd9B_J9V9uKLlmuY,18336
43
+ iqm/qiskit_iqm/move_gate.py,sha256=UbrQSfrpVV3QKGJ93TelxEfZkl1wY4uWL8IH_QDpGUw,2840
44
+ iqm/qiskit_iqm/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
45
+ iqm/qiskit_iqm/qiskit_to_iqm.py,sha256=imMmuOi_tuujIrGzBFnGOtxpGHcIkncXutr5QFekJl4,20677
46
+ iqm/qiskit_iqm/transpiler_plugins.py,sha256=BrCkUFQUdi0m8K8V0ERYgtPAkvoX4VlYIJwnFKyCN9Q,8831
47
+ iqm/qiskit_iqm/examples/__init__.py,sha256=M4ElQHCo-WxtVXK39bF3QiFT3IGXPtZ1khqexHiTBEc,20
48
+ iqm/qiskit_iqm/examples/bell_measure.py,sha256=pwSGeggb8HuUWh4SYUkbLDeZI00gxRmVw0svA_hbn-Y,3056
49
+ iqm/qiskit_iqm/examples/transpile_example.py,sha256=FNpHy6nxJgaXhgbl7_SavHRNaNMyk4lSkd_0a2GSKh4,2410
50
+ iqm/qiskit_iqm/fake_backends/__init__.py,sha256=fkw2UHT-3aJbAKvR1WYUN7_4N5Gdwpx9bm6vlWj1tm0,874
51
+ iqm/qiskit_iqm/fake_backends/fake_adonis.py,sha256=DWftCPu6ElE4P47Z-y3bojg0unDJ6F5ur2aDqfoY85s,2248
52
+ iqm/qiskit_iqm/fake_backends/fake_aphrodite.py,sha256=oB5wFtZ2VYy0TxoL-eBgYnVAwamGPYlzIQK89npwQUg,15533
53
+ iqm/qiskit_iqm/fake_backends/fake_apollo.py,sha256=C05u3knnCsA8DCuWU31EwUvJ6xilef36J06GoE2vAe4,6623
54
+ iqm/qiskit_iqm/fake_backends/fake_deneb.py,sha256=HstlV-PSbyCttY1uEyWr-YgsMNkv9Ntf6zffav837_4,3216
55
+ iqm/qiskit_iqm/fake_backends/fake_garnet.py,sha256=GI0xafTCj1Um09qVuccO6GPOGBm6ygul_O40Wu220Ys,5555
56
+ iqm/qiskit_iqm/fake_backends/iqm_fake_backend.py,sha256=IfVKxlPlAVCHTgli2scRP1gkVCXIiSuL6-QjjsZp83Q,16808
57
+ iqm_client-33.0.1.dist-info/AUTHORS.rst,sha256=qsxeK5A3-B_xK3hNbhFHEIkoHNpo7sdzYyRTs7Bdtm8,795
58
+ iqm_client-33.0.1.dist-info/LICENSE.txt,sha256=2DXrmQtVVUV9Fc9RBFJidMiTEaQlG2oAtlC9PMrEwTk,11333
59
+ iqm_client-33.0.1.dist-info/METADATA,sha256=yJ-Xub8FOJ-nI248Vlv15cG_8H38DvRxSp6JxFx3ucY,17435
60
+ iqm_client-33.0.1.dist-info/WHEEL,sha256=y4mX-SOX4fYIkonsAGA5N0Oy-8_gI4FXw5HNI1xqvWg,91
61
+ iqm_client-33.0.1.dist-info/entry_points.txt,sha256=Kk2qfRwk8vbIJ7qCAvmaUogfRRn6t92_hBFhe6kqAE4,1317
62
+ iqm_client-33.0.1.dist-info/top_level.txt,sha256=NB4XRfyDS6_wG9gMsyX-9LTU7kWnTQxNvkbzIxGv3-c,4
63
+ iqm_client-33.0.1.dist-info/RECORD,,
iqm/iqm_client/api.py DELETED
@@ -1,90 +0,0 @@
1
- # Copyright 2024 IQM client developers
2
- #
3
- # Licensed under the Apache License, Version 2.0 (the "License");
4
- # you may not use this file except in compliance with the License.
5
- # You may obtain a copy of the License at
6
- #
7
- # http://www.apache.org/licenses/LICENSE-2.0
8
- #
9
- # Unless required by applicable law or agreed to in writing, software
10
- # distributed under the License is distributed on an "AS IS" BASIS,
11
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- # See the License for the specific language governing permissions and
13
- # limitations under the License.
14
- """This module contains definitions of IQM Server API endpoints."""
15
-
16
- from enum import Enum, auto
17
- from posixpath import join
18
-
19
-
20
- class APIEndpoint(Enum):
21
- """Supported API endpoints."""
22
-
23
- GET_JOB_RESULT = auto()
24
- GET_JOB_REQUEST_PARAMETERS = auto()
25
- GET_JOB_CALIBRATION_SET_ID = auto()
26
- GET_JOB_CIRCUITS_BATCH = auto()
27
- GET_JOB_ERROR_LOG = auto()
28
- SUBMIT_JOB = auto()
29
- GET_JOB_COUNTS = auto()
30
- GET_JOB_STATUS = auto()
31
- GET_JOB_TIMELINE = auto()
32
- ABORT_JOB = auto()
33
-
34
-
35
- class APIConfig:
36
- """Provides supported API endpoints for a given API variant."""
37
-
38
- def __init__(self, station_control_url: str):
39
- """Args:
40
- station_control_url: URL of the IQM server,
41
- e.g. https://test.qc.iqm.fi/station or https://cocos.resonance.meetiqm.com/garnet
42
-
43
- """
44
- self.station_control_url = station_control_url
45
- self.urls = self._get_api_urls()
46
-
47
- @staticmethod
48
- def _get_api_urls() -> dict[APIEndpoint, str]:
49
- """Returns:
50
- Relative URLs for each supported API endpoints.
51
-
52
- """
53
- return {
54
- # TODO SW-1434: Use StationControlClient methods for communication instead of REST endpoints
55
- APIEndpoint.GET_JOB_RESULT: "jobs/%s/measurements",
56
- APIEndpoint.GET_JOB_REQUEST_PARAMETERS: "jobs/%s/request_parameters",
57
- APIEndpoint.GET_JOB_CALIBRATION_SET_ID: "jobs/%s/calibration_set_id",
58
- APIEndpoint.GET_JOB_CIRCUITS_BATCH: "jobs/%s/circuits_batch",
59
- APIEndpoint.GET_JOB_ERROR_LOG: "jobs/%s/error_log",
60
- APIEndpoint.SUBMIT_JOB: "circuits",
61
- APIEndpoint.GET_JOB_COUNTS: "circuits/%s/counts",
62
- APIEndpoint.GET_JOB_STATUS: "jobs/%s/status",
63
- APIEndpoint.GET_JOB_TIMELINE: "jobs/%s/timeline",
64
- APIEndpoint.ABORT_JOB: "jobs/%s/abort",
65
- }
66
-
67
- def is_supported(self, endpoint: APIEndpoint) -> bool:
68
- """Args:
69
- endpoint: API endpoint.
70
-
71
- Returns:
72
- True if the endpoint is supported, False otherwise.
73
-
74
- """
75
- return endpoint in self.urls
76
-
77
- def url(self, endpoint: APIEndpoint, *args) -> str:
78
- """Args:
79
- endpoint: API endpoint.
80
- args: Arguments to be passed to the URL.
81
-
82
- Returns:
83
- URL for the given endpoint.
84
-
85
- Raises:
86
- ValueError: If the endpoint is not supported.
87
-
88
- """
89
- url = self.urls.get(endpoint, "")
90
- return join(self.station_control_url, url % args)