qilisdk 0.1.3__py3-none-any.whl → 0.1.5__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.
- qilisdk/__init__.py +11 -2
- qilisdk/__init__.pyi +2 -3
- qilisdk/_logging.py +135 -0
- qilisdk/_optionals.py +5 -7
- qilisdk/analog/__init__.py +3 -18
- qilisdk/analog/exceptions.py +2 -4
- qilisdk/analog/hamiltonian.py +455 -110
- qilisdk/analog/linear_schedule.py +118 -0
- qilisdk/analog/schedule.py +272 -79
- qilisdk/backends/__init__.py +45 -0
- qilisdk/{digital/digital_algorithm.py → backends/__init__.pyi} +3 -5
- qilisdk/backends/backend.py +117 -0
- qilisdk/{extras/cuda → backends}/cuda_backend.py +153 -161
- qilisdk/backends/qutip_backend.py +492 -0
- qilisdk/common/__init__.py +48 -2
- qilisdk/common/algorithm.py +2 -1
- qilisdk/{extras/qaas/qaas_settings.py → common/exceptions.py} +12 -6
- qilisdk/common/model.py +1019 -1
- qilisdk/common/parameterizable.py +75 -0
- qilisdk/common/qtensor.py +666 -0
- qilisdk/common/result.py +2 -1
- qilisdk/common/variables.py +1931 -0
- qilisdk/{extras/cuda/cuda_analog_result.py → cost_functions/__init__.py} +3 -4
- qilisdk/cost_functions/cost_function.py +77 -0
- qilisdk/cost_functions/model_cost_function.py +145 -0
- qilisdk/cost_functions/observable_cost_function.py +109 -0
- qilisdk/digital/__init__.py +3 -22
- qilisdk/digital/ansatz.py +203 -160
- qilisdk/digital/circuit.py +81 -9
- qilisdk/digital/exceptions.py +12 -6
- qilisdk/digital/gates.py +228 -85
- qilisdk/{extras/qaas/qaas_analog_result.py → functionals/__init__.py} +14 -5
- qilisdk/functionals/functional.py +39 -0
- qilisdk/{extras/cuda/cuda_digital_result.py → functionals/functional_result.py} +3 -4
- qilisdk/functionals/sampling.py +81 -0
- qilisdk/functionals/sampling_result.py +92 -0
- qilisdk/functionals/time_evolution.py +98 -0
- qilisdk/functionals/time_evolution_result.py +84 -0
- qilisdk/functionals/variational_program.py +80 -0
- qilisdk/functionals/variational_program_result.py +69 -0
- qilisdk/logging_config.yaml +16 -0
- qilisdk/{common/backend.py → optimizers/__init__.py} +2 -1
- qilisdk/optimizers/optimizer.py +39 -0
- qilisdk/{common → optimizers}/optimizer_result.py +3 -12
- qilisdk/{common/optimizer.py → optimizers/scipy_optimizer.py} +10 -28
- qilisdk/settings.py +78 -0
- qilisdk/{extras → speqtrum}/__init__.py +7 -8
- qilisdk/{extras → speqtrum}/__init__.pyi +3 -3
- qilisdk/speqtrum/experiments/__init__.py +25 -0
- qilisdk/speqtrum/experiments/experiment_functional.py +124 -0
- qilisdk/speqtrum/experiments/experiment_result.py +231 -0
- qilisdk/{extras/qaas → speqtrum}/keyring.py +8 -4
- qilisdk/speqtrum/speqtrum.py +432 -0
- qilisdk/speqtrum/speqtrum_models.py +300 -0
- qilisdk/utils/__init__.py +0 -14
- qilisdk/utils/openqasm2.py +1 -1
- qilisdk/utils/serialization.py +1 -1
- qilisdk/utils/visualization/PlusJakartaSans-SemiBold.ttf +0 -0
- qilisdk/utils/visualization/__init__.py +24 -0
- qilisdk/utils/visualization/circuit_renderers.py +781 -0
- qilisdk/utils/visualization/schedule_renderers.py +161 -0
- qilisdk/utils/visualization/style.py +154 -0
- qilisdk/utils/visualization/themes.py +76 -0
- qilisdk/yaml.py +126 -0
- {qilisdk-0.1.3.dist-info → qilisdk-0.1.5.dist-info}/METADATA +180 -135
- qilisdk-0.1.5.dist-info/RECORD +69 -0
- qilisdk/analog/algorithms.py +0 -111
- qilisdk/analog/analog_backend.py +0 -43
- qilisdk/analog/analog_result.py +0 -114
- qilisdk/analog/quantum_objects.py +0 -533
- qilisdk/digital/digital_backend.py +0 -90
- qilisdk/digital/digital_result.py +0 -145
- qilisdk/digital/vqe.py +0 -166
- qilisdk/extras/cuda/__init__.py +0 -13
- qilisdk/extras/qaas/__init__.py +0 -13
- qilisdk/extras/qaas/models.py +0 -132
- qilisdk/extras/qaas/qaas_backend.py +0 -255
- qilisdk/extras/qaas/qaas_digital_result.py +0 -20
- qilisdk/extras/qaas/qaas_time_evolution_result.py +0 -20
- qilisdk/extras/qaas/qaas_vqe_result.py +0 -20
- qilisdk-0.1.3.dist-info/RECORD +0 -51
- {qilisdk-0.1.3.dist-info → qilisdk-0.1.5.dist-info}/WHEEL +0 -0
- {qilisdk-0.1.3.dist-info → qilisdk-0.1.5.dist-info}/licenses/LICENCE +0 -0
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
# Copyright 2025 Qilimanjaro Quantum Tech
|
|
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
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import base64
|
|
17
|
+
import json
|
|
18
|
+
import time
|
|
19
|
+
from base64 import urlsafe_b64encode
|
|
20
|
+
from datetime import datetime, timezone
|
|
21
|
+
from typing import TYPE_CHECKING, Callable, cast
|
|
22
|
+
|
|
23
|
+
import httpx
|
|
24
|
+
from loguru import logger
|
|
25
|
+
from pydantic import TypeAdapter
|
|
26
|
+
|
|
27
|
+
from qilisdk.functionals import Sampling, TimeEvolution, VariationalProgram
|
|
28
|
+
from qilisdk.settings import get_settings
|
|
29
|
+
from qilisdk.speqtrum.experiments import ExperimentFunctional, RabiExperiment, T1Experiment
|
|
30
|
+
|
|
31
|
+
from .keyring import delete_credentials, load_credentials, store_credentials
|
|
32
|
+
from .speqtrum_models import (
|
|
33
|
+
Device,
|
|
34
|
+
ExecutePayload,
|
|
35
|
+
ExecuteType,
|
|
36
|
+
JobDetail,
|
|
37
|
+
JobId,
|
|
38
|
+
JobInfo,
|
|
39
|
+
JobStatus,
|
|
40
|
+
JobType,
|
|
41
|
+
RabiExperimentPayload,
|
|
42
|
+
SamplingPayload,
|
|
43
|
+
T1ExperimentPayload,
|
|
44
|
+
TimeEvolutionPayload,
|
|
45
|
+
Token,
|
|
46
|
+
VariationalProgramPayload,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
if TYPE_CHECKING:
|
|
50
|
+
from qilisdk.functionals.functional import Functional, PrimitiveFunctional
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class SpeQtrum:
|
|
54
|
+
"""Synchronous client for the Qilimanjaro SpeQtrum API."""
|
|
55
|
+
|
|
56
|
+
def __init__(self) -> None:
|
|
57
|
+
logger.debug("Initializing QaaS client")
|
|
58
|
+
credentials = load_credentials()
|
|
59
|
+
if credentials is None:
|
|
60
|
+
logger.error("No QaaS credentials found. Call `.login()` or set env vars before instantiation.")
|
|
61
|
+
raise RuntimeError("Missing QaaS credentials - invoke SpeQtrum.login() first.")
|
|
62
|
+
self._username, self._token = credentials
|
|
63
|
+
self._handlers: dict[type[Functional], Callable[[Functional, str], int]] = {
|
|
64
|
+
Sampling: lambda f, device: self._submit_sampling(cast("Sampling", f), device),
|
|
65
|
+
TimeEvolution: lambda f, device: self._submit_time_evolution(cast("TimeEvolution", f), device),
|
|
66
|
+
VariationalProgram: lambda f, device: self._submit_variational_program(
|
|
67
|
+
cast("VariationalProgram", f), device
|
|
68
|
+
),
|
|
69
|
+
RabiExperiment: lambda f, device: self._submit_rabi_program(cast("RabiExperiment", f), device),
|
|
70
|
+
T1Experiment: lambda f, device: self._submit_t1_program(cast("T1Experiment", f), device),
|
|
71
|
+
}
|
|
72
|
+
self._settings = get_settings()
|
|
73
|
+
logger.success("QaaS client initialised for user '{}'", self._username)
|
|
74
|
+
|
|
75
|
+
@classmethod
|
|
76
|
+
def _get_headers(cls) -> dict:
|
|
77
|
+
from qilisdk import __version__ # noqa: PLC0415
|
|
78
|
+
|
|
79
|
+
return {"User-Agent": f"qilisdk/{__version__}"}
|
|
80
|
+
|
|
81
|
+
def _get_authorized_headers(self) -> dict:
|
|
82
|
+
return {**self._get_headers(), "Authorization": f"Bearer {self._token.access_token}"}
|
|
83
|
+
|
|
84
|
+
@classmethod
|
|
85
|
+
def login(
|
|
86
|
+
cls,
|
|
87
|
+
username: str | None = None,
|
|
88
|
+
apikey: str | None = None,
|
|
89
|
+
) -> bool:
|
|
90
|
+
"""Authenticate and cache credentials in the system keyring.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
username: SpeQtrum account user name. If ``None``, the value is read
|
|
94
|
+
from the environment.
|
|
95
|
+
apikey: SpeQtrum API key. If ``None``, the value is read from the
|
|
96
|
+
environment.
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
``True`` if authentication succeeds, otherwise ``False``.
|
|
100
|
+
|
|
101
|
+
Note:
|
|
102
|
+
The resulting tokens are stored securely in the OS keyring so that future
|
|
103
|
+
:class:`SpeQtrum` constructions require no explicit credentials.
|
|
104
|
+
"""
|
|
105
|
+
# Use provided parameters or fall back to environment variables via Settings()
|
|
106
|
+
settings = get_settings()
|
|
107
|
+
username = username or settings.speqtrum_username
|
|
108
|
+
apikey = apikey or settings.speqtrum_apikey
|
|
109
|
+
|
|
110
|
+
if not username or not apikey:
|
|
111
|
+
logger.warning("Login called without credentials - aborting")
|
|
112
|
+
return False
|
|
113
|
+
|
|
114
|
+
# Send login request to QaaS
|
|
115
|
+
logger.debug("Attempting login for user '{}'", username)
|
|
116
|
+
try:
|
|
117
|
+
assertion = {
|
|
118
|
+
"username": username,
|
|
119
|
+
"api_key": apikey,
|
|
120
|
+
"audience": settings.speqtrum_audience,
|
|
121
|
+
"iat": int(datetime.now(timezone.utc).timestamp()),
|
|
122
|
+
}
|
|
123
|
+
encoded_assertion = urlsafe_b64encode(json.dumps(assertion, indent=2).encode("utf-8")).decode("utf-8")
|
|
124
|
+
with httpx.Client(timeout=10.0) as client:
|
|
125
|
+
response = client.post(
|
|
126
|
+
settings.speqtrum_api_url + "/authorisation-tokens",
|
|
127
|
+
json={
|
|
128
|
+
"grantType": "urn:ietf:params:oauth:grant-type:jwt-bearer",
|
|
129
|
+
"assertion": encoded_assertion,
|
|
130
|
+
"scope": "user profile",
|
|
131
|
+
},
|
|
132
|
+
headers=cls._get_headers(),
|
|
133
|
+
)
|
|
134
|
+
response.raise_for_status()
|
|
135
|
+
token = Token(**response.json())
|
|
136
|
+
except httpx.HTTPStatusError as exc:
|
|
137
|
+
logger.error("Login failed - server returned {} {}", exc.response.status_code, exc.response.reason_phrase)
|
|
138
|
+
return False
|
|
139
|
+
except httpx.RequestError:
|
|
140
|
+
logger.exception("Network error while logging in to QaaS")
|
|
141
|
+
return False
|
|
142
|
+
|
|
143
|
+
store_credentials(username=username, token=token)
|
|
144
|
+
logger.success("Login successful for user '{}'", username)
|
|
145
|
+
return True
|
|
146
|
+
|
|
147
|
+
@classmethod
|
|
148
|
+
def logout(cls) -> None:
|
|
149
|
+
"""Delete cached credentials from the keyring."""
|
|
150
|
+
delete_credentials()
|
|
151
|
+
logger.info("Cached credentials removed - user logged out")
|
|
152
|
+
|
|
153
|
+
def list_devices(self, where: Callable[[Device], bool] | None = None) -> list[Device]:
|
|
154
|
+
"""Return all visible devices, optionally filtered.
|
|
155
|
+
|
|
156
|
+
Args:
|
|
157
|
+
where: A predicate that retains a device when it evaluates to
|
|
158
|
+
``True``. Pass ``None`` to disable filtering.
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
A list of :class:`~qilisdk.models.Device` objects.
|
|
162
|
+
"""
|
|
163
|
+
logger.debug("Fetching device list from server…")
|
|
164
|
+
with httpx.Client() as client:
|
|
165
|
+
response = client.get(self._settings.speqtrum_api_url + "/devices", headers=self._get_authorized_headers())
|
|
166
|
+
response.raise_for_status()
|
|
167
|
+
|
|
168
|
+
devices = TypeAdapter(list[Device]).validate_python(response.json()["items"])
|
|
169
|
+
|
|
170
|
+
logger.success("{} devices retrieved", len(devices))
|
|
171
|
+
return [d for d in devices if where(d)] if where else devices
|
|
172
|
+
|
|
173
|
+
def list_jobs(self, where: Callable[[JobInfo], bool] | None = None) -> list[JobInfo]:
|
|
174
|
+
"""Return lightweight job summaries.
|
|
175
|
+
|
|
176
|
+
Args:
|
|
177
|
+
where: Optional predicate applied client-side. A
|
|
178
|
+
:class:`~qilisdk.models.JobInfo` remains in the list if the
|
|
179
|
+
predicate returns ``True``. ``None`` disables filtering.
|
|
180
|
+
|
|
181
|
+
Returns:
|
|
182
|
+
A list of :class:`~qilisdk.models.JobInfo` objects.
|
|
183
|
+
"""
|
|
184
|
+
logger.debug("Fetching job list…")
|
|
185
|
+
with httpx.Client() as client:
|
|
186
|
+
response = client.get(self._settings.speqtrum_api_url + "/jobs", headers=self._get_authorized_headers())
|
|
187
|
+
response.raise_for_status()
|
|
188
|
+
|
|
189
|
+
jobs = TypeAdapter(list[JobInfo]).validate_python(response.json()["items"])
|
|
190
|
+
|
|
191
|
+
logger.success("{} jobs retrieved", len(jobs))
|
|
192
|
+
return [j for j in jobs if where(j)] if where else jobs
|
|
193
|
+
|
|
194
|
+
def get_job_details(self, id: int) -> JobDetail:
|
|
195
|
+
"""Fetch the complete record of *id*.
|
|
196
|
+
|
|
197
|
+
Args:
|
|
198
|
+
id: Identifier of the job.
|
|
199
|
+
|
|
200
|
+
Returns:
|
|
201
|
+
A :class:`~qilisdk.models.JobDetail` instance containing payload,
|
|
202
|
+
result, logs and error information.
|
|
203
|
+
"""
|
|
204
|
+
logger.debug("Retrieving job {} details", id)
|
|
205
|
+
with httpx.Client() as client:
|
|
206
|
+
response = client.get(
|
|
207
|
+
f"{self._settings.speqtrum_api_url}/jobs/{id}",
|
|
208
|
+
headers=self._get_authorized_headers(),
|
|
209
|
+
params={
|
|
210
|
+
"payload": True,
|
|
211
|
+
"result": True,
|
|
212
|
+
"logs": True,
|
|
213
|
+
"error_logs": True,
|
|
214
|
+
"error": True,
|
|
215
|
+
},
|
|
216
|
+
)
|
|
217
|
+
response.raise_for_status()
|
|
218
|
+
data = response.json()
|
|
219
|
+
|
|
220
|
+
raw_payload = data["payload"]
|
|
221
|
+
if raw_payload is not None:
|
|
222
|
+
data["payload"] = json.loads(raw_payload)
|
|
223
|
+
|
|
224
|
+
raw_result = data.get("result")
|
|
225
|
+
if raw_result is not None:
|
|
226
|
+
decoded_result: bytes = base64.b64decode(raw_result)
|
|
227
|
+
text_result = decoded_result.decode("utf-8")
|
|
228
|
+
data["result"] = json.loads(text_result)
|
|
229
|
+
|
|
230
|
+
raw_error = data.get("error")
|
|
231
|
+
if raw_error is not None:
|
|
232
|
+
decoded_error: bytes = base64.b64decode(raw_error)
|
|
233
|
+
data["error"] = decoded_error.decode("utf-8")
|
|
234
|
+
|
|
235
|
+
raw_logs = data.get("logs")
|
|
236
|
+
if raw_logs is not None:
|
|
237
|
+
decoded_logs: bytes = base64.b64decode(raw_logs)
|
|
238
|
+
data["logs"] = decoded_logs.decode("utf-8")
|
|
239
|
+
|
|
240
|
+
job_detail = TypeAdapter(JobDetail).validate_python(data)
|
|
241
|
+
logger.debug("Job {} details retrieved (status {})", id, job_detail.status.value)
|
|
242
|
+
return job_detail
|
|
243
|
+
|
|
244
|
+
def wait_for_job(
|
|
245
|
+
self,
|
|
246
|
+
id: int,
|
|
247
|
+
*,
|
|
248
|
+
poll_interval: float = 5.0,
|
|
249
|
+
timeout: float | None = None,
|
|
250
|
+
) -> JobDetail:
|
|
251
|
+
"""Block until *id* reaches a terminal state.
|
|
252
|
+
|
|
253
|
+
Args:
|
|
254
|
+
id: Job identifier.
|
|
255
|
+
poll_interval: Seconds between successive polls. Defaults to ``5``.
|
|
256
|
+
timeout: Maximum wait time in seconds. ``None`` waits indefinitely.
|
|
257
|
+
|
|
258
|
+
Returns:
|
|
259
|
+
Final :class:`~qilisdk.models.JobDetail` snapshot.
|
|
260
|
+
|
|
261
|
+
Raises:
|
|
262
|
+
TimeoutError: If *timeout* elapses before the job finishes.
|
|
263
|
+
"""
|
|
264
|
+
logger.info("Waiting for job {} (poll={}s, timeout={}s)…", id, poll_interval, timeout)
|
|
265
|
+
start_t = time.monotonic()
|
|
266
|
+
terminal_states = {
|
|
267
|
+
JobStatus.COMPLETED,
|
|
268
|
+
JobStatus.ERROR,
|
|
269
|
+
JobStatus.CANCELLED,
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
# poll until we hit a terminal state or timeout
|
|
273
|
+
while True:
|
|
274
|
+
current = self.get_job_details(id)
|
|
275
|
+
|
|
276
|
+
if current.status in terminal_states:
|
|
277
|
+
logger.success("Job {} reached terminal state {}", id, current.status.value)
|
|
278
|
+
return current
|
|
279
|
+
|
|
280
|
+
if timeout is not None and (time.monotonic() - start_t) >= timeout:
|
|
281
|
+
logger.error(
|
|
282
|
+
"Timeout while waiting for job {} after {}s (last status {})", id, timeout, current.status.value
|
|
283
|
+
)
|
|
284
|
+
raise TimeoutError(
|
|
285
|
+
f"Timed out after {timeout}s while waiting for job {id} (last status {current.status.value!r})"
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
logger.debug("Job {} still {}, sleeping {}s", id, current.status.value, poll_interval)
|
|
289
|
+
time.sleep(poll_interval)
|
|
290
|
+
|
|
291
|
+
def submit(self, functional: PrimitiveFunctional | ExperimentFunctional, device: str) -> int:
|
|
292
|
+
"""
|
|
293
|
+
Submit a quantum functional for execution on the selected device.
|
|
294
|
+
|
|
295
|
+
The concrete subclass of
|
|
296
|
+
:class:`~qilisdk.functionals.functional.Functional` provided in
|
|
297
|
+
*functional* determines which private ``_execute_*`` routine is
|
|
298
|
+
invoked. Supported types are:
|
|
299
|
+
|
|
300
|
+
* :class:`~qilisdk.functionals.sampling.Sampling`
|
|
301
|
+
* :class:`~qilisdk.functionals.time_evolution.TimeEvolution`
|
|
302
|
+
|
|
303
|
+
A backend device must be selected beforehand with
|
|
304
|
+
:py:meth:`set_device`.
|
|
305
|
+
|
|
306
|
+
Args:
|
|
307
|
+
functional: A fully configured functional instance (e.g.,
|
|
308
|
+
``Sampling`` or ``TimeEvolution``) that defines the quantum
|
|
309
|
+
workload to be executed.
|
|
310
|
+
device: Device code returned by :py:meth:`list_devices`.
|
|
311
|
+
|
|
312
|
+
Returns:
|
|
313
|
+
int: The numeric identifier of the created job on SpeQtrum.
|
|
314
|
+
|
|
315
|
+
Raises:
|
|
316
|
+
NotImplementedError: If *functional* is not of a supported type.
|
|
317
|
+
"""
|
|
318
|
+
try:
|
|
319
|
+
handler = self._handlers[type(functional)]
|
|
320
|
+
except KeyError as exc:
|
|
321
|
+
logger.error("Unsupported functional type: {}", type(functional).__qualname__)
|
|
322
|
+
raise NotImplementedError(
|
|
323
|
+
f"{type(self).__qualname__} does not support {type(functional).__qualname__}"
|
|
324
|
+
) from exc
|
|
325
|
+
|
|
326
|
+
logger.info("Submitting {}", type(functional).__qualname__)
|
|
327
|
+
job_id = handler(functional, device)
|
|
328
|
+
logger.success("Submission complete - job {}", job_id)
|
|
329
|
+
return job_id
|
|
330
|
+
|
|
331
|
+
def _submit_sampling(self, sampling: Sampling, device: str) -> int:
|
|
332
|
+
payload = ExecutePayload(
|
|
333
|
+
type=ExecuteType.SAMPLING,
|
|
334
|
+
sampling_payload=SamplingPayload(sampling=sampling),
|
|
335
|
+
)
|
|
336
|
+
json = {"device_code": device, "payload": payload.model_dump_json(), "job_type": JobType.DIGITAL, "meta": {}}
|
|
337
|
+
logger.debug("Executing Sampling on device {}", device)
|
|
338
|
+
with httpx.Client() as client:
|
|
339
|
+
response = client.post(
|
|
340
|
+
self._settings.speqtrum_api_url + "/execute",
|
|
341
|
+
headers=self._get_authorized_headers(),
|
|
342
|
+
json=json,
|
|
343
|
+
)
|
|
344
|
+
response.raise_for_status()
|
|
345
|
+
job = JobId(**response.json())
|
|
346
|
+
return job.id
|
|
347
|
+
|
|
348
|
+
def _submit_rabi_program(self, rabi_experiment: RabiExperiment, device: str) -> int:
|
|
349
|
+
payload = ExecutePayload(
|
|
350
|
+
type=ExecuteType.RABI_EXPERIMENT,
|
|
351
|
+
rabi_experiment_payload=RabiExperimentPayload(rabi_experiment=rabi_experiment),
|
|
352
|
+
)
|
|
353
|
+
json = {"device_code": device, "payload": payload.model_dump_json(), "job_type": JobType.PULSE, "meta": {}}
|
|
354
|
+
logger.debug("Executing Rabi experiment on device {}", device)
|
|
355
|
+
with httpx.Client() as client:
|
|
356
|
+
response = client.post(
|
|
357
|
+
self._settings.speqtrum_api_url + "/execute",
|
|
358
|
+
headers=self._get_authorized_headers(),
|
|
359
|
+
json=json,
|
|
360
|
+
)
|
|
361
|
+
response.raise_for_status()
|
|
362
|
+
job = JobId(**response.json())
|
|
363
|
+
logger.info("Rabi experiment job submitted: {}", job.id)
|
|
364
|
+
return job.id
|
|
365
|
+
|
|
366
|
+
def _submit_t1_program(self, t1_experiment: T1Experiment, device: str) -> int:
|
|
367
|
+
payload = ExecutePayload(
|
|
368
|
+
type=ExecuteType.T1_EXPERIMENT,
|
|
369
|
+
t1_experiment_payload=T1ExperimentPayload(t1_experiment=t1_experiment),
|
|
370
|
+
)
|
|
371
|
+
json = {"device_code": device, "payload": payload.model_dump_json(), "job_type": JobType.PULSE, "meta": {}}
|
|
372
|
+
logger.debug("Executing T1 experiment on device {}", device)
|
|
373
|
+
with httpx.Client() as client:
|
|
374
|
+
response = client.post(
|
|
375
|
+
self._settings.speqtrum_api_url + "/execute",
|
|
376
|
+
headers=self._get_authorized_headers(),
|
|
377
|
+
json=json,
|
|
378
|
+
)
|
|
379
|
+
response.raise_for_status()
|
|
380
|
+
job = JobId(**response.json())
|
|
381
|
+
logger.info("T1 experiment job submitted: {}", job.id)
|
|
382
|
+
return job.id
|
|
383
|
+
|
|
384
|
+
def _submit_time_evolution(self, time_evolution: TimeEvolution, device: str) -> int:
|
|
385
|
+
payload = ExecutePayload(
|
|
386
|
+
type=ExecuteType.TIME_EVOLUTION,
|
|
387
|
+
time_evolution_payload=TimeEvolutionPayload(time_evolution=time_evolution),
|
|
388
|
+
)
|
|
389
|
+
json = {"device_code": device, "payload": payload.model_dump_json(), "job_type": JobType.ANALOG, "meta": {}}
|
|
390
|
+
logger.debug("Executing time evolution on device {}", device)
|
|
391
|
+
with httpx.Client() as client:
|
|
392
|
+
response = client.post(
|
|
393
|
+
self._settings.speqtrum_api_url + "/execute",
|
|
394
|
+
headers=self._get_authorized_headers(),
|
|
395
|
+
json=json,
|
|
396
|
+
)
|
|
397
|
+
response.raise_for_status()
|
|
398
|
+
job = JobId(**response.json())
|
|
399
|
+
logger.info("Time evolution job submitted: {}", job.id)
|
|
400
|
+
return job.id
|
|
401
|
+
|
|
402
|
+
def _submit_variational_program(self, variational_program: VariationalProgram, device: str) -> int:
|
|
403
|
+
"""Run a Variational Program on the selected device.
|
|
404
|
+
|
|
405
|
+
Args:
|
|
406
|
+
variational_program (VariationalProgram): Problem definition containing Hamiltonian and ansatz.
|
|
407
|
+
device (str): The SpeQtrum device's code to execute the variational program upon.
|
|
408
|
+
|
|
409
|
+
Returns:
|
|
410
|
+
The numeric identifier of the created job.
|
|
411
|
+
"""
|
|
412
|
+
payload = ExecutePayload(
|
|
413
|
+
type=ExecuteType.VARIATIONAL_PROGRAM,
|
|
414
|
+
variational_program_payload=VariationalProgramPayload(
|
|
415
|
+
variational_program=variational_program,
|
|
416
|
+
),
|
|
417
|
+
)
|
|
418
|
+
json = {
|
|
419
|
+
"device_code": device,
|
|
420
|
+
"payload": payload.model_dump_json(),
|
|
421
|
+
"job_type": JobType.VARIATIONAL,
|
|
422
|
+
"meta": {},
|
|
423
|
+
}
|
|
424
|
+
with httpx.Client() as client:
|
|
425
|
+
response = client.post(
|
|
426
|
+
self._settings.speqtrum_api_url + "/execute",
|
|
427
|
+
headers=self._get_authorized_headers(),
|
|
428
|
+
json=json,
|
|
429
|
+
)
|
|
430
|
+
response.raise_for_status()
|
|
431
|
+
job = JobId(**response.json())
|
|
432
|
+
return job.id
|