iqm-client 32.0.0__py3-none-any.whl → 33.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) 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 +2 -2
  20. iqm/qiskit_iqm/examples/transpile_example.py +9 -4
  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 +115 -56
  31. iqm/qiskit_iqm/iqm_provider.py +49 -36
  32. iqm/qiskit_iqm/iqm_target.py +12 -8
  33. iqm/qiskit_iqm/iqm_transpilation.py +219 -26
  34. iqm/qiskit_iqm/qiskit_to_iqm.py +150 -41
  35. iqm/qiskit_iqm/transpiler_plugins.py +11 -8
  36. {iqm_client-32.0.0.dist-info → iqm_client-33.0.0.dist-info}/METADATA +4 -14
  37. iqm_client-33.0.0.dist-info/RECORD +63 -0
  38. iqm/iqm_client/api.py +0 -90
  39. iqm/iqm_client/authentication.py +0 -206
  40. iqm_client-32.0.0.dist-info/RECORD +0 -60
  41. {iqm_client-32.0.0.dist-info → iqm_client-33.0.0.dist-info}/AUTHORS.rst +0 -0
  42. {iqm_client-32.0.0.dist-info → iqm_client-33.0.0.dist-info}/LICENSE.txt +0 -0
  43. {iqm_client-32.0.0.dist-info → iqm_client-33.0.0.dist-info}/WHEEL +0 -0
  44. {iqm_client-32.0.0.dist-info → iqm_client-33.0.0.dist-info}/entry_points.txt +0 -0
  45. {iqm_client-32.0.0.dist-info → iqm_client-33.0.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,179 @@
1
+ # Copyright 2025 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
+ """Data models used by IQMServerClient."""
15
+
16
+ from __future__ import annotations
17
+
18
+ from datetime import datetime
19
+ from enum import StrEnum
20
+ from typing import Literal, TypeAlias
21
+ from uuid import UUID
22
+
23
+ from pydantic import BaseModel, Field
24
+
25
+ from iqm.station_control.interface.models import ObservationSetWithObservations
26
+ from iqm.station_control.interface.pydantic_base import PydanticBase
27
+
28
+ CalibrationSet: TypeAlias = ObservationSetWithObservations
29
+ QualityMetricSet: TypeAlias = ObservationSetWithObservations
30
+
31
+
32
+ # Keep 'str' for potential new sources
33
+ Source: TypeAlias = Literal["iqm-server", "iqm-station-control"] | str
34
+ """Type indicating the source of a particular job-related object from the server."""
35
+
36
+
37
+ class QuantumComputer(PydanticBase):
38
+ """Quantum computer attributes."""
39
+
40
+ id: UUID
41
+ """Unique ID of the quantum computer."""
42
+ alias: str
43
+ """Quantum computer alias that can be used as a substitute for id in API calls."""
44
+
45
+
46
+ class ListQuantumComputersResponse(PydanticBase):
47
+ """Response of GET /v1/quantum-computers"""
48
+
49
+ quantum_computers: list[QuantumComputer]
50
+ """List of available quantum computers."""
51
+
52
+
53
+ class ProgressInfo(PydanticBase):
54
+ """Progress information about completing an arbitrary task.
55
+
56
+ Used e.g. for tracking job completion.
57
+ """
58
+
59
+ value: int
60
+ """Current progress indicator value."""
61
+ max_value: int
62
+ """When we hit this, the task is done."""
63
+
64
+
65
+ class JobExecution(PydanticBase):
66
+ """Progress information about job execution."""
67
+
68
+ progress: dict[str, ProgressInfo]
69
+ """Mapping from label to its progress information (value, max_value)."""
70
+
71
+
72
+ class JobCompilation(PydanticBase):
73
+ """Progress information about job compilation."""
74
+
75
+ calibration_set_id: UUID | None = None
76
+ """ID of the calibration set used by the compiler."""
77
+
78
+
79
+ class JobMessage(PydanticBase):
80
+ """Message log for a job."""
81
+
82
+ source: Source
83
+ """Source of the message."""
84
+ message: str
85
+ """Content of the message."""
86
+
87
+ def __str__(self) -> str:
88
+ """Prettyprinting."""
89
+ return f"{self.source}: {self.message}"
90
+
91
+
92
+ class JobError(PydanticBase):
93
+ """Error log for a job."""
94
+
95
+ source: Source
96
+ """Source of the error."""
97
+ message: str
98
+ """Verbose error message."""
99
+ error_code: str | None = None
100
+ """Short error code classifying the error category."""
101
+
102
+ def __str__(self) -> str:
103
+ """Prettyprinting."""
104
+ return f"{self.source}: {self.error_code}: {self.message}"
105
+
106
+
107
+ class TimelineEntry(BaseModel):
108
+ """Timeline entry for a job."""
109
+
110
+ source: Source
111
+ """Source of the timeline entry."""
112
+ status: str
113
+ """Name of the execution step that was reached."""
114
+ timestamp: datetime
115
+ """Time at which ``status`` was reached."""
116
+
117
+
118
+ class JobStatus(StrEnum):
119
+ """Job statuses in IQMServer."""
120
+
121
+ WAITING = "waiting"
122
+ """Job is in a queue, waiting to be executed."""
123
+ PROCESSING = "processing"
124
+ """Job is being executed."""
125
+ COMPLETED = "completed"
126
+ """Job has completed successfully."""
127
+ FAILED = "failed"
128
+ """Job has failed."""
129
+ CANCELLED = "cancelled"
130
+ """Job has been cancelled by the user or the admin."""
131
+
132
+ @classmethod
133
+ def terminal_statuses(cls) -> frozenset[JobStatus]:
134
+ """Statuses from which the execution can't continue."""
135
+ return frozenset({cls.COMPLETED, cls.FAILED, cls.CANCELLED})
136
+
137
+
138
+ # Note: there is another, related JobData class for StationControlClient
139
+ class JobData(PydanticBase):
140
+ """Status, artifacts and metadata of a job."""
141
+
142
+ id: UUID
143
+ """Unique ID of the job."""
144
+ status: JobStatus
145
+ """Current job status."""
146
+ execution: JobExecution | None = None
147
+ """Execution information for the job."""
148
+ compilation: JobCompilation | None = None
149
+ """Compilation information for the job."""
150
+ messages: list[JobMessage] = Field(default=[])
151
+ """Informational messages for the job."""
152
+ errors: list[JobError] = Field(default=[])
153
+ """Errors for a failed job."""
154
+ queue_position: int | None = None
155
+ """Iff the status is JobStatus.WAITING, the number of jobs ahead of this job in the queue.
156
+ Otherwise None."""
157
+ timeline: list[TimelineEntry] = Field(default=[])
158
+ """Server-side statuses reached by the job so far. May include statuses from several services."""
159
+
160
+ def find_timeline_entry(
161
+ self,
162
+ *,
163
+ status: str,
164
+ source: Source | None = None,
165
+ ) -> TimelineEntry | None:
166
+ """Search the timeline for an entry matching the given criteria.
167
+
168
+ Args:
169
+ status: Status of the searched timeline entry.
170
+ source: Source of the searched timeline entry. If None, accepts any source.
171
+
172
+ Returns:
173
+ The first matching entry or ``None`` if the job timeline does not have any matching entries.
174
+
175
+ """
176
+ for timeline_entry in self.timeline:
177
+ if timeline_entry.status == status and (timeline_entry.source == source or source is None):
178
+ return timeline_entry
179
+ return None
File without changes
@@ -13,6 +13,8 @@
13
13
  # limitations under the License.
14
14
  """Qiskit adapter for IQM's quantum computers."""
15
15
 
16
+ import warnings
17
+
16
18
  from iqm.qiskit_iqm.fake_backends import IQMErrorProfile, IQMFakeAdonis, IQMFakeAphrodite, IQMFakeApollo, IQMFakeDeneb
17
19
  from iqm.qiskit_iqm.fake_backends.iqm_fake_backend import IQMFakeBackend
18
20
  from iqm.qiskit_iqm.iqm_circuit import IQMCircuit
@@ -23,4 +25,10 @@ from iqm.qiskit_iqm.iqm_provider import IQMBackend, IQMProvider, __version__
23
25
  from iqm.qiskit_iqm.iqm_transpilation import IQMOptimizeSingleQubitGates, optimize_single_qubit_gates
24
26
  from iqm.qiskit_iqm.move_gate import MoveGate
25
27
  from iqm.qiskit_iqm.transpiler_plugins import * # noqa: F403
28
+ from packaging.version import Version
26
29
  from qiskit import __version__ as qiskit_version
30
+
31
+ if Version(qiskit_version) < Version("2.0.0"):
32
+ warnings.warn(
33
+ DeprecationWarning("Support for Qiskit versions < 2.0 is deprecated and will be removed in a future release.")
34
+ )
@@ -27,7 +27,7 @@ def bell_measure(server_url: str, token: str | None = None, shots: int = 1000) -
27
27
  """Execute a quantum circuit that prepares and measures a generalized Bell (aka GHZ) state.
28
28
 
29
29
  Args:
30
- server_url: URL of the IQM server used for execution
30
+ server_url: URL of the IQM Server used for execution
31
31
  token: API token for authentication. If not given, uses :env:`IQM_TOKEN`.
32
32
  shots: Requested number of shots.
33
33
 
@@ -66,7 +66,7 @@ def bell_measure(server_url: str, token: str | None = None, shots: int = 1000) -
66
66
  if __name__ == "__main__":
67
67
  argparser = argparse.ArgumentParser()
68
68
  argparser.add_argument(
69
- "--url", required=True, help='IQM server URL, for example "https://cocos.resonance.meetiqm.com/garnet"'
69
+ "--url", required=True, help='IQM Server URL, for example "https://cocos.resonance.meetiqm.com/garnet"'
70
70
  )
71
71
  argparser.add_argument(
72
72
  "--token",
@@ -18,15 +18,20 @@ https://docs.meetiqm.com/iqm-client/user_guide_qiskit.html
18
18
 
19
19
  import argparse
20
20
 
21
- from iqm.qiskit_iqm.iqm_provider import IQMProvider
21
+ from iqm.qiskit_iqm.iqm_provider import IQMBackend, IQMProvider
22
22
  from qiskit import QuantumCircuit, transpile
23
23
 
24
24
 
25
+ def num_connected_qubits(backend: IQMBackend) -> int:
26
+ """Return the size of the largest connected component of the backend coupling map."""
27
+ return max(backend.coupling_map.connected_components(), key=lambda cmap: cmap.size()).size()
28
+
29
+
25
30
  def transpile_example(server_url: str) -> tuple[QuantumCircuit, dict[str, int]]:
26
31
  """Run a GHZ circuit transpiled using the Qiskit transpile function.
27
32
 
28
33
  Args:
29
- server_url: URL of the IQM server used for execution
34
+ server_url: URL of the IQM Server used for execution
30
35
 
31
36
  Returns:
32
37
  transpiled circuit, a mapping of bitstrings representing qubit measurement results to counts for each result
@@ -34,7 +39,7 @@ def transpile_example(server_url: str) -> tuple[QuantumCircuit, dict[str, int]]:
34
39
  """
35
40
  backend = IQMProvider(server_url).get_backend()
36
41
 
37
- num_qubits = min(backend.num_qubits, 5) # use at most 5 qubits
42
+ num_qubits = min(num_connected_qubits(backend), 5) # use at most 5 qubits
38
43
  circuit = QuantumCircuit(num_qubits)
39
44
  circuit.h(0)
40
45
  for i in range(1, num_qubits):
@@ -51,7 +56,7 @@ if __name__ == "__main__":
51
56
  argparser = argparse.ArgumentParser()
52
57
  argparser.add_argument(
53
58
  "--url",
54
- help="IQM server URL",
59
+ help="IQM Server URL",
55
60
  # For example https://cocos.resonance.meetiqm.com/garnet
56
61
  default="https://<IQM SERVER>",
57
62
  )
@@ -13,9 +13,10 @@
13
13
  # limitations under the License.
14
14
  """Fake backend for IQM's 5-qubit Adonis architecture."""
15
15
 
16
- from iqm.iqm_client import StaticQuantumArchitecture
17
16
  from iqm.qiskit_iqm.fake_backends.iqm_fake_backend import IQMErrorProfile, IQMFakeBackend
18
17
 
18
+ from iqm.station_control.interface.models import StaticQuantumArchitecture
19
+
19
20
 
20
21
  def IQMFakeAdonis() -> IQMFakeBackend:
21
22
  """Return IQMFakeBackend instance representing IQM's Adonis architecture."""
@@ -13,9 +13,10 @@
13
13
  # limitations under the License.
14
14
  """Fake (i.e. simulated) backend for IQM's 54-qubit Aphrodite architecture"""
15
15
 
16
- from iqm.iqm_client import StaticQuantumArchitecture
17
16
  from iqm.qiskit_iqm.fake_backends.iqm_fake_backend import IQMErrorProfile, IQMFakeBackend
18
17
 
18
+ from iqm.station_control.interface.models import StaticQuantumArchitecture
19
+
19
20
 
20
21
  def IQMFakeAphrodite() -> IQMFakeBackend:
21
22
  """Return IQMFakeBackend instance representing IQM's Aphrodite architecture."""
@@ -13,9 +13,10 @@
13
13
  # limitations under the License.
14
14
  """Fake (i.e. simulated) backend for IQM's 20-qubit Apollo architecture"""
15
15
 
16
- from iqm.iqm_client import StaticQuantumArchitecture
17
16
  from iqm.qiskit_iqm.fake_backends.iqm_fake_backend import IQMErrorProfile, IQMFakeBackend
18
17
 
18
+ from iqm.station_control.interface.models import StaticQuantumArchitecture
19
+
19
20
 
20
21
  def IQMFakeApollo() -> IQMFakeBackend:
21
22
  """Return IQMFakeBackend instance representing IQM's Apollo architecture."""
@@ -13,9 +13,10 @@
13
13
  # limitations under the License.
14
14
  """Fake backend for IQM's 6-qubit Deneb architecture."""
15
15
 
16
- from iqm.iqm_client import StaticQuantumArchitecture
17
16
  from iqm.qiskit_iqm.fake_backends.iqm_fake_backend import IQMErrorProfile, IQMFakeBackend
18
17
 
18
+ from iqm.station_control.interface.models import StaticQuantumArchitecture
19
+
19
20
 
20
21
  def IQMFakeDeneb() -> IQMFakeBackend:
21
22
  """Return IQMFakeBackend instance representing IQM's Deneb architecture."""
@@ -20,13 +20,6 @@ from dataclasses import dataclass
20
20
  from itertools import permutations
21
21
  from uuid import UUID
22
22
 
23
- from iqm.iqm_client import (
24
- DynamicQuantumArchitecture,
25
- GateImplementationInfo,
26
- GateInfo,
27
- Locus,
28
- StaticQuantumArchitecture,
29
- )
30
23
  from iqm.qiskit_iqm.iqm_backend import IQM_TO_QISKIT_GATE_NAME, IQMBackendBase
31
24
  from iqm.qiskit_iqm.iqm_circuit_validation import validate_circuit
32
25
  from iqm.qiskit_iqm.iqm_transpilation import IQMReplaceGateWithUnitaryPass
@@ -37,6 +30,14 @@ from qiskit_aer import AerSimulator
37
30
  from qiskit_aer.noise import NoiseModel, QuantumError
38
31
  from qiskit_aer.noise.errors import depolarizing_error, thermal_relaxation_error
39
32
 
33
+ from iqm.station_control.interface.models import (
34
+ DynamicQuantumArchitecture,
35
+ GateImplementationInfo,
36
+ GateInfo,
37
+ Locus,
38
+ StaticQuantumArchitecture,
39
+ )
40
+
40
41
 
41
42
  def _dqa_from_sqa(
42
43
  sqa: StaticQuantumArchitecture,
@@ -19,14 +19,13 @@ from abc import ABC
19
19
  import logging
20
20
  from typing import Final
21
21
 
22
- from iqm.iqm_client import (
23
- DynamicQuantumArchitecture,
24
- ObservationFinder,
25
- )
22
+ from iqm.iqm_client import ObservationFinder
26
23
  from iqm.qiskit_iqm.iqm_target import IQMTarget
27
24
  from qiskit.providers import BackendV2
28
25
  from qiskit.transpiler import Target
29
26
 
27
+ from iqm.station_control.interface.models import DynamicQuantumArchitecture
28
+
30
29
  IQM_TO_QISKIT_GATE_NAME: Final[dict[str, str]] = {"prx": "r", "cz": "cz"}
31
30
  logger = logging.getLogger(__name__)
32
31
 
@@ -13,26 +13,27 @@
13
13
  # limitations under the License.
14
14
  """Helper functions for circuit validation."""
15
15
 
16
- from iqm.iqm_client import Circuit as IQMClientCircuit
17
- from iqm.iqm_client import MoveGateValidationMode
18
16
  from iqm.iqm_client.validation import validate_circuit_instructions
19
17
  from iqm.qiskit_iqm.iqm_backend import IQMBackendBase
20
18
  from iqm.qiskit_iqm.qiskit_to_iqm import serialize_instructions
21
19
  from qiskit import QuantumCircuit
22
20
 
21
+ from iqm.pulse import Circuit
22
+ from iqm.station_control.interface.models import MoveGateValidationMode
23
+
23
24
 
24
25
  def validate_circuit( # noqa: ANN201
25
26
  circuit: QuantumCircuit,
26
27
  backend: IQMBackendBase,
27
28
  validate_moves: MoveGateValidationMode | None = None,
28
- qubit_mapping: dict[int, str] | None = None,
29
+ qubit_index_to_name: dict[int, str] | None = None,
29
30
  ):
30
31
  """Validate a circuit against the backend."""
31
- if qubit_mapping is None:
32
- qubit_mapping = backend._idx_to_qb
33
- new_circuit = IQMClientCircuit(
32
+ if qubit_index_to_name is None:
33
+ qubit_index_to_name = backend._idx_to_qb
34
+ new_circuit = Circuit(
34
35
  name="Validation circuit",
35
- instructions=tuple(serialize_instructions(circuit=circuit, qubit_index_to_name=qubit_mapping)),
36
+ instructions=tuple(serialize_instructions(circuit=circuit, qubit_index_to_name=qubit_index_to_name)),
36
37
  metadata=None,
37
38
  )
38
39
  if validate_moves is None: