classiq 0.59.0__py3-none-any.whl → 0.60.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.
- classiq/_internals/api_wrapper.py +84 -18
- classiq/_internals/client.py +70 -11
- classiq/_internals/jobs.py +44 -35
- classiq/execution/execution_session.py +30 -6
- classiq/execution/jobs.py +64 -23
- classiq/execution/qaoa.py +17 -15
- classiq/execution/qnn.py +17 -18
- classiq/interface/_version.py +1 -1
- {classiq-0.59.0.dist-info → classiq-0.60.1.dist-info}/METADATA +1 -1
- {classiq-0.59.0.dist-info → classiq-0.60.1.dist-info}/RECORD +11 -11
- {classiq-0.59.0.dist-info → classiq-0.60.1.dist-info}/WHEEL +0 -0
@@ -1,6 +1,7 @@
|
|
1
1
|
import json
|
2
2
|
from typing import Any, Optional, Protocol, TypeVar
|
3
3
|
|
4
|
+
import httpx
|
4
5
|
import pydantic
|
5
6
|
|
6
7
|
import classiq.interface.executor.execution_result
|
@@ -70,13 +71,18 @@ class ApiWrapper:
|
|
70
71
|
url: str,
|
71
72
|
model: pydantic.BaseModel,
|
72
73
|
use_versioned_url: bool = True,
|
74
|
+
http_client: Optional[httpx.AsyncClient] = None,
|
73
75
|
) -> dict:
|
74
76
|
# TODO: we can't use model.dict() - it doesn't serialize complex class.
|
75
77
|
# This was added because JSON serializer doesn't serialize complex type, and pydantic does.
|
76
78
|
# We should add support for smarter json serialization.
|
77
79
|
body = json.loads(model.model_dump_json())
|
78
80
|
return await cls._call_task(
|
79
|
-
http_method,
|
81
|
+
http_method,
|
82
|
+
url,
|
83
|
+
body,
|
84
|
+
use_versioned_url=use_versioned_url,
|
85
|
+
http_client=http_client,
|
80
86
|
)
|
81
87
|
|
82
88
|
@classmethod
|
@@ -89,6 +95,7 @@ class ApiWrapper:
|
|
89
95
|
use_versioned_url: bool = True,
|
90
96
|
headers: Optional[dict[str, str]] = None,
|
91
97
|
allow_none: bool = False,
|
98
|
+
http_client: Optional[httpx.AsyncClient] = None,
|
92
99
|
) -> dict:
|
93
100
|
res: Any = await client().call_api(
|
94
101
|
http_method=http_method,
|
@@ -97,6 +104,7 @@ class ApiWrapper:
|
|
97
104
|
headers=headers,
|
98
105
|
params=params,
|
99
106
|
use_versioned_url=use_versioned_url,
|
107
|
+
http_client=http_client,
|
100
108
|
)
|
101
109
|
if allow_none and res is None:
|
102
110
|
return {}
|
@@ -106,25 +114,34 @@ class ApiWrapper:
|
|
106
114
|
|
107
115
|
@classmethod
|
108
116
|
async def call_generation_task(
|
109
|
-
cls,
|
117
|
+
cls,
|
118
|
+
model: Model,
|
119
|
+
http_client: Optional[httpx.AsyncClient] = None,
|
110
120
|
) -> generator_result.QuantumProgram:
|
111
121
|
poller = JobPoller(base_url=routes.TASKS_GENERATE_FULL_PATH)
|
112
|
-
result = await poller.run_pydantic(
|
122
|
+
result = await poller.run_pydantic(
|
123
|
+
model, timeout_sec=None, http_client=http_client
|
124
|
+
)
|
113
125
|
return _parse_job_response(result, generator_result.QuantumProgram)
|
114
126
|
|
115
127
|
@classmethod
|
116
128
|
async def call_convert_quantum_program(
|
117
|
-
cls,
|
129
|
+
cls,
|
130
|
+
circuit: generator_result.QuantumProgram,
|
131
|
+
http_client: Optional[httpx.AsyncClient] = None,
|
118
132
|
) -> dict:
|
119
133
|
return await cls._call_task_pydantic(
|
120
134
|
http_method=HTTPMethod.POST,
|
121
135
|
url=routes.CONVERSION_GENERATED_CIRCUIT_TO_EXECUTION_INPUT_FULL,
|
122
136
|
model=circuit,
|
137
|
+
http_client=http_client,
|
123
138
|
)
|
124
139
|
|
125
140
|
@classmethod
|
126
141
|
async def call_execute_execution_input(
|
127
|
-
cls,
|
142
|
+
cls,
|
143
|
+
execution_input: dict,
|
144
|
+
http_client: Optional[httpx.AsyncClient] = None,
|
128
145
|
) -> execution_request.ExecutionJobDetails:
|
129
146
|
headers = {
|
130
147
|
_ACCEPT_HEADER: "v1",
|
@@ -136,6 +153,7 @@ class ApiWrapper:
|
|
136
153
|
url=routes.EXECUTION_JOBS_NON_VERSIONED_FULL_PATH,
|
137
154
|
body=execution_input,
|
138
155
|
use_versioned_url=False,
|
156
|
+
http_client=http_client,
|
139
157
|
)
|
140
158
|
return execution_request.ExecutionJobDetails.model_validate(data)
|
141
159
|
|
@@ -143,6 +161,7 @@ class ApiWrapper:
|
|
143
161
|
async def call_get_execution_job_details(
|
144
162
|
cls,
|
145
163
|
job_id: JobID,
|
164
|
+
http_client: Optional[httpx.AsyncClient] = None,
|
146
165
|
) -> execution_request.ExecutionJobDetails:
|
147
166
|
headers = {_ACCEPT_HEADER: "v1"}
|
148
167
|
data = await cls._call_task(
|
@@ -150,6 +169,7 @@ class ApiWrapper:
|
|
150
169
|
headers=headers,
|
151
170
|
url=f"{routes.EXECUTION_JOBS_NON_VERSIONED_FULL_PATH}/{job_id.job_id}",
|
152
171
|
use_versioned_url=False,
|
172
|
+
http_client=http_client,
|
153
173
|
)
|
154
174
|
return execution_request.ExecutionJobDetails.model_validate(data)
|
155
175
|
|
@@ -158,12 +178,14 @@ class ApiWrapper:
|
|
158
178
|
cls,
|
159
179
|
job_id: JobID,
|
160
180
|
version: str,
|
181
|
+
http_client: Optional[httpx.AsyncClient] = None,
|
161
182
|
) -> classiq.interface.executor.execution_result.ExecuteGeneratedCircuitResults:
|
162
183
|
data = await cls._call_task(
|
163
184
|
http_method=HTTPMethod.GET,
|
164
185
|
url=f"{routes.EXECUTION_JOBS_NON_VERSIONED_FULL_PATH}/{job_id.job_id}/result",
|
165
186
|
use_versioned_url=False,
|
166
187
|
headers={CLASSIQ_ACCEPT_HEADER: version},
|
188
|
+
http_client=http_client,
|
167
189
|
)
|
168
190
|
return classiq.interface.executor.execution_result.ExecuteGeneratedCircuitResults.model_validate(
|
169
191
|
data
|
@@ -174,6 +196,7 @@ class ApiWrapper:
|
|
174
196
|
cls,
|
175
197
|
job_id: JobID,
|
176
198
|
name: str,
|
199
|
+
http_client: Optional[httpx.AsyncClient] = None,
|
177
200
|
) -> ExecutionJobDetailsV1:
|
178
201
|
data = await cls._call_task(
|
179
202
|
http_method=HTTPMethod.PATCH,
|
@@ -182,6 +205,7 @@ class ApiWrapper:
|
|
182
205
|
"name": name,
|
183
206
|
},
|
184
207
|
use_versioned_url=False,
|
208
|
+
http_client=http_client,
|
185
209
|
)
|
186
210
|
return ExecutionJobDetailsV1.model_validate(data)
|
187
211
|
|
@@ -189,12 +213,14 @@ class ApiWrapper:
|
|
189
213
|
async def call_cancel_execution_job(
|
190
214
|
cls,
|
191
215
|
job_id: JobID,
|
216
|
+
http_client: Optional[httpx.AsyncClient] = None,
|
192
217
|
) -> None:
|
193
218
|
await cls._call_task(
|
194
219
|
http_method=HTTPMethod.PUT,
|
195
220
|
url=f"{routes.EXECUTION_JOBS_NON_VERSIONED_FULL_PATH}/{job_id.job_id}/cancel",
|
196
221
|
use_versioned_url=False,
|
197
222
|
allow_none=True,
|
223
|
+
http_client=http_client,
|
198
224
|
)
|
199
225
|
|
200
226
|
@classmethod
|
@@ -202,6 +228,7 @@ class ApiWrapper:
|
|
202
228
|
cls,
|
203
229
|
offset: int,
|
204
230
|
limit: int,
|
231
|
+
http_client: Optional[httpx.AsyncClient] = None,
|
205
232
|
) -> ExecutionJobsQueryResultsV1:
|
206
233
|
data = await cls._call_task(
|
207
234
|
http_method=HTTPMethod.GET,
|
@@ -211,73 +238,92 @@ class ApiWrapper:
|
|
211
238
|
"limit": limit,
|
212
239
|
},
|
213
240
|
use_versioned_url=False,
|
241
|
+
http_client=http_client,
|
214
242
|
)
|
215
243
|
return ExecutionJobsQueryResultsV1.model_validate(data)
|
216
244
|
|
217
245
|
@classmethod
|
218
246
|
async def call_analysis_task(
|
219
|
-
cls,
|
247
|
+
cls,
|
248
|
+
params: analysis_params.AnalysisParams,
|
249
|
+
http_client: Optional[httpx.AsyncClient] = None,
|
220
250
|
) -> analysis_result.Analysis:
|
221
251
|
data = await cls._call_task_pydantic(
|
222
252
|
http_method=HTTPMethod.POST,
|
223
253
|
url=routes.ANALYZER_FULL_PATH,
|
224
254
|
model=params,
|
255
|
+
http_client=http_client,
|
225
256
|
)
|
226
257
|
|
227
258
|
return analysis_result.Analysis.model_validate(data)
|
228
259
|
|
229
260
|
@classmethod
|
230
261
|
async def call_analyzer_app(
|
231
|
-
cls,
|
262
|
+
cls,
|
263
|
+
params: generator_result.QuantumProgram,
|
264
|
+
http_client: Optional[httpx.AsyncClient] = None,
|
232
265
|
) -> analysis_result.DataID:
|
233
266
|
data = await cls._call_task_pydantic(
|
234
267
|
http_method=HTTPMethod.POST,
|
235
268
|
url=routes.ANALYZER_DATA_FULL_PATH,
|
236
269
|
model=params,
|
270
|
+
http_client=http_client,
|
237
271
|
)
|
238
272
|
return analysis_result.DataID.model_validate(data)
|
239
273
|
|
240
274
|
@classmethod
|
241
275
|
async def get_generated_circuit_from_qasm(
|
242
|
-
cls,
|
276
|
+
cls,
|
277
|
+
params: analysis_result.QasmCode,
|
278
|
+
http_client: Optional[httpx.AsyncClient] = None,
|
243
279
|
) -> generator_result.QuantumProgram:
|
244
280
|
data = await cls._call_task_pydantic(
|
245
281
|
http_method=HTTPMethod.POST,
|
246
282
|
url=routes.IDE_QASM_FULL_PATH,
|
247
283
|
model=params,
|
284
|
+
http_client=http_client,
|
248
285
|
)
|
249
286
|
return generator_result.QuantumProgram.model_validate(data)
|
250
287
|
|
251
288
|
@classmethod
|
252
289
|
async def get_analyzer_app_data(
|
253
|
-
cls,
|
290
|
+
cls,
|
291
|
+
params: analysis_result.DataID,
|
292
|
+
http_client: Optional[httpx.AsyncClient] = None,
|
254
293
|
) -> generator_result.QuantumProgram:
|
255
294
|
data = await cls._call_task(
|
256
295
|
http_method=HTTPMethod.GET,
|
257
296
|
url=f"{routes.ANALYZER_DATA_FULL_PATH}/{params.id}",
|
297
|
+
http_client=http_client,
|
258
298
|
)
|
259
299
|
return generator_result.QuantumProgram.model_validate(data)
|
260
300
|
|
261
301
|
@classmethod
|
262
302
|
async def call_rb_analysis_task(
|
263
|
-
cls,
|
303
|
+
cls,
|
304
|
+
params: AnalysisRBParams,
|
305
|
+
http_client: Optional[httpx.AsyncClient] = None,
|
264
306
|
) -> analysis_result.RbResults:
|
265
307
|
data = await cls._call_task(
|
266
308
|
http_method=HTTPMethod.POST,
|
267
309
|
url=routes.ANALYZER_RB_FULL_PATH,
|
268
310
|
body=params.model_dump(),
|
311
|
+
http_client=http_client,
|
269
312
|
)
|
270
313
|
|
271
314
|
return analysis_result.RbResults.model_validate(data)
|
272
315
|
|
273
316
|
@classmethod
|
274
317
|
async def call_hardware_connectivity_task(
|
275
|
-
cls,
|
318
|
+
cls,
|
319
|
+
params: analysis_params.AnalysisHardwareParams,
|
320
|
+
http_client: Optional[httpx.AsyncClient] = None,
|
276
321
|
) -> analysis_result.GraphResult:
|
277
322
|
data = await cls._call_task_pydantic(
|
278
323
|
http_method=HTTPMethod.POST,
|
279
324
|
url=routes.ANALYZER_HC_GRAPH_FULL_PATH,
|
280
325
|
model=params,
|
326
|
+
http_client=http_client,
|
281
327
|
)
|
282
328
|
return analysis_result.GraphResult.model_validate(data)
|
283
329
|
|
@@ -285,17 +331,21 @@ class ApiWrapper:
|
|
285
331
|
async def call_table_graphs_task(
|
286
332
|
cls,
|
287
333
|
params: analysis_params.AnalysisHardwareListParams,
|
334
|
+
http_client: Optional[httpx.AsyncClient] = None,
|
288
335
|
) -> analysis_result.GraphResult:
|
289
336
|
poller = JobPoller(base_url=routes.ANALYZER_HC_TABLE_GRAPH_FULL_PATH)
|
290
|
-
result = await poller.run_pydantic(
|
337
|
+
result = await poller.run_pydantic(
|
338
|
+
params, timeout_sec=None, http_client=http_client
|
339
|
+
)
|
291
340
|
return _parse_job_response(result, analysis_result.GraphResult)
|
292
341
|
|
293
342
|
@classmethod
|
294
343
|
async def call_available_devices_task(
|
295
344
|
cls,
|
296
345
|
params: analysis_params.AnalysisOptionalDevicesParams,
|
346
|
+
http_client: Optional[httpx.AsyncClient] = None,
|
297
347
|
) -> analysis_result.DevicesResult:
|
298
|
-
hardware_info = await cls.call_get_all_hardware_devices()
|
348
|
+
hardware_info = await cls.call_get_all_hardware_devices(http_client=http_client)
|
299
349
|
return cls._get_devices_from_hardware_info(hardware_info, params)
|
300
350
|
|
301
351
|
@staticmethod
|
@@ -323,11 +373,15 @@ class ApiWrapper:
|
|
323
373
|
)
|
324
374
|
|
325
375
|
@classmethod
|
326
|
-
async def call_get_all_hardware_devices(
|
376
|
+
async def call_get_all_hardware_devices(
|
377
|
+
cls,
|
378
|
+
http_client: Optional[httpx.AsyncClient] = None,
|
379
|
+
) -> list[HardwareInformation]:
|
327
380
|
data = await client().call_api(
|
328
381
|
http_method=HTTPMethod.GET,
|
329
382
|
url="/hardware-catalog/v1/hardwares",
|
330
383
|
use_versioned_url=False,
|
384
|
+
http_client=http_client,
|
331
385
|
)
|
332
386
|
if not isinstance(data, list):
|
333
387
|
raise ClassiqAPIError(f"Unexpected value: {data}")
|
@@ -335,33 +389,45 @@ class ApiWrapper:
|
|
335
389
|
|
336
390
|
@classmethod
|
337
391
|
async def call_generate_hamiltonian_task(
|
338
|
-
cls,
|
392
|
+
cls,
|
393
|
+
problem: ground_state_problem.CHEMISTRY_PROBLEMS_TYPE,
|
394
|
+
http_client: Optional[httpx.AsyncClient] = None,
|
339
395
|
) -> operator.PauliOperator:
|
340
396
|
poller = JobPoller(
|
341
397
|
base_url=routes.GENERATE_HAMILTONIAN_FULL_PATH,
|
342
398
|
use_versioned_url=False,
|
343
399
|
)
|
344
|
-
result = await poller.run_pydantic(
|
400
|
+
result = await poller.run_pydantic(
|
401
|
+
problem, timeout_sec=None, http_client=http_client
|
402
|
+
)
|
345
403
|
return _parse_job_response(result, operator.PauliOperator)
|
346
404
|
|
347
405
|
@classmethod
|
348
|
-
async def call_iqcc_init_auth(
|
406
|
+
async def call_iqcc_init_auth(
|
407
|
+
cls,
|
408
|
+
data: IQCCInitAuthData,
|
409
|
+
http_client: Optional[httpx.AsyncClient] = None,
|
410
|
+
) -> IQCCInitAuthResponse:
|
349
411
|
response = await cls._call_task_pydantic(
|
350
412
|
http_method=HTTPMethod.PUT,
|
351
413
|
url=f"{routes.IQCC_INIT_AUTH_FULL_PATH}",
|
352
414
|
model=data,
|
415
|
+
http_client=http_client,
|
353
416
|
)
|
354
417
|
return IQCCInitAuthResponse.model_validate(response)
|
355
418
|
|
356
419
|
@classmethod
|
357
420
|
async def call_iqcc_probe_auth(
|
358
|
-
cls,
|
421
|
+
cls,
|
422
|
+
data: IQCCProbeAuthData,
|
423
|
+
http_client: Optional[httpx.AsyncClient] = None,
|
359
424
|
) -> Optional[IQCCProbeAuthResponse]:
|
360
425
|
try:
|
361
426
|
response = await cls._call_task_pydantic(
|
362
427
|
http_method=HTTPMethod.PUT,
|
363
428
|
url=f"{routes.IQCC_PROBE_AUTH_FULL_PATH}",
|
364
429
|
model=data,
|
430
|
+
http_client=http_client,
|
365
431
|
)
|
366
432
|
except ClassiqAPIError as ex:
|
367
433
|
if ex.status_code == 418:
|
classiq/_internals/client.py
CHANGED
@@ -1,12 +1,14 @@
|
|
1
1
|
import asyncio
|
2
|
+
import contextlib
|
2
3
|
import functools
|
3
4
|
import inspect
|
4
5
|
import logging
|
5
6
|
import os
|
6
7
|
import platform
|
7
|
-
import ssl
|
8
8
|
import sys
|
9
|
+
import time
|
9
10
|
from collections.abc import Awaitable
|
11
|
+
from types import TracebackType
|
10
12
|
from typing import (
|
11
13
|
Any,
|
12
14
|
Callable,
|
@@ -143,19 +145,37 @@ def try_again_on_failure(
|
|
143
145
|
return wrapper
|
144
146
|
|
145
147
|
|
148
|
+
class _AsyncNullContext(contextlib.AbstractAsyncContextManager):
|
149
|
+
"""
|
150
|
+
This class is meant to replace `contextlib.nullcontext`, which hadn't supported
|
151
|
+
async context manager until python 3.10.
|
152
|
+
"""
|
153
|
+
|
154
|
+
def __init__(self, enter_result: Any = None) -> None:
|
155
|
+
self._enter_result = enter_result
|
156
|
+
|
157
|
+
async def __aenter__(self) -> Any:
|
158
|
+
return self._enter_result
|
159
|
+
|
160
|
+
async def __aexit__(
|
161
|
+
self,
|
162
|
+
exc_type: Optional[type[BaseException]],
|
163
|
+
exc_val: Optional[BaseException],
|
164
|
+
exc_tb: Optional[TracebackType],
|
165
|
+
) -> None:
|
166
|
+
pass
|
167
|
+
|
168
|
+
|
146
169
|
class Client:
|
147
170
|
_UNKNOWN_VERSION = HostChecker._UNKNOWN_VERSION
|
148
171
|
_SESSION_HEADER = "Classiq-Session"
|
149
172
|
_WARNINGS_HEADER = "X-Classiq-Warnings"
|
150
173
|
_LATEST_VERSION_API_PREFIX = "/api/v1"
|
174
|
+
_HTTP_TIMEOUT_SECONDS = 3600 # Needs to be synced with load-balancer timeout
|
151
175
|
|
152
176
|
def __init__(self, conf: config.Configuration) -> None:
|
153
177
|
self._config = conf
|
154
178
|
self._token_manager = token_manager.TokenManager(config=self._config)
|
155
|
-
self._ssl_context = ssl.create_default_context()
|
156
|
-
self._HTTP_TIMEOUT_SECONDS = (
|
157
|
-
3600 # Needs to be synced with load-balancer timeout
|
158
|
-
)
|
159
179
|
self._api_prefix = self._make_api_prefix()
|
160
180
|
self._session_id: Optional[str] = None
|
161
181
|
|
@@ -203,14 +223,44 @@ class Client:
|
|
203
223
|
pass
|
204
224
|
raise ClassiqAPIError(message, response.status_code)
|
205
225
|
|
226
|
+
@try_again_on_failure
|
227
|
+
async def request(
|
228
|
+
self,
|
229
|
+
http_client: httpx.AsyncClient,
|
230
|
+
method: str,
|
231
|
+
url: str,
|
232
|
+
json: Optional[dict] = None,
|
233
|
+
params: Optional[dict] = None,
|
234
|
+
headers: Optional[dict[str, str]] = None,
|
235
|
+
) -> httpx.Response:
|
236
|
+
http_client.headers.update(self._get_headers())
|
237
|
+
|
238
|
+
_logger.debug("HTTP request: %s %s", method.upper(), url)
|
239
|
+
start_time = time.monotonic()
|
240
|
+
response = await http_client.request(
|
241
|
+
method=method,
|
242
|
+
url=url,
|
243
|
+
json=json,
|
244
|
+
params=params,
|
245
|
+
headers=headers,
|
246
|
+
)
|
247
|
+
_logger.debug(
|
248
|
+
"HTTP response: %s %s %d (%.0fms)",
|
249
|
+
method.upper(),
|
250
|
+
url,
|
251
|
+
response.status_code,
|
252
|
+
(time.monotonic() - start_time) * 1000,
|
253
|
+
)
|
254
|
+
self.handle_response(response)
|
255
|
+
return response
|
256
|
+
|
206
257
|
def _make_client_args(self) -> dict[str, Any]:
|
207
258
|
return {
|
208
259
|
"base_url": str(self._config.host),
|
209
260
|
"timeout": self._HTTP_TIMEOUT_SECONDS,
|
210
|
-
"headers": self.
|
261
|
+
"headers": self._get_headers(),
|
211
262
|
}
|
212
263
|
|
213
|
-
@try_again_on_failure
|
214
264
|
async def call_api(
|
215
265
|
self,
|
216
266
|
http_method: str,
|
@@ -219,20 +269,29 @@ class Client:
|
|
219
269
|
params: Optional[dict] = None,
|
220
270
|
use_versioned_url: bool = True,
|
221
271
|
headers: Optional[dict[str, str]] = None,
|
272
|
+
http_client: Optional[httpx.AsyncClient] = None,
|
222
273
|
) -> Union[dict, list, str]:
|
223
274
|
if use_versioned_url:
|
224
275
|
url = self.make_versioned_url(url)
|
225
|
-
async with self.
|
226
|
-
response = await
|
276
|
+
async with self.use_client_or_create(http_client) as async_client:
|
277
|
+
response = await self.request(
|
278
|
+
http_client=async_client,
|
227
279
|
method=http_method,
|
228
280
|
url=url,
|
229
281
|
json=body,
|
230
282
|
params=params,
|
231
283
|
headers=headers,
|
232
284
|
)
|
233
|
-
self.handle_response(response)
|
234
285
|
return response.json()
|
235
286
|
|
287
|
+
def use_client_or_create(
|
288
|
+
self, http_client: Optional[httpx.AsyncClient]
|
289
|
+
) -> contextlib.AbstractAsyncContextManager[httpx.AsyncClient]:
|
290
|
+
if http_client is None:
|
291
|
+
return self.async_client()
|
292
|
+
else:
|
293
|
+
return _AsyncNullContext(enter_result=http_client)
|
294
|
+
|
236
295
|
def sync_call_api(
|
237
296
|
self,
|
238
297
|
http_method: str,
|
@@ -253,7 +312,7 @@ class Client:
|
|
253
312
|
def async_client(self) -> httpx.AsyncClient:
|
254
313
|
return httpx.AsyncClient(**self._make_client_args())
|
255
314
|
|
256
|
-
def
|
315
|
+
def _get_headers(self) -> Headers:
|
257
316
|
headers = dict()
|
258
317
|
access_token = self._token_manager.get_access_token()
|
259
318
|
if access_token is not None:
|
classiq/_internals/jobs.py
CHANGED
@@ -10,7 +10,7 @@ from classiq.interface.exceptions import ClassiqAPIError
|
|
10
10
|
from classiq.interface.jobs import JobDescription, JobID, JSONObject
|
11
11
|
|
12
12
|
from classiq._internals.async_utils import poll_for
|
13
|
-
from classiq._internals.client import client
|
13
|
+
from classiq._internals.client import client
|
14
14
|
from classiq._internals.config import SDKMode
|
15
15
|
|
16
16
|
_URL_PATH_SEP = "/"
|
@@ -53,11 +53,9 @@ class JobPoller:
|
|
53
53
|
def __init__(
|
54
54
|
self,
|
55
55
|
base_url: str,
|
56
|
-
required_headers: Optional[set[str]] = None,
|
57
56
|
use_versioned_url: bool = True,
|
58
57
|
additional_headers: Optional[dict[str, str]] = None,
|
59
58
|
) -> None:
|
60
|
-
self._required_headers = required_headers or set()
|
61
59
|
self._additional_headers = additional_headers
|
62
60
|
client_instance = client()
|
63
61
|
self._base_url = (
|
@@ -65,7 +63,6 @@ class JobPoller:
|
|
65
63
|
if use_versioned_url
|
66
64
|
else base_url
|
67
65
|
)
|
68
|
-
self._async_client = client_instance.async_client()
|
69
66
|
self._mode = client_instance.config.mode
|
70
67
|
|
71
68
|
def _parse_job_id_response(self, response: httpx.Response) -> JobID:
|
@@ -78,29 +75,27 @@ class JobPoller:
|
|
78
75
|
def _make_cancel_url(poll_url: str) -> str:
|
79
76
|
return _join_url_path(poll_url, "cancel")
|
80
77
|
|
81
|
-
def _update_headers(self, response: httpx.Response) -> None:
|
82
|
-
for header in self._required_headers:
|
83
|
-
try:
|
84
|
-
self._async_client.headers[header] = response.headers[header]
|
85
|
-
except KeyError as exc:
|
86
|
-
raise ClassiqAPIError(
|
87
|
-
f"Response to {self._base_url} is missing header {header}"
|
88
|
-
) from exc
|
89
|
-
|
90
|
-
@try_again_on_failure
|
91
78
|
async def _request(
|
92
|
-
self,
|
79
|
+
self,
|
80
|
+
http_client: httpx.AsyncClient,
|
81
|
+
http_method: str,
|
82
|
+
url: str,
|
83
|
+
body: Optional[dict] = None,
|
93
84
|
) -> httpx.Response:
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
85
|
+
return await client().request(
|
86
|
+
http_client=http_client,
|
87
|
+
method=http_method,
|
88
|
+
url=url,
|
89
|
+
json=body,
|
90
|
+
headers=self._additional_headers,
|
98
91
|
)
|
99
|
-
client().handle_response(response)
|
100
|
-
return response
|
101
92
|
|
102
|
-
async def _submit(
|
103
|
-
|
93
|
+
async def _submit(
|
94
|
+
self, http_client: httpx.AsyncClient, body: dict
|
95
|
+
) -> httpx.Response:
|
96
|
+
return await self._request(
|
97
|
+
http_client=http_client, http_method="POST", url=self._base_url, body=body
|
98
|
+
)
|
104
99
|
|
105
100
|
def _interval_sec(self) -> Iterable[float]:
|
106
101
|
if self._mode == SDKMode.DEV:
|
@@ -114,13 +109,16 @@ class JobPoller:
|
|
114
109
|
|
115
110
|
async def _poll(
|
116
111
|
self,
|
112
|
+
http_client: httpx.AsyncClient,
|
117
113
|
poll_url: str,
|
118
114
|
timeout_sec: Optional[float],
|
119
115
|
response_parser: Callable[[JSONObject], Optional[T]] = _general_job_description_parser, # type: ignore[assignment]
|
120
116
|
) -> T:
|
121
117
|
async def poller() -> JSONObject:
|
122
118
|
nonlocal self, poll_url
|
123
|
-
raw_response = await self._request(
|
119
|
+
raw_response = await self._request(
|
120
|
+
http_client=http_client, http_method="GET", url=poll_url
|
121
|
+
)
|
124
122
|
return raw_response.json()
|
125
123
|
|
126
124
|
async for json_response in poll_for(
|
@@ -136,39 +134,50 @@ class JobPoller:
|
|
136
134
|
job_id: JobID,
|
137
135
|
timeout_sec: Optional[float],
|
138
136
|
response_parser: Callable[[JSONObject], Optional[T]] = _general_job_description_parser, # type: ignore[assignment]
|
137
|
+
http_client: Optional[httpx.AsyncClient] = None,
|
139
138
|
) -> T:
|
140
139
|
poll_url = self._make_poll_url(job_id=job_id)
|
141
|
-
async with
|
140
|
+
async with client().use_client_or_create(http_client) as async_client:
|
142
141
|
return await self._poll(
|
142
|
+
http_client=async_client,
|
143
143
|
poll_url=poll_url,
|
144
144
|
response_parser=response_parser,
|
145
145
|
timeout_sec=timeout_sec,
|
146
146
|
)
|
147
147
|
|
148
|
-
async def _cancel(self, poll_url: str) -> None:
|
148
|
+
async def _cancel(self, http_client: httpx.AsyncClient, poll_url: str) -> None:
|
149
149
|
_logger.info("Cancelling job %s", poll_url, exc_info=True)
|
150
150
|
cancel_url = self._make_cancel_url(poll_url)
|
151
|
-
await self._request(http_method="PUT", url=cancel_url)
|
151
|
+
await self._request(http_client=http_client, http_method="PUT", url=cancel_url)
|
152
152
|
|
153
153
|
async def run(
|
154
|
-
self,
|
154
|
+
self,
|
155
|
+
body: dict,
|
156
|
+
timeout_sec: Optional[float],
|
157
|
+
http_client: Optional[httpx.AsyncClient] = None,
|
155
158
|
) -> GeneralJobDescription:
|
156
|
-
async with
|
157
|
-
submit_response = await self._submit(body=body)
|
159
|
+
async with client().use_client_or_create(http_client) as async_client:
|
160
|
+
submit_response = await self._submit(http_client=async_client, body=body)
|
158
161
|
job_id = self._parse_job_id_response(response=submit_response)
|
159
162
|
poll_url = self._make_poll_url(job_id=job_id)
|
160
|
-
self._update_headers(response=submit_response)
|
161
163
|
try:
|
162
|
-
return await self._poll(
|
164
|
+
return await self._poll(
|
165
|
+
http_client=async_client,
|
166
|
+
poll_url=poll_url,
|
167
|
+
timeout_sec=timeout_sec,
|
168
|
+
)
|
163
169
|
except Exception:
|
164
|
-
await self._cancel(poll_url=poll_url)
|
170
|
+
await self._cancel(http_client=async_client, poll_url=poll_url)
|
165
171
|
raise
|
166
172
|
|
167
173
|
async def run_pydantic(
|
168
|
-
self,
|
174
|
+
self,
|
175
|
+
model: pydantic.BaseModel,
|
176
|
+
timeout_sec: Optional[float],
|
177
|
+
http_client: Optional[httpx.AsyncClient] = None,
|
169
178
|
) -> GeneralJobDescription:
|
170
179
|
# TODO: we can't use model.dict() - it doesn't serialize complex class.
|
171
180
|
# This was added because JSON serializer doesn't serialize complex and UUID,
|
172
181
|
# while pydantic does. We should add support for smarter json serialization.
|
173
182
|
body = json.loads(model.model_dump_json())
|
174
|
-
return await self.run(body, timeout_sec)
|
183
|
+
return await self.run(body, timeout_sec, http_client=http_client)
|
@@ -1,5 +1,6 @@
|
|
1
1
|
import json
|
2
2
|
from functools import cached_property
|
3
|
+
from types import TracebackType
|
3
4
|
from typing import Any, Callable, Optional, Union, cast
|
4
5
|
|
5
6
|
import numpy as np
|
@@ -18,6 +19,7 @@ from classiq.interface.generator.quantum_program import QuantumProgram
|
|
18
19
|
|
19
20
|
from classiq._internals import async_utils
|
20
21
|
from classiq._internals.api_wrapper import ApiWrapper
|
22
|
+
from classiq._internals.client import client
|
21
23
|
from classiq.applications.combinatorial_helpers.pauli_helpers.pauli_utils import (
|
22
24
|
_pauli_dict_to_pauli_terms,
|
23
25
|
_pauli_terms_to_qmod,
|
@@ -147,6 +149,7 @@ class ExecutionSession:
|
|
147
149
|
"""
|
148
150
|
A session for executing a quantum program.
|
149
151
|
`ExecutionSession` allows to execute the quantum program with different parameters and operations without the need to re-synthesize the model.
|
152
|
+
The session must be closed in order to ensure resources are properly cleaned up. It's recommended to use `ExecutionSession` as a context manager for this purpose. Alternatively, you can directly use the `close` method.
|
150
153
|
|
151
154
|
Attributes:
|
152
155
|
quantum_program (Union[SerializedQuantumProgram, QuantumProgram]): The quantum program to execute.
|
@@ -165,9 +168,30 @@ class ExecutionSession:
|
|
165
168
|
# because cmain is expected in some cases
|
166
169
|
self.program.model.classical_execution_code = "dummy"
|
167
170
|
|
171
|
+
self._async_client = client().async_client()
|
172
|
+
|
173
|
+
def __enter__(self) -> "ExecutionSession":
|
174
|
+
return self
|
175
|
+
|
176
|
+
def __exit__(
|
177
|
+
self,
|
178
|
+
exc_type: Optional[type[BaseException]],
|
179
|
+
exc_val: Optional[BaseException],
|
180
|
+
exc_tb: Optional[TracebackType],
|
181
|
+
) -> None:
|
182
|
+
self.close()
|
183
|
+
|
184
|
+
def close(self) -> None:
|
185
|
+
"""
|
186
|
+
Close the session and clean up its resources.
|
187
|
+
"""
|
188
|
+
async_utils.run(self._async_client.aclose())
|
189
|
+
|
168
190
|
@cached_property
|
169
191
|
def _execution_input(self) -> dict:
|
170
|
-
return async_utils.run(
|
192
|
+
return async_utils.run(
|
193
|
+
ApiWrapper.call_convert_quantum_program(self.program, self._async_client)
|
194
|
+
)
|
171
195
|
|
172
196
|
def _execute(
|
173
197
|
self, classical_execution_code: str, primitives_input: PrimitivesInput
|
@@ -179,7 +203,7 @@ class ExecutionSession:
|
|
179
203
|
primitives_input.model_dump_json()
|
180
204
|
)
|
181
205
|
result = async_utils.run(
|
182
|
-
ApiWrapper.call_execute_execution_input(execution_input)
|
206
|
+
ApiWrapper.call_execute_execution_input(execution_input, self._async_client)
|
183
207
|
)
|
184
208
|
return ExecutionJob(details=result)
|
185
209
|
|
@@ -209,7 +233,7 @@ class ExecutionSession:
|
|
209
233
|
The result of the sampling.
|
210
234
|
"""
|
211
235
|
job = self.submit_sample(parameters=parameters)
|
212
|
-
return job.get_sample_result()
|
236
|
+
return job.get_sample_result(_http_client=self._async_client)
|
213
237
|
|
214
238
|
def submit_sample(
|
215
239
|
self, parameters: Optional[ExecutionParams] = None
|
@@ -245,7 +269,7 @@ class ExecutionSession:
|
|
245
269
|
List[ExecutionDetails]: The results of all the sampling iterations.
|
246
270
|
"""
|
247
271
|
job = self.submit_batch_sample(parameters=parameters)
|
248
|
-
return job.get_batch_sample_result()
|
272
|
+
return job.get_batch_sample_result(_http_client=self._async_client)
|
249
273
|
|
250
274
|
def submit_batch_sample(self, parameters: list[ExecutionParams]) -> ExecutionJob:
|
251
275
|
"""
|
@@ -282,7 +306,7 @@ class ExecutionSession:
|
|
282
306
|
EstimationResult: The result of the estimation.
|
283
307
|
"""
|
284
308
|
job = self.submit_estimate(hamiltonian=hamiltonian, parameters=parameters)
|
285
|
-
return job.get_estimate_result()
|
309
|
+
return job.get_estimate_result(_http_client=self._async_client)
|
286
310
|
|
287
311
|
def submit_estimate(
|
288
312
|
self, hamiltonian: Hamiltonian, parameters: Optional[ExecutionParams] = None
|
@@ -329,7 +353,7 @@ class ExecutionSession:
|
|
329
353
|
List[EstimationResult]: The results of all the estimation iterations.
|
330
354
|
"""
|
331
355
|
job = self.submit_batch_estimate(hamiltonian=hamiltonian, parameters=parameters)
|
332
|
-
return job.get_batch_estimate_result()
|
356
|
+
return job.get_batch_estimate_result(_http_client=self._async_client)
|
333
357
|
|
334
358
|
def submit_batch_estimate(
|
335
359
|
self, hamiltonian: Hamiltonian, parameters: list[ExecutionParams]
|
classiq/execution/jobs.py
CHANGED
@@ -3,6 +3,8 @@ from datetime import datetime
|
|
3
3
|
from typing import Any, Optional, Union
|
4
4
|
from urllib.parse import urljoin
|
5
5
|
|
6
|
+
import httpx
|
7
|
+
|
6
8
|
from classiq.interface.exceptions import (
|
7
9
|
ClassiqAPIError,
|
8
10
|
ClassiqError,
|
@@ -100,22 +102,28 @@ class ExecutionJob:
|
|
100
102
|
return f"{self._details.cost.total_cost} {self._details.cost.currency_code}"
|
101
103
|
|
102
104
|
@classmethod
|
103
|
-
async def from_id_async(
|
104
|
-
|
105
|
+
async def from_id_async(
|
106
|
+
cls,
|
107
|
+
id: str,
|
108
|
+
_http_client: Optional[httpx.AsyncClient] = None,
|
109
|
+
) -> "ExecutionJob":
|
110
|
+
details = await ApiWrapper.call_get_execution_job_details(
|
111
|
+
JobID(job_id=id), http_client=_http_client
|
112
|
+
)
|
105
113
|
return cls(details)
|
106
114
|
|
107
|
-
|
108
|
-
def from_id(cls, id: str) -> "ExecutionJob":
|
109
|
-
return syncify_function(cls.from_id_async)(id)
|
115
|
+
from_id = syncify_function(from_id_async)
|
110
116
|
|
111
117
|
@property
|
112
118
|
def _job_id(self) -> JobID:
|
113
119
|
return JobID(job_id=self.id)
|
114
120
|
|
115
121
|
async def result_async(
|
116
|
-
self,
|
122
|
+
self,
|
123
|
+
timeout_sec: Optional[float] = None,
|
124
|
+
_http_client: Optional[httpx.AsyncClient] = None,
|
117
125
|
) -> ResultsCollection:
|
118
|
-
await self.poll_async(timeout_sec=timeout_sec)
|
126
|
+
await self.poll_async(timeout_sec=timeout_sec, _http_client=_http_client)
|
119
127
|
|
120
128
|
if self.status == JobStatus.FAILED:
|
121
129
|
raise ClassiqAPIError(self.error or "")
|
@@ -125,7 +133,9 @@ class ExecutionJob:
|
|
125
133
|
if self._result is None:
|
126
134
|
self._result = (
|
127
135
|
await ApiWrapper.call_get_execution_job_result(
|
128
|
-
job_id=self._job_id,
|
136
|
+
job_id=self._job_id,
|
137
|
+
version=_JOB_RESULT_VERSION,
|
138
|
+
http_client=_http_client,
|
129
139
|
)
|
130
140
|
).results
|
131
141
|
return self._result
|
@@ -135,7 +145,9 @@ class ExecutionJob:
|
|
135
145
|
def result_value(self, *args: Any, **kwargs: Any) -> Any:
|
136
146
|
return self.result(*args, **kwargs)[0].value
|
137
147
|
|
138
|
-
def get_sample_result(
|
148
|
+
def get_sample_result(
|
149
|
+
self, _http_client: Optional[httpx.AsyncClient] = None
|
150
|
+
) -> ExecutionDetails:
|
139
151
|
"""
|
140
152
|
Returns the job's result as a single sample result after validation. If the result is not yet available, waits for it.
|
141
153
|
|
@@ -146,7 +158,7 @@ class ExecutionJob:
|
|
146
158
|
ClassiqExecutionResultError: In case the result does not contain a single sample result.
|
147
159
|
ClassiqAPIError: In case the job has failed.
|
148
160
|
"""
|
149
|
-
results = self.result()
|
161
|
+
results = self.result(_http_client=_http_client)
|
150
162
|
if len(results) != 1:
|
151
163
|
raise ClassiqExecutionResultError("sample")
|
152
164
|
|
@@ -157,7 +169,9 @@ class ExecutionJob:
|
|
157
169
|
return result.details[0]
|
158
170
|
raise ClassiqExecutionResultError("sample")
|
159
171
|
|
160
|
-
def get_batch_sample_result(
|
172
|
+
def get_batch_sample_result(
|
173
|
+
self, _http_client: Optional[httpx.AsyncClient] = None
|
174
|
+
) -> list[ExecutionDetails]:
|
161
175
|
"""
|
162
176
|
Returns the job's result as a single batch_sample result after validation. If the result is not yet available, waits for it.
|
163
177
|
|
@@ -168,7 +182,7 @@ class ExecutionJob:
|
|
168
182
|
ClassiqExecutionResultError: In case the result does not contain a single batch_sample result.
|
169
183
|
ClassiqAPIError: In case the job has failed.
|
170
184
|
"""
|
171
|
-
results = self.result()
|
185
|
+
results = self.result(_http_client=_http_client)
|
172
186
|
if len(results) != 1:
|
173
187
|
raise ClassiqExecutionResultError("batch_sample")
|
174
188
|
|
@@ -180,7 +194,9 @@ class ExecutionJob:
|
|
180
194
|
|
181
195
|
raise ClassiqExecutionResultError("batch_sample")
|
182
196
|
|
183
|
-
def get_estimate_result(
|
197
|
+
def get_estimate_result(
|
198
|
+
self, _http_client: Optional[httpx.AsyncClient] = None
|
199
|
+
) -> EstimationResult:
|
184
200
|
"""
|
185
201
|
Returns the job's result as a single estimate result after validation. If the result is not yet available, waits for it.
|
186
202
|
|
@@ -191,7 +207,7 @@ class ExecutionJob:
|
|
191
207
|
ClassiqExecutionResultError: In case the result does not contain a single estimate result.
|
192
208
|
ClassiqAPIError: In case the job has failed.
|
193
209
|
"""
|
194
|
-
results = self.result()
|
210
|
+
results = self.result(_http_client=_http_client)
|
195
211
|
if len(results) != 1:
|
196
212
|
raise ClassiqExecutionResultError("estimate")
|
197
213
|
|
@@ -202,7 +218,9 @@ class ExecutionJob:
|
|
202
218
|
return result.results[0]
|
203
219
|
raise ClassiqExecutionResultError("estimate")
|
204
220
|
|
205
|
-
def get_batch_estimate_result(
|
221
|
+
def get_batch_estimate_result(
|
222
|
+
self, _http_client: Optional[httpx.AsyncClient] = None
|
223
|
+
) -> list[EstimationResult]:
|
206
224
|
"""
|
207
225
|
Returns the job's result as a single batch_estimate result after validation. If the result is not yet available, waits for it.
|
208
226
|
|
@@ -213,7 +231,7 @@ class ExecutionJob:
|
|
213
231
|
ClassiqExecutionResultError: In case the result does not contain a single batch_estimate result.
|
214
232
|
ClassiqAPIError: In case the job has failed.
|
215
233
|
"""
|
216
|
-
results = self.result()
|
234
|
+
results = self.result(_http_client=_http_client)
|
217
235
|
if len(results) != 1:
|
218
236
|
raise ClassiqExecutionResultError("batch_estimate")
|
219
237
|
|
@@ -225,13 +243,21 @@ class ExecutionJob:
|
|
225
243
|
|
226
244
|
raise ClassiqExecutionResultError("batch_estimate")
|
227
245
|
|
228
|
-
async def poll_async(
|
246
|
+
async def poll_async(
|
247
|
+
self,
|
248
|
+
timeout_sec: Optional[float] = None,
|
249
|
+
_http_client: Optional[httpx.AsyncClient] = None,
|
250
|
+
) -> None:
|
229
251
|
if not self.status.is_final():
|
230
|
-
await self._poll_job(timeout_sec=timeout_sec)
|
252
|
+
await self._poll_job(timeout_sec=timeout_sec, _http_client=_http_client)
|
231
253
|
|
232
254
|
poll = syncify_function(poll_async)
|
233
255
|
|
234
|
-
async def _poll_job(
|
256
|
+
async def _poll_job(
|
257
|
+
self,
|
258
|
+
timeout_sec: Optional[float] = None,
|
259
|
+
_http_client: Optional[httpx.AsyncClient] = None,
|
260
|
+
) -> None:
|
235
261
|
def response_parser(json_response: JSONObject) -> Optional[bool]:
|
236
262
|
self._details = ExecutionJobDetails.model_validate(json_response)
|
237
263
|
if self.status.is_final():
|
@@ -247,14 +273,26 @@ class ExecutionJob:
|
|
247
273
|
job_id=self._job_id,
|
248
274
|
response_parser=response_parser,
|
249
275
|
timeout_sec=timeout_sec,
|
276
|
+
http_client=_http_client,
|
250
277
|
)
|
251
278
|
|
252
|
-
async def rename_async(
|
253
|
-
self
|
279
|
+
async def rename_async(
|
280
|
+
self,
|
281
|
+
name: str,
|
282
|
+
_http_client: Optional[httpx.AsyncClient] = None,
|
283
|
+
) -> None:
|
284
|
+
self._details = await ApiWrapper.call_patch_execution_job(
|
285
|
+
self._job_id,
|
286
|
+
name,
|
287
|
+
http_client=_http_client,
|
288
|
+
)
|
254
289
|
|
255
290
|
rename = syncify_function(rename_async)
|
256
291
|
|
257
|
-
async def cancel_async(
|
292
|
+
async def cancel_async(
|
293
|
+
self,
|
294
|
+
_http_client: Optional[httpx.AsyncClient] = None,
|
295
|
+
) -> None:
|
258
296
|
"""
|
259
297
|
Cancels the execution job. This implies the cancellation of any ongoing jobs
|
260
298
|
sent to the provider during this execution job.
|
@@ -263,7 +301,10 @@ class ExecutionJob:
|
|
263
301
|
to continue polling the job in order to ensure its cancellation, which might
|
264
302
|
not be immediate.
|
265
303
|
"""
|
266
|
-
await ApiWrapper.call_cancel_execution_job(
|
304
|
+
await ApiWrapper.call_cancel_execution_job(
|
305
|
+
self._job_id,
|
306
|
+
http_client=_http_client,
|
307
|
+
)
|
267
308
|
|
268
309
|
cancel = syncify_function(cancel_async)
|
269
310
|
|
classiq/execution/qaoa.py
CHANGED
@@ -65,20 +65,22 @@ def execute_qaoa(
|
|
65
65
|
model = create_model(main)
|
66
66
|
qprog = synthesize(model)
|
67
67
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
68
|
+
with ExecutionSession(qprog, execution_preferences) as es:
|
69
|
+
initial_params = (
|
70
|
+
np.concatenate(
|
71
|
+
(np.linspace(0, 1, num_layers), np.linspace(1, 0, num_layers))
|
72
|
+
)
|
73
|
+
* math.pi
|
74
|
+
)
|
75
|
+
final_params = scipy.optimize.minimize(
|
76
|
+
lambda params: es.estimate_cost(
|
77
|
+
lambda state: cost_func(state["v"]),
|
78
|
+
{"params": params.tolist()},
|
79
|
+
),
|
80
|
+
x0=initial_params,
|
81
|
+
method="COBYLA",
|
82
|
+
options={"maxiter": maxiter},
|
83
|
+
).x.tolist()
|
84
|
+
result = es.sample({"params": final_params})
|
83
85
|
|
84
86
|
return model, qprog, result
|
classiq/execution/qnn.py
CHANGED
@@ -59,22 +59,21 @@ def execute_qnn(
|
|
59
59
|
arguments: MultipleArguments,
|
60
60
|
observable: Optional[PauliOperator] = None,
|
61
61
|
) -> ResultsCollection:
|
62
|
-
|
62
|
+
with ExecutionSession(quantum_program) as session:
|
63
|
+
if observable:
|
64
|
+
execute_function = functools.partial(
|
65
|
+
_execute_qnn_estimate,
|
66
|
+
session=session,
|
67
|
+
observable=observable,
|
68
|
+
)
|
69
|
+
else:
|
70
|
+
execute_function = functools.partial(
|
71
|
+
_execute_qnn_sample,
|
72
|
+
session=session,
|
73
|
+
)
|
63
74
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
)
|
70
|
-
else:
|
71
|
-
execute_function = functools.partial(
|
72
|
-
_execute_qnn_sample,
|
73
|
-
session=session,
|
74
|
-
)
|
75
|
-
|
76
|
-
result: ResultsCollection = []
|
77
|
-
for chunk in more_itertools.chunked(arguments, _MAX_ARGUMENTS_SIZE):
|
78
|
-
chunk_result = execute_function(arguments=chunk)
|
79
|
-
result.extend(chunk_result)
|
80
|
-
return result
|
75
|
+
result: ResultsCollection = []
|
76
|
+
for chunk in more_itertools.chunked(arguments, _MAX_ARGUMENTS_SIZE):
|
77
|
+
chunk_result = execute_function(arguments=chunk)
|
78
|
+
result.extend(chunk_result)
|
79
|
+
return result
|
classiq/interface/_version.py
CHANGED
@@ -3,7 +3,7 @@ classiq/_analyzer_extras/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZ
|
|
3
3
|
classiq/_analyzer_extras/_ipywidgets_async_extension.py,sha256=7zv7mANDaLHH8QRGyY9QGDPPKPTjKKYdoq22iJaq_Ns,2190
|
4
4
|
classiq/_analyzer_extras/interactive_hardware.py,sha256=f7ad2HeFq1f-2dJtPpgOE_w2IFzm49W6P_c-MzqJ5qE,3257
|
5
5
|
classiq/_internals/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
6
|
-
classiq/_internals/api_wrapper.py,sha256=
|
6
|
+
classiq/_internals/api_wrapper.py,sha256=RmaC6gCinBwQat0kff66T1GW6Kf55IwOQgBwxN01nWc,15198
|
7
7
|
classiq/_internals/async_utils.py,sha256=utfzFLuCqmvxTLgg3JhMi-sBMIQFVW2LU5dqUEAJLCM,3370
|
8
8
|
classiq/_internals/authentication/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
9
9
|
classiq/_internals/authentication/auth0.py,sha256=K6yCnDUhIfgx-GfUHivak88_u06USHjXAjx9b3m5iek,3663
|
@@ -11,11 +11,11 @@ classiq/_internals/authentication/authentication.py,sha256=58Xv1QtynXwEfBqIV5E3L
|
|
11
11
|
classiq/_internals/authentication/device.py,sha256=dUii1-Z26j0NY4R6J4p0gjYSq5Goj-gwMxXPSnVANuE,4024
|
12
12
|
classiq/_internals/authentication/password_manager.py,sha256=aVR3wt07EJNa1OcUOmM9Nyg9Y4w8-3XzzrxJNsBWUBM,4438
|
13
13
|
classiq/_internals/authentication/token_manager.py,sha256=XStkqCPUQEqc3uj6tt_XzfUIcUlyT-94tBZ8tpQIy_s,5280
|
14
|
-
classiq/_internals/client.py,sha256=
|
14
|
+
classiq/_internals/client.py,sha256=WbIzf9jliAa-JssNTeEsFHm9K6DBXkD6ojocN-HVjGs,12218
|
15
15
|
classiq/_internals/config.py,sha256=-UKKvPp_61sxXY-URqU1feLqlfh9ww4wCDH4yl14EiA,3534
|
16
16
|
classiq/_internals/help.py,sha256=9gl64Y8nKW-f-8pYt7lMozOP6uERcIIf8dotgn_WKA0,460
|
17
17
|
classiq/_internals/host_checker.py,sha256=D0rgnoZrHo62rYS32yCuYZSyMrMChZG5ITsJxwj0R5g,3969
|
18
|
-
classiq/_internals/jobs.py,sha256=
|
18
|
+
classiq/_internals/jobs.py,sha256=Wu8FvnhqAjLghBhfrCOHj0cMSH7LV39gI-6Iit_TWOc,6329
|
19
19
|
classiq/_internals/logger.py,sha256=TGV37AR6aezLUzKUz4V1JercHeC68o_nNMlIRNHSRFM,634
|
20
20
|
classiq/_internals/type_validation.py,sha256=kRd6td9krHPSZWgfclNsQcX7sNTqikp5wc3DvNbDsrA,1332
|
21
21
|
classiq/analyzer/__init__.py,sha256=1ASEd3a5BLznMq_uD4ogR6buanALXfJIONZYmCweAgA,186
|
@@ -88,14 +88,14 @@ classiq/applications/qsvm/qsvm_data_generation.py,sha256=BIXBAxYNAvwZi1pVQJeSPTa
|
|
88
88
|
classiq/applications/qsvm/qsvm_model_constructor.py,sha256=4Md7FuKZsG9FUCwBVDwkLic2_EtYCfYCkT0mnH-IsVQ,3945
|
89
89
|
classiq/execution/__init__.py,sha256=t43kgz7GlyB8WT4gY10kr6tBBCTsD95oSWy-AZZZF_8,1220
|
90
90
|
classiq/execution/all_hardware_devices.py,sha256=KpLefEISE03FDdgFPGggXeG7NAxBW4090gN4272Dl-E,368
|
91
|
-
classiq/execution/execution_session.py,sha256=
|
91
|
+
classiq/execution/execution_session.py,sha256=LICEO4UcRg0QN-g3gvXxA1IjitaqNy8bUGTAsjZIDwo,17598
|
92
92
|
classiq/execution/iqcc.py,sha256=0wy1tgQFcwqf9wFYyi6_OYRtx4s0AEViIAIvUkyKBmk,2429
|
93
|
-
classiq/execution/jobs.py,sha256=
|
94
|
-
classiq/execution/qaoa.py,sha256=
|
95
|
-
classiq/execution/qnn.py,sha256=
|
93
|
+
classiq/execution/jobs.py,sha256=d8GxbMgnvsqVFgTl6ZZzE0pLiXMLBp_lytk-XoWN31k,10980
|
94
|
+
classiq/execution/qaoa.py,sha256=P2lrUaZrNM9wrkK0wLybwO6tbYi_WVLPAN9p6w5-5Tc,3100
|
95
|
+
classiq/execution/qnn.py,sha256=WGPvncz5uS2WxSY3-yBWt2LFiCk6Ug8WKWF-Kp-f7TM,2403
|
96
96
|
classiq/executor.py,sha256=rvcOI95rYtU2OSsuHLSjh9jutAk-CPSP6Bi01Fw3NqQ,2686
|
97
97
|
classiq/interface/__init__.py,sha256=cg7hD_XVu1_jJ1fgwmT0rMIoZHopNVeB8xtlmMx-E_A,83
|
98
|
-
classiq/interface/_version.py,sha256=
|
98
|
+
classiq/interface/_version.py,sha256=2Ae9frR30aMOSJn-oAr28BByDJGHJAjWfEMFbXGQLOg,197
|
99
99
|
classiq/interface/analyzer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
100
100
|
classiq/interface/analyzer/analysis_params.py,sha256=dM5rwSks798cxk4FWe4_X5ToRYtgZQh34F1u0XrFkK8,3881
|
101
101
|
classiq/interface/analyzer/cytoscape_graph.py,sha256=MpeRBIYS1TfwYwiFpgTO51IE0KoxhY510pmEM3S0rbw,2361
|
@@ -510,6 +510,6 @@ classiq/qmod/type_attribute_remover.py,sha256=NZmTXAsngWqthXjE8n-n6yE72fiWTFM12-
|
|
510
510
|
classiq/qmod/utilities.py,sha256=z_VnIRmOYTWjJp2UlOcWK0rQRtMqysmP_Gr6WYY_nak,2734
|
511
511
|
classiq/qmod/write_qmod.py,sha256=Oo-j_rSfcmzC5MOn0Vq334vv_OTvdD4P7K9pv-gbo8c,1833
|
512
512
|
classiq/synthesis.py,sha256=WLk3wpX_xPiAMstF9PGMO5SWVb_qqa1sN2Nj3MekX34,8113
|
513
|
-
classiq-0.
|
514
|
-
classiq-0.
|
515
|
-
classiq-0.
|
513
|
+
classiq-0.60.1.dist-info/METADATA,sha256=FQ0H8VWQncpnZcXnCQkVRFqlgq5DtajZCQYQ_VJTNPQ,3459
|
514
|
+
classiq-0.60.1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
515
|
+
classiq-0.60.1.dist-info/RECORD,,
|
File without changes
|