classiq 0.58.1__py3-none-any.whl → 0.60.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.
- classiq/_internals/api_wrapper.py +91 -20
- classiq/_internals/client.py +48 -11
- classiq/_internals/jobs.py +47 -40
- classiq/execution/execution_session.py +62 -22
- classiq/execution/jobs.py +64 -23
- classiq/execution/qaoa.py +17 -15
- classiq/execution/qnn.py +17 -18
- classiq/executor.py +2 -1
- classiq/interface/_version.py +1 -1
- classiq/interface/generator/arith/arithmetic_operations.py +1 -0
- classiq/interface/generator/register_role.py +8 -0
- classiq/interface/model/handle_binding.py +22 -3
- classiq/model_expansions/capturing/captured_vars.py +316 -0
- classiq/model_expansions/capturing/mangling_utils.py +18 -9
- classiq/model_expansions/closure.py +29 -74
- classiq/model_expansions/function_builder.py +51 -66
- classiq/model_expansions/interpreter.py +4 -7
- classiq/model_expansions/quantum_operations/bind.py +1 -3
- classiq/model_expansions/quantum_operations/call_emitter.py +46 -11
- classiq/model_expansions/quantum_operations/classicalif.py +2 -5
- classiq/model_expansions/quantum_operations/control.py +13 -16
- classiq/model_expansions/quantum_operations/emitter.py +36 -8
- classiq/model_expansions/quantum_operations/expression_operation.py +9 -19
- classiq/model_expansions/quantum_operations/inplace_binary_operation.py +4 -6
- classiq/model_expansions/quantum_operations/invert.py +5 -8
- classiq/model_expansions/quantum_operations/power.py +5 -10
- classiq/model_expansions/quantum_operations/quantum_assignment_operation.py +1 -3
- classiq/model_expansions/quantum_operations/quantum_function_call.py +1 -3
- classiq/model_expansions/quantum_operations/repeat.py +3 -3
- classiq/model_expansions/quantum_operations/variable_decleration.py +1 -1
- classiq/model_expansions/quantum_operations/within_apply.py +1 -5
- classiq/model_expansions/scope.py +2 -2
- classiq/model_expansions/transformers/var_splitter.py +32 -19
- classiq/model_expansions/utils/handles_collector.py +33 -0
- classiq/model_expansions/visitors/variable_references.py +18 -2
- classiq/qmod/qfunc.py +9 -13
- classiq/qmod/quantum_expandable.py +1 -21
- classiq/qmod/quantum_function.py +16 -0
- {classiq-0.58.1.dist-info → classiq-0.60.0.dist-info}/METADATA +1 -1
- {classiq-0.58.1.dist-info → classiq-0.60.0.dist-info}/RECORD +41 -42
- classiq/interface/executor/aws_execution_cost.py +0 -90
- classiq/model_expansions/capturing/captured_var_manager.py +0 -48
- classiq/model_expansions/capturing/propagated_var_stack.py +0 -194
- {classiq-0.58.1.dist-info → classiq-0.60.0.dist-info}/WHEEL +0 -0
@@ -1,4 +1,6 @@
|
|
1
1
|
import json
|
2
|
+
from functools import cached_property
|
3
|
+
from types import TracebackType
|
2
4
|
from typing import Any, Callable, Optional, Union, cast
|
3
5
|
|
4
6
|
import numpy as np
|
@@ -15,12 +17,14 @@ from classiq.interface.executor.result import (
|
|
15
17
|
from classiq.interface.generator.functions.qmod_python_interface import QmodPyStruct
|
16
18
|
from classiq.interface.generator.quantum_program import QuantumProgram
|
17
19
|
|
20
|
+
from classiq._internals import async_utils
|
21
|
+
from classiq._internals.api_wrapper import ApiWrapper
|
22
|
+
from classiq._internals.client import client
|
18
23
|
from classiq.applications.combinatorial_helpers.pauli_helpers.pauli_utils import (
|
19
24
|
_pauli_dict_to_pauli_terms,
|
20
25
|
_pauli_terms_to_qmod,
|
21
26
|
)
|
22
27
|
from classiq.execution.jobs import ExecutionJob
|
23
|
-
from classiq.executor import execute
|
24
28
|
from classiq.qmod.builtins import PauliTerm
|
25
29
|
from classiq.qmod.builtins.classical_execution_primitives import (
|
26
30
|
CARRAY_SEPARATOR,
|
@@ -145,6 +149,7 @@ class ExecutionSession:
|
|
145
149
|
"""
|
146
150
|
A session for executing a quantum program.
|
147
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.
|
148
153
|
|
149
154
|
Attributes:
|
150
155
|
quantum_program (Union[SerializedQuantumProgram, QuantumProgram]): The quantum program to execute.
|
@@ -158,14 +163,49 @@ class ExecutionSession:
|
|
158
163
|
):
|
159
164
|
self.program: QuantumProgram = _deserialize_program(quantum_program)
|
160
165
|
self.update_execution_preferences(execution_preferences)
|
166
|
+
# When the primitives are called, we always override the
|
167
|
+
# classical_execution_code, and we don't want the conversion route to fail
|
168
|
+
# because cmain is expected in some cases
|
169
|
+
self.program.model.classical_execution_code = "dummy"
|
161
170
|
|
162
|
-
|
163
|
-
|
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:
|
164
185
|
"""
|
165
|
-
|
166
|
-
SerializedQuantumProgram: The serialized quantum program (str). See `QuantumProgram`.
|
186
|
+
Close the session and clean up its resources.
|
167
187
|
"""
|
168
|
-
|
188
|
+
async_utils.run(self._async_client.aclose())
|
189
|
+
|
190
|
+
@cached_property
|
191
|
+
def _execution_input(self) -> dict:
|
192
|
+
return async_utils.run(
|
193
|
+
ApiWrapper.call_convert_quantum_program(self.program, self._async_client)
|
194
|
+
)
|
195
|
+
|
196
|
+
def _execute(
|
197
|
+
self, classical_execution_code: str, primitives_input: PrimitivesInput
|
198
|
+
) -> ExecutionJob:
|
199
|
+
execution_input = self._execution_input.copy()
|
200
|
+
execution_input["classical_execution_code"] = classical_execution_code
|
201
|
+
# The use of `model_dump_json` is necessary for complex numbers serialization
|
202
|
+
execution_input["primitives_input"] = json.loads(
|
203
|
+
primitives_input.model_dump_json()
|
204
|
+
)
|
205
|
+
result = async_utils.run(
|
206
|
+
ApiWrapper.call_execute_execution_input(execution_input, self._async_client)
|
207
|
+
)
|
208
|
+
return ExecutionJob(details=result)
|
169
209
|
|
170
210
|
def update_execution_preferences(
|
171
211
|
self, execution_preferences: Optional[ExecutionPreferences]
|
@@ -193,7 +233,7 @@ class ExecutionSession:
|
|
193
233
|
The result of the sampling.
|
194
234
|
"""
|
195
235
|
job = self.submit_sample(parameters=parameters)
|
196
|
-
return job.get_sample_result()
|
236
|
+
return job.get_sample_result(_http_client=self._async_client)
|
197
237
|
|
198
238
|
def submit_sample(
|
199
239
|
self, parameters: Optional[ExecutionParams] = None
|
@@ -210,13 +250,13 @@ class ExecutionSession:
|
|
210
250
|
Returns:
|
211
251
|
The execution job.
|
212
252
|
"""
|
213
|
-
|
253
|
+
classical_execution_code = generate_code_snippet(
|
214
254
|
SupportedPrimitives.SAMPLE, parameters=format_parameters(parameters)
|
215
255
|
)
|
216
|
-
|
256
|
+
execution_primitives_input = PrimitivesInput(
|
217
257
|
sample=[parse_params(parameters)] if parameters is not None else [{}]
|
218
258
|
)
|
219
|
-
return
|
259
|
+
return self._execute(classical_execution_code, execution_primitives_input)
|
220
260
|
|
221
261
|
def batch_sample(self, parameters: list[ExecutionParams]) -> list[ExecutionDetails]:
|
222
262
|
"""
|
@@ -229,7 +269,7 @@ class ExecutionSession:
|
|
229
269
|
List[ExecutionDetails]: The results of all the sampling iterations.
|
230
270
|
"""
|
231
271
|
job = self.submit_batch_sample(parameters=parameters)
|
232
|
-
return job.get_batch_sample_result()
|
272
|
+
return job.get_batch_sample_result(_http_client=self._async_client)
|
233
273
|
|
234
274
|
def submit_batch_sample(self, parameters: list[ExecutionParams]) -> ExecutionJob:
|
235
275
|
"""
|
@@ -244,13 +284,13 @@ class ExecutionSession:
|
|
244
284
|
Returns:
|
245
285
|
The execution job.
|
246
286
|
"""
|
247
|
-
|
287
|
+
classical_execution_code = generate_code_snippet(
|
248
288
|
SupportedPrimitives.BATCH_SAMPLE, parameters=format_parameters(parameters)
|
249
289
|
)
|
250
|
-
|
290
|
+
execution_primitives_input = PrimitivesInput(
|
251
291
|
sample=[parse_params(params) for params in parameters]
|
252
292
|
)
|
253
|
-
return
|
293
|
+
return self._execute(classical_execution_code, execution_primitives_input)
|
254
294
|
|
255
295
|
def estimate(
|
256
296
|
self, hamiltonian: Hamiltonian, parameters: Optional[ExecutionParams] = None
|
@@ -266,7 +306,7 @@ class ExecutionSession:
|
|
266
306
|
EstimationResult: The result of the estimation.
|
267
307
|
"""
|
268
308
|
job = self.submit_estimate(hamiltonian=hamiltonian, parameters=parameters)
|
269
|
-
return job.get_estimate_result()
|
309
|
+
return job.get_estimate_result(_http_client=self._async_client)
|
270
310
|
|
271
311
|
def submit_estimate(
|
272
312
|
self, hamiltonian: Hamiltonian, parameters: Optional[ExecutionParams] = None
|
@@ -284,12 +324,12 @@ class ExecutionSession:
|
|
284
324
|
Returns:
|
285
325
|
The execution job.
|
286
326
|
"""
|
287
|
-
|
327
|
+
classical_execution_code = generate_code_snippet(
|
288
328
|
SupportedPrimitives.ESTIMATE,
|
289
329
|
parameters=format_parameters(parameters),
|
290
330
|
hamiltonian=to_hamiltonian_str(hamiltonian),
|
291
331
|
)
|
292
|
-
|
332
|
+
execution_primitives_input = PrimitivesInput(
|
293
333
|
estimate=EstimateInput(
|
294
334
|
hamiltonian=_hamiltonian_to_pauli_operator(hamiltonian),
|
295
335
|
parameters=(
|
@@ -297,7 +337,7 @@ class ExecutionSession:
|
|
297
337
|
),
|
298
338
|
)
|
299
339
|
)
|
300
|
-
return
|
340
|
+
return self._execute(classical_execution_code, execution_primitives_input)
|
301
341
|
|
302
342
|
def batch_estimate(
|
303
343
|
self, hamiltonian: Hamiltonian, parameters: list[ExecutionParams]
|
@@ -313,7 +353,7 @@ class ExecutionSession:
|
|
313
353
|
List[EstimationResult]: The results of all the estimation iterations.
|
314
354
|
"""
|
315
355
|
job = self.submit_batch_estimate(hamiltonian=hamiltonian, parameters=parameters)
|
316
|
-
return job.get_batch_estimate_result()
|
356
|
+
return job.get_batch_estimate_result(_http_client=self._async_client)
|
317
357
|
|
318
358
|
def submit_batch_estimate(
|
319
359
|
self, hamiltonian: Hamiltonian, parameters: list[ExecutionParams]
|
@@ -331,18 +371,18 @@ class ExecutionSession:
|
|
331
371
|
Returns:
|
332
372
|
The execution job.
|
333
373
|
"""
|
334
|
-
|
374
|
+
classical_execution_code = generate_code_snippet(
|
335
375
|
SupportedPrimitives.BATCH_ESTIMATE,
|
336
376
|
parameters=format_parameters(parameters),
|
337
377
|
hamiltonian=to_hamiltonian_str(hamiltonian),
|
338
378
|
)
|
339
|
-
|
379
|
+
execution_primitives_input = PrimitivesInput(
|
340
380
|
estimate=EstimateInput(
|
341
381
|
hamiltonian=_hamiltonian_to_pauli_operator(hamiltonian),
|
342
382
|
parameters=[parse_params(params) for params in parameters],
|
343
383
|
)
|
344
384
|
)
|
345
|
-
return
|
385
|
+
return self._execute(classical_execution_code, execution_primitives_input)
|
346
386
|
|
347
387
|
def estimate_cost(
|
348
388
|
self,
|
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/executor.py
CHANGED
@@ -32,7 +32,8 @@ def _parse_serialized_qprog(
|
|
32
32
|
|
33
33
|
async def execute_async(quantum_program: SerializedQuantumProgram) -> ExecutionJob:
|
34
34
|
circuit = _parse_serialized_qprog(quantum_program)
|
35
|
-
|
35
|
+
execution_input = await ApiWrapper.call_convert_quantum_program(circuit)
|
36
|
+
result = await ApiWrapper.call_execute_execution_input(execution_input)
|
36
37
|
return ExecutionJob(details=result)
|
37
38
|
|
38
39
|
|
classiq/interface/_version.py
CHANGED
@@ -10,6 +10,7 @@ from classiq.interface.generator.arith.machine_precision import (
|
|
10
10
|
from classiq.interface.generator.arith.register_user_input import RegisterArithmeticInfo
|
11
11
|
from classiq.interface.generator.function_params import FunctionParams
|
12
12
|
|
13
|
+
IMPLICIT_OUTPUTS: Final[str] = "implicit_outputs"
|
13
14
|
DEFAULT_GARBAGE_OUT_NAME: Final[str] = "extra_qubits"
|
14
15
|
MODULO_WITH_FRACTION_PLACES_ERROR_MSG: Final[str] = (
|
15
16
|
"Modulo with fraction places not supported"
|
@@ -29,3 +29,11 @@ class RegisterRole(StrEnum):
|
|
29
29
|
RegisterRole.AUXILIARY,
|
30
30
|
RegisterRole.EXPLICIT_ZERO_INPUT,
|
31
31
|
}
|
32
|
+
|
33
|
+
@staticmethod
|
34
|
+
def clean_output_roles() -> set["RegisterRole"]:
|
35
|
+
return {RegisterRole.ZERO_OUTPUT, RegisterRole.AUXILIARY}
|
36
|
+
|
37
|
+
@staticmethod
|
38
|
+
def dirty_output_roles() -> set["RegisterRole"]:
|
39
|
+
return {RegisterRole.OUTPUT, RegisterRole.GARBAGE_OUTPUT}
|
@@ -19,6 +19,10 @@ class HandleBinding(ASTNode):
|
|
19
19
|
def __str__(self) -> str:
|
20
20
|
return self.name
|
21
21
|
|
22
|
+
@property
|
23
|
+
def qmod_expr(self) -> str:
|
24
|
+
return self.name
|
25
|
+
|
22
26
|
def is_bindable(self) -> bool:
|
23
27
|
return True
|
24
28
|
|
@@ -57,6 +61,9 @@ class HandleBinding(ASTNode):
|
|
57
61
|
return replacement
|
58
62
|
return self
|
59
63
|
|
64
|
+
def __contains__(self, other_handle: "HandleBinding") -> bool:
|
65
|
+
return self.collapse() in other_handle.collapse().prefixes()
|
66
|
+
|
60
67
|
|
61
68
|
class NestedHandleBinding(HandleBinding):
|
62
69
|
base_handle: "ConcreteHandleBinding"
|
@@ -66,8 +73,8 @@ class NestedHandleBinding(HandleBinding):
|
|
66
73
|
def _set_name(cls, values: Any) -> dict[str, Any]:
|
67
74
|
if isinstance(values, dict):
|
68
75
|
orig = values
|
69
|
-
while "base_handle" in values:
|
70
|
-
values = values["base_handle"]
|
76
|
+
while "base_handle" in dict(values):
|
77
|
+
values = dict(values)["base_handle"]
|
71
78
|
orig["name"] = dict(values).get("name")
|
72
79
|
return orig
|
73
80
|
if isinstance(values, NestedHandleBinding):
|
@@ -106,6 +113,10 @@ class SubscriptHandleBinding(NestedHandleBinding):
|
|
106
113
|
def __str__(self) -> str:
|
107
114
|
return f"{self.base_handle}[{self.index}]"
|
108
115
|
|
116
|
+
@property
|
117
|
+
def qmod_expr(self) -> str:
|
118
|
+
return f"{self.base_handle.qmod_expr}[{self.index}]"
|
119
|
+
|
109
120
|
@property
|
110
121
|
def identifier(self) -> str:
|
111
122
|
return f"{self.base_handle.identifier}{HANDLE_ID_SEPARATOR}{self.index}"
|
@@ -157,6 +168,10 @@ class SlicedHandleBinding(NestedHandleBinding):
|
|
157
168
|
def __str__(self) -> str:
|
158
169
|
return f"{self.base_handle}[{self.start}:{self.end}]"
|
159
170
|
|
171
|
+
@property
|
172
|
+
def qmod_expr(self) -> str:
|
173
|
+
return f"{self.base_handle.qmod_expr}[{self.start}:{self.end}]"
|
174
|
+
|
160
175
|
@property
|
161
176
|
def identifier(self) -> str:
|
162
177
|
return (
|
@@ -165,7 +180,7 @@ class SlicedHandleBinding(NestedHandleBinding):
|
|
165
180
|
|
166
181
|
def collapse(self) -> HandleBinding:
|
167
182
|
if isinstance(self.base_handle, SlicedHandleBinding):
|
168
|
-
return
|
183
|
+
return SlicedHandleBinding(
|
169
184
|
base_handle=self.base_handle.base_handle,
|
170
185
|
start=self._get_collapsed_start(),
|
171
186
|
end=self._get_collapsed_stop(),
|
@@ -224,6 +239,10 @@ class FieldHandleBinding(NestedHandleBinding):
|
|
224
239
|
def __str__(self) -> str:
|
225
240
|
return f"{self.base_handle}.{self.field}"
|
226
241
|
|
242
|
+
@property
|
243
|
+
def qmod_expr(self) -> str:
|
244
|
+
return f"get_field({self.base_handle.qmod_expr}, '{self.field}')"
|
245
|
+
|
227
246
|
@property
|
228
247
|
def identifier(self) -> str:
|
229
248
|
return f"{self.base_handle.identifier}{HANDLE_ID_SEPARATOR}{self.field}"
|