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.
- iqm/cirq_iqm/devices/iqm_device_metadata.py +2 -1
- iqm/cirq_iqm/examples/demo_common.py +1 -1
- iqm/cirq_iqm/examples/demo_iqm_execution.py +3 -3
- iqm/cirq_iqm/iqm_sampler.py +47 -29
- iqm/cirq_iqm/serialize.py +1 -1
- iqm/cirq_iqm/transpiler.py +3 -1
- iqm/iqm_client/__init__.py +0 -2
- iqm/iqm_client/errors.py +6 -17
- iqm/iqm_client/iqm_client.py +199 -602
- iqm/iqm_client/models.py +20 -611
- iqm/iqm_client/transpile.py +11 -8
- iqm/iqm_client/validation.py +18 -9
- iqm/iqm_server_client/__init__.py +14 -0
- iqm/iqm_server_client/errors.py +6 -0
- iqm/iqm_server_client/iqm_server_client.py +755 -0
- iqm/iqm_server_client/models.py +179 -0
- iqm/iqm_server_client/py.typed +0 -0
- iqm/qiskit_iqm/__init__.py +8 -0
- iqm/qiskit_iqm/examples/bell_measure.py +2 -2
- iqm/qiskit_iqm/examples/transpile_example.py +9 -4
- iqm/qiskit_iqm/fake_backends/fake_adonis.py +2 -1
- iqm/qiskit_iqm/fake_backends/fake_aphrodite.py +2 -1
- iqm/qiskit_iqm/fake_backends/fake_apollo.py +2 -1
- iqm/qiskit_iqm/fake_backends/fake_deneb.py +2 -1
- iqm/qiskit_iqm/fake_backends/iqm_fake_backend.py +8 -7
- iqm/qiskit_iqm/iqm_backend.py +3 -4
- iqm/qiskit_iqm/iqm_circuit_validation.py +8 -7
- iqm/qiskit_iqm/iqm_job.py +106 -88
- iqm/qiskit_iqm/iqm_move_layout.py +2 -1
- iqm/qiskit_iqm/iqm_naive_move_pass.py +115 -56
- iqm/qiskit_iqm/iqm_provider.py +49 -36
- iqm/qiskit_iqm/iqm_target.py +12 -8
- iqm/qiskit_iqm/iqm_transpilation.py +219 -26
- iqm/qiskit_iqm/qiskit_to_iqm.py +150 -41
- iqm/qiskit_iqm/transpiler_plugins.py +11 -8
- {iqm_client-32.0.0.dist-info → iqm_client-33.0.0.dist-info}/METADATA +4 -14
- iqm_client-33.0.0.dist-info/RECORD +63 -0
- iqm/iqm_client/api.py +0 -90
- iqm/iqm_client/authentication.py +0 -206
- iqm_client-32.0.0.dist-info/RECORD +0 -60
- {iqm_client-32.0.0.dist-info → iqm_client-33.0.0.dist-info}/AUTHORS.rst +0 -0
- {iqm_client-32.0.0.dist-info → iqm_client-33.0.0.dist-info}/LICENSE.txt +0 -0
- {iqm_client-32.0.0.dist-info → iqm_client-33.0.0.dist-info}/WHEEL +0 -0
- {iqm_client-32.0.0.dist-info → iqm_client-33.0.0.dist-info}/entry_points.txt +0 -0
- {iqm_client-32.0.0.dist-info → iqm_client-33.0.0.dist-info}/top_level.txt +0 -0
|
@@ -20,9 +20,10 @@ from collections.abc import Iterable
|
|
|
20
20
|
import cirq
|
|
21
21
|
from cirq import Gate, NamedQid, devices, ops
|
|
22
22
|
from iqm.cirq_iqm.serialize import _IQM_CIRQ_OP_MAP
|
|
23
|
-
from iqm.iqm_client import DynamicQuantumArchitecture
|
|
24
23
|
import networkx as nx
|
|
25
24
|
|
|
25
|
+
from iqm.station_control.interface.models import DynamicQuantumArchitecture
|
|
26
|
+
|
|
26
27
|
|
|
27
28
|
@cirq.value.value_equality
|
|
28
29
|
class IQMDeviceMetadata(devices.DeviceMetadata):
|
|
@@ -13,13 +13,13 @@
|
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
"""Demonstrates executing a quantum circuit on an IQM quantum computer.
|
|
15
15
|
|
|
16
|
-
Set the
|
|
16
|
+
Set the IQM_SERVER_URL environment variable before running this script.
|
|
17
17
|
Also, if the server you are running against requires authentication you will also have to use
|
|
18
18
|
the IQM_TOKENS_FILE or the IQM_TOKEN variable to set the access token.
|
|
19
19
|
|
|
20
20
|
E.g.
|
|
21
21
|
|
|
22
|
-
export
|
|
22
|
+
export IQM_SERVER_URL="https://example.com"
|
|
23
23
|
export IQM_TOKENS_FILE="/path/to/my/tokens.json"
|
|
24
24
|
export IQM_TOKEN="<token here>"
|
|
25
25
|
"""
|
|
@@ -45,7 +45,7 @@ def demo_run_circuit() -> None:
|
|
|
45
45
|
print("Original circuit:\n")
|
|
46
46
|
print(circuit)
|
|
47
47
|
|
|
48
|
-
sampler = IQMSampler(os.environ["
|
|
48
|
+
sampler = IQMSampler(os.environ["IQM_SERVER_URL"])
|
|
49
49
|
|
|
50
50
|
circuit_routed, _, _ = sampler.device.route_circuit(circuit)
|
|
51
51
|
circuit_decomposed = sampler.device.decompose_circuit(circuit_routed)
|
iqm/cirq_iqm/iqm_sampler.py
CHANGED
|
@@ -25,20 +25,29 @@ import warnings
|
|
|
25
25
|
import cirq
|
|
26
26
|
from iqm.cirq_iqm.devices.iqm_device import IQMDevice, IQMDeviceMetadata
|
|
27
27
|
from iqm.cirq_iqm.serialize import serialize_circuit
|
|
28
|
-
from iqm.iqm_client import
|
|
28
|
+
from iqm.iqm_client import APITimeoutError, CircuitCompilationOptions, CircuitExecutionError, IQMClient
|
|
29
|
+
from iqm.iqm_server_client.models import JobStatus
|
|
29
30
|
import numpy as np
|
|
30
31
|
|
|
32
|
+
from exa.common.errors.station_control_errors import StationControlError
|
|
33
|
+
from iqm.station_control.interface.models import CircuitBatch, RunRequest
|
|
34
|
+
|
|
31
35
|
|
|
32
36
|
class IQMSampler(cirq.work.Sampler):
|
|
33
37
|
"""Circuit sampler for executing quantum circuits on IQM quantum computers.
|
|
34
38
|
|
|
35
|
-
IQMSampler connects to a quantum computer through an IQM
|
|
39
|
+
IQMSampler connects to a quantum computer through an IQM Server.
|
|
36
40
|
If the server requires user authentication, you can provide it either using environment
|
|
37
41
|
variables, or as keyword arguments to IQMSampler. The user authentication kwargs are passed
|
|
38
42
|
through to :class:`~iqm.iqm_client.iqm_client.IQMClient` as is, and are documented there.
|
|
39
43
|
|
|
40
44
|
Args:
|
|
41
|
-
url: URL of the IQM
|
|
45
|
+
url: URL of the IQM Server (e.g. "https://resonance.meetiqm.com/").
|
|
46
|
+
quantum_computer: ID or alias of the quantum computer to connect to, if the IQM Server
|
|
47
|
+
instance controls more than one (e.g. "garnet"). ``None`` means connect to the
|
|
48
|
+
default one.
|
|
49
|
+
use_timeslot: Submits the job to the timeslot queue if set to ``True``. If set to ``False``,
|
|
50
|
+
the job is submitted to the normal on-demand queue.
|
|
42
51
|
device: Device to execute the circuits on. If ``None``, the device will be created based
|
|
43
52
|
on the calibration-specific dynamic quantum architecture obtained from
|
|
44
53
|
:class:`~iqm.iqm_client.iqm_client.IQMClient`.
|
|
@@ -54,17 +63,20 @@ class IQMSampler(cirq.work.Sampler):
|
|
|
54
63
|
self,
|
|
55
64
|
url: str,
|
|
56
65
|
*,
|
|
66
|
+
quantum_computer: str | None = None,
|
|
57
67
|
device: IQMDevice | None = None,
|
|
58
68
|
calibration_set_id: UUID | None = None,
|
|
59
|
-
run_sweep_timeout:
|
|
69
|
+
run_sweep_timeout: float | None = None,
|
|
60
70
|
compiler_options: CircuitCompilationOptions | None = None,
|
|
71
|
+
use_timeslot: bool = False,
|
|
61
72
|
**user_auth_args, # contains keyword args token or tokens_file
|
|
62
73
|
):
|
|
63
|
-
self._client = IQMClient(url, **user_auth_args)
|
|
74
|
+
self._client = IQMClient(url, quantum_computer=quantum_computer, **user_auth_args)
|
|
64
75
|
dqa = self._client.get_dynamic_quantum_architecture(calibration_set_id)
|
|
65
76
|
server_device_metadata = IQMDeviceMetadata.from_architecture(dqa)
|
|
66
77
|
self._use_default_calibration_set = calibration_set_id is None
|
|
67
78
|
self._calibration_set_id = dqa.calibration_set_id
|
|
79
|
+
self._use_timeslot = use_timeslot
|
|
68
80
|
if device is None:
|
|
69
81
|
self._device = IQMDevice(server_device_metadata)
|
|
70
82
|
else:
|
|
@@ -85,13 +97,6 @@ class IQMSampler(cirq.work.Sampler):
|
|
|
85
97
|
"""Returns the device used by the sampler."""
|
|
86
98
|
return self._device
|
|
87
99
|
|
|
88
|
-
def close_client(self): # noqa: ANN201
|
|
89
|
-
"""Close IQMClient's session with the user authentication server. Discard the client."""
|
|
90
|
-
if not self._client:
|
|
91
|
-
return
|
|
92
|
-
self._client.close_auth_session()
|
|
93
|
-
self._client = None
|
|
94
|
-
|
|
95
100
|
def run_sweep( # type: ignore[override]
|
|
96
101
|
self, program: cirq.Circuit, params: cirq.Sweepable, repetitions: int = 1
|
|
97
102
|
) -> list[IQMResult]:
|
|
@@ -122,7 +127,6 @@ class IQMSampler(cirq.work.Sampler):
|
|
|
122
127
|
ValueError: circuits are not valid for execution
|
|
123
128
|
CircuitExecutionError: something went wrong on the server
|
|
124
129
|
APITimeoutError: server did not return the results in the allocated time
|
|
125
|
-
RuntimeError: IQM client session has been closed
|
|
126
130
|
|
|
127
131
|
"""
|
|
128
132
|
results, metadata = self._send_circuits(
|
|
@@ -153,9 +157,6 @@ class IQMSampler(cirq.work.Sampler):
|
|
|
153
157
|
|
|
154
158
|
serialized_circuits: CircuitBatch = [serialize_circuit(circuit) for circuit in programs]
|
|
155
159
|
|
|
156
|
-
if not self._client:
|
|
157
|
-
raise RuntimeError("Cannot submit circuits since session to IQM client has been closed.")
|
|
158
|
-
|
|
159
160
|
different_calset_ids = set()
|
|
160
161
|
for circuit in programs:
|
|
161
162
|
if hasattr(circuit, "iqm_calibration_set_id"):
|
|
@@ -192,7 +193,7 @@ class IQMSampler(cirq.work.Sampler):
|
|
|
192
193
|
) -> tuple[list[dict[str, np.ndarray]], ResultMetadata]:
|
|
193
194
|
"""Sends a batch of circuits to be executed and retrieves the results.
|
|
194
195
|
|
|
195
|
-
If a user interrupts the program while it is waiting for results, attempts to
|
|
196
|
+
If a user interrupts the program while it is waiting for results, attempts to cancel the submitted job.
|
|
196
197
|
|
|
197
198
|
Args:
|
|
198
199
|
circuits: quantum circuits to execute
|
|
@@ -201,29 +202,46 @@ class IQMSampler(cirq.work.Sampler):
|
|
|
201
202
|
Returns:
|
|
202
203
|
circuit execution results, result metadata
|
|
203
204
|
|
|
205
|
+
Raises:
|
|
206
|
+
CircuitExecutionError: something went wrong on the server
|
|
207
|
+
APITimeoutError: server did not return the results in the allocated time
|
|
208
|
+
|
|
204
209
|
"""
|
|
205
210
|
run_request = self.create_run_request(circuits, repetitions=repetitions)
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
timeout_arg = [self._run_sweep_timeout] if self._run_sweep_timeout is not None else []
|
|
211
|
+
job = self._client.submit_run_request(run_request, use_timeslot=self._use_timeslot)
|
|
209
212
|
|
|
210
213
|
try:
|
|
211
|
-
|
|
212
|
-
|
|
214
|
+
if self._run_sweep_timeout is not None:
|
|
215
|
+
status = job.wait_for_completion(timeout_secs=self._run_sweep_timeout)
|
|
216
|
+
else:
|
|
217
|
+
status = job.wait_for_completion()
|
|
213
218
|
except KeyboardInterrupt:
|
|
219
|
+
# user pressed Ctrl-C before the job finished
|
|
214
220
|
try:
|
|
215
|
-
|
|
216
|
-
except
|
|
217
|
-
warnings.warn(f"Failed to
|
|
221
|
+
job.cancel()
|
|
222
|
+
except StationControlError as e:
|
|
223
|
+
warnings.warn(f"Failed to cancel job: {e}")
|
|
218
224
|
finally:
|
|
219
225
|
sys.exit()
|
|
220
226
|
|
|
221
|
-
if
|
|
222
|
-
|
|
227
|
+
if status not in JobStatus.terminal_statuses():
|
|
228
|
+
# cancel jobs which did not complete in time
|
|
229
|
+
# FIXME this is bad UX, we should return a job object instead so the user can decide if they want to cancel
|
|
230
|
+
job.cancel()
|
|
231
|
+
raise APITimeoutError(f"Job {job.job_id} didn't finish in the given time. Cancelled.")
|
|
232
|
+
|
|
233
|
+
if status != JobStatus.COMPLETED:
|
|
234
|
+
raise CircuitExecutionError(f"Job {job.job_id} did not finish successfully: {status}: {job._errors}")
|
|
235
|
+
|
|
236
|
+
result = job.result()
|
|
237
|
+
if result is None:
|
|
238
|
+
raise RuntimeError("No measurements received from IQM Server.")
|
|
223
239
|
|
|
224
240
|
return (
|
|
225
|
-
[{k: np.array(v) for k, v in measurements.items()} for measurements in
|
|
226
|
-
ResultMetadata(
|
|
241
|
+
[{k: np.array(v) for k, v in measurements.items()} for measurements in result],
|
|
242
|
+
ResultMetadata(
|
|
243
|
+
job.job_id, job.data.compilation.calibration_set_id if job.data.compilation else None, run_request
|
|
244
|
+
),
|
|
227
245
|
)
|
|
228
246
|
|
|
229
247
|
@staticmethod
|
iqm/cirq_iqm/serialize.py
CHANGED
|
@@ -90,7 +90,7 @@ def circuit_operation_to_operation(circuit_operation: CircuitOperation) -> Opera
|
|
|
90
90
|
|
|
91
91
|
|
|
92
92
|
class OperationNotSupportedError(RuntimeError):
|
|
93
|
-
"""Raised when a given operation is not supported by the IQM
|
|
93
|
+
"""Raised when a given operation is not supported by the IQM Server."""
|
|
94
94
|
|
|
95
95
|
|
|
96
96
|
def operation_to_circuit_operation(operation: Operation) -> CircuitOperation:
|
iqm/cirq_iqm/transpiler.py
CHANGED
|
@@ -21,6 +21,8 @@ from cirq import Circuit
|
|
|
21
21
|
from iqm.cirq_iqm.serialize import deserialize_circuit, serialize_circuit
|
|
22
22
|
from iqm.iqm_client import ExistingMoveHandlingOptions, transpile_insert_moves
|
|
23
23
|
|
|
24
|
+
from iqm.station_control.interface.models import QubitMapping
|
|
25
|
+
|
|
24
26
|
if TYPE_CHECKING:
|
|
25
27
|
from iqm.cirq_iqm.devices import IQMDevice
|
|
26
28
|
|
|
@@ -29,7 +31,7 @@ def transpile_insert_moves_into_circuit(
|
|
|
29
31
|
cirq_circuit: Circuit,
|
|
30
32
|
device: IQMDevice,
|
|
31
33
|
existing_moves: ExistingMoveHandlingOptions = ExistingMoveHandlingOptions.KEEP,
|
|
32
|
-
qubit_mapping:
|
|
34
|
+
qubit_mapping: QubitMapping | None = None,
|
|
33
35
|
) -> Circuit:
|
|
34
36
|
"""Transpile the circuit to insert MOVE gates where needed.
|
|
35
37
|
|
iqm/iqm_client/__init__.py
CHANGED
|
@@ -17,8 +17,6 @@ from importlib.metadata import PackageNotFoundError, version
|
|
|
17
17
|
import sys
|
|
18
18
|
import warnings
|
|
19
19
|
|
|
20
|
-
from iqm.iqm_client.api import * # noqa: F403
|
|
21
|
-
from iqm.iqm_client.authentication import * # noqa: F403
|
|
22
20
|
from iqm.iqm_client.errors import * # noqa: F403
|
|
23
21
|
from iqm.iqm_client.iqm_client import * # noqa: F403
|
|
24
22
|
from iqm.iqm_client.models import * # noqa: F403
|
iqm/iqm_client/errors.py
CHANGED
|
@@ -14,14 +14,6 @@
|
|
|
14
14
|
"""This module contains error classes required by IQMClient."""
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
class ClientAuthenticationError(RuntimeError):
|
|
18
|
-
"""Something went wrong with user authentication."""
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
class ClientConfigurationError(RuntimeError):
|
|
22
|
-
"""Wrong configuration provided."""
|
|
23
|
-
|
|
24
|
-
|
|
25
17
|
class CircuitValidationError(RuntimeError):
|
|
26
18
|
"""Circuit validation failed."""
|
|
27
19
|
|
|
@@ -31,16 +23,13 @@ class CircuitTranspilationError(RuntimeError):
|
|
|
31
23
|
|
|
32
24
|
|
|
33
25
|
class CircuitExecutionError(RuntimeError):
|
|
34
|
-
"""Something went wrong on the server.
|
|
26
|
+
"""Something went wrong on the server.
|
|
27
|
+
|
|
28
|
+
Should be used in sync calls that must return valid circuit execution results.
|
|
29
|
+
Async calls may instead return a job object that indicates the
|
|
30
|
+
excution did not succeed without raising anything.
|
|
31
|
+
"""
|
|
35
32
|
|
|
36
33
|
|
|
37
34
|
class APITimeoutError(CircuitExecutionError):
|
|
38
35
|
"""Executing a job on the server took too long."""
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
class JobAbortionError(RuntimeError):
|
|
42
|
-
"""Job abortion failed."""
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
class EndpointRequestError(RuntimeError):
|
|
46
|
-
"""Retrieving something from a server endpoint failed because we did not understand the response."""
|