classiq 0.46.1__py3-none-any.whl → 0.47.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 +45 -8
- classiq/execution/execution_session.py +96 -39
- classiq/execution/jobs.py +108 -1
- classiq/interface/_version.py +1 -1
- classiq/interface/backend/quantum_backend_providers.py +0 -1
- classiq/interface/debug_info/debug_info.py +23 -1
- classiq/interface/execution/primitives.py +9 -0
- classiq/interface/generator/arith/arithmetic_operations.py +5 -2
- classiq/interface/generator/arith/binary_ops.py +21 -14
- classiq/interface/generator/arith/extremum_operations.py +9 -1
- classiq/interface/generator/arith/number_utils.py +6 -0
- classiq/interface/generator/arith/register_user_input.py +30 -21
- classiq/interface/generator/arith/unary_ops.py +13 -1
- classiq/interface/generator/generated_circuit_data.py +47 -2
- classiq/interface/generator/quantum_program.py +10 -2
- classiq/interface/ide/visual_model.py +7 -1
- classiq/interface/interface_version.py +1 -1
- classiq/interface/model/phase_operation.py +11 -0
- classiq/interface/model/statement_block.py +3 -0
- classiq/interface/server/routes.py +0 -3
- classiq/model_expansions/interpreter.py +6 -0
- classiq/model_expansions/quantum_operations/emitter.py +8 -2
- classiq/model_expansions/quantum_operations/phase.py +177 -0
- classiq/qmod/builtins/operations.py +14 -0
- classiq/qmod/native/pretty_printer.py +6 -0
- classiq/qmod/pretty_print/pretty_printer.py +6 -0
- {classiq-0.46.1.dist-info → classiq-0.47.0.dist-info}/METADATA +7 -4
- {classiq-0.46.1.dist-info → classiq-0.47.0.dist-info}/RECORD +29 -26
- {classiq-0.46.1.dist-info → classiq-0.47.0.dist-info}/WHEEL +0 -0
@@ -1,5 +1,5 @@
|
|
1
1
|
import json
|
2
|
-
from typing import Dict, List, Optional, Protocol, Type, TypeVar
|
2
|
+
from typing import Any, Dict, List, Optional, Protocol, Type, TypeVar
|
3
3
|
|
4
4
|
import pydantic
|
5
5
|
|
@@ -7,6 +7,7 @@ import classiq.interface.executor.execution_result
|
|
7
7
|
import classiq.interface.pyomo_extension
|
8
8
|
from classiq.interface.analyzer import analysis_params, result as analysis_result
|
9
9
|
from classiq.interface.analyzer.analysis_params import AnalysisRBParams
|
10
|
+
from classiq.interface.analyzer.result import GraphStatus
|
10
11
|
from classiq.interface.chemistry import ground_state_problem, operator
|
11
12
|
from classiq.interface.enum_utils import StrEnum
|
12
13
|
from classiq.interface.exceptions import ClassiqAPIError, ClassiqValueError
|
@@ -16,7 +17,7 @@ from classiq.interface.execution.jobs import (
|
|
16
17
|
)
|
17
18
|
from classiq.interface.executor import execution_request
|
18
19
|
from classiq.interface.generator import quantum_program as generator_result
|
19
|
-
from classiq.interface.hardware import HardwareInformation
|
20
|
+
from classiq.interface.hardware import HardwareInformation, Provider
|
20
21
|
from classiq.interface.jobs import JobDescription, JobID, JSONObject
|
21
22
|
from classiq.interface.model.model import Model
|
22
23
|
from classiq.interface.server import routes
|
@@ -36,6 +37,7 @@ class HTTPMethod(StrEnum):
|
|
36
37
|
GET = "GET"
|
37
38
|
POST = "POST"
|
38
39
|
PATCH = "PATCH"
|
40
|
+
PUT = "PUT"
|
39
41
|
|
40
42
|
|
41
43
|
class StatusType(Protocol):
|
@@ -80,8 +82,9 @@ class ApiWrapper:
|
|
80
82
|
params: Optional[Dict] = None,
|
81
83
|
use_versioned_url: bool = True,
|
82
84
|
headers: Optional[Dict[str, str]] = None,
|
85
|
+
allow_none: bool = False,
|
83
86
|
) -> dict:
|
84
|
-
res = await client().call_api(
|
87
|
+
res: Any = await client().call_api(
|
85
88
|
http_method=http_method,
|
86
89
|
url=url,
|
87
90
|
body=body,
|
@@ -89,6 +92,8 @@ class ApiWrapper:
|
|
89
92
|
params=params,
|
90
93
|
use_versioned_url=use_versioned_url,
|
91
94
|
)
|
95
|
+
if allow_none and res is None:
|
96
|
+
return {}
|
92
97
|
if not isinstance(res, dict):
|
93
98
|
raise ClassiqValueError(f"Unexpected returned value: {res}")
|
94
99
|
return res
|
@@ -169,6 +174,18 @@ class ApiWrapper:
|
|
169
174
|
)
|
170
175
|
return ExecutionJobDetailsV1.parse_obj(data)
|
171
176
|
|
177
|
+
@classmethod
|
178
|
+
async def call_cancel_execution_job(
|
179
|
+
cls,
|
180
|
+
job_id: JobID,
|
181
|
+
) -> None:
|
182
|
+
await cls._call_task(
|
183
|
+
http_method=HTTPMethod.PUT,
|
184
|
+
url=f"{routes.EXECUTION_JOBS_NON_VERSIONED_FULL_PATH}/{job_id.job_id}/cancel",
|
185
|
+
use_versioned_url=False,
|
186
|
+
allow_none=True,
|
187
|
+
)
|
188
|
+
|
172
189
|
@classmethod
|
173
190
|
async def call_query_execution_jobs(
|
174
191
|
cls,
|
@@ -267,12 +284,32 @@ class ApiWrapper:
|
|
267
284
|
cls,
|
268
285
|
params: analysis_params.AnalysisOptionalDevicesParams,
|
269
286
|
) -> analysis_result.DevicesResult:
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
287
|
+
hardware_info = await cls.call_get_all_hardware_devices()
|
288
|
+
return cls._get_devices_from_hardware_info(hardware_info, params)
|
289
|
+
|
290
|
+
@staticmethod
|
291
|
+
def _get_devices_from_hardware_info(
|
292
|
+
hardware_info: List[HardwareInformation],
|
293
|
+
params: analysis_params.AnalysisOptionalDevicesParams,
|
294
|
+
) -> analysis_result.DevicesResult:
|
295
|
+
available_hardware: Dict[Provider, Dict[str, bool]] = {
|
296
|
+
Provider.IBM_QUANTUM: {},
|
297
|
+
Provider.AMAZON_BRAKET: {},
|
298
|
+
Provider.AZURE_QUANTUM: {},
|
299
|
+
}
|
300
|
+
for info in hardware_info:
|
301
|
+
if info.provider not in available_hardware:
|
302
|
+
continue
|
303
|
+
is_available = info.number_of_qubits >= params.qubit_count
|
304
|
+
available_hardware[info.provider][info.display_name] = is_available
|
305
|
+
return analysis_result.DevicesResult(
|
306
|
+
devices=analysis_result.AvailableHardware(
|
307
|
+
ibm_quantum=available_hardware[Provider.IBM_QUANTUM],
|
308
|
+
azure_quantum=available_hardware[Provider.AZURE_QUANTUM],
|
309
|
+
amazon_braket=available_hardware[Provider.AMAZON_BRAKET],
|
310
|
+
),
|
311
|
+
status=GraphStatus.SUCCESS,
|
274
312
|
)
|
275
|
-
return analysis_result.DevicesResult.parse_obj(data)
|
276
313
|
|
277
314
|
@classmethod
|
278
315
|
async def call_get_all_hardware_devices(cls) -> List[HardwareInformation]:
|
@@ -2,13 +2,11 @@ import json
|
|
2
2
|
from typing import Any, Callable, Dict, List, Optional, Tuple, Union, cast
|
3
3
|
|
4
4
|
from classiq.interface.exceptions import ClassiqValueError
|
5
|
+
from classiq.interface.execution.primitives import PrimitivesInput
|
5
6
|
from classiq.interface.executor.execution_preferences import ExecutionPreferences
|
6
|
-
from classiq.interface.executor.execution_result import ResultsCollection
|
7
7
|
from classiq.interface.executor.result import (
|
8
8
|
EstimationResult,
|
9
|
-
EstimationResults,
|
10
9
|
ExecutionDetails,
|
11
|
-
MultipleExecutionDetails,
|
12
10
|
)
|
13
11
|
from classiq.interface.generator.functions.qmod_python_interface import QmodPyStruct
|
14
12
|
from classiq.interface.generator.quantum_program import QuantumProgram
|
@@ -17,6 +15,7 @@ from classiq.applications.combinatorial_helpers.pauli_helpers.pauli_utils import
|
|
17
15
|
_pauli_dict_to_str,
|
18
16
|
_pauli_terms_to_qmod,
|
19
17
|
)
|
18
|
+
from classiq.execution.jobs import ExecutionJob
|
20
19
|
from classiq.executor import execute
|
21
20
|
from classiq.qmod.builtins import PauliTerm
|
22
21
|
from classiq.qmod.builtins.classical_execution_primitives import (
|
@@ -165,14 +164,6 @@ class ExecutionSession:
|
|
165
164
|
if execution_preferences is not None:
|
166
165
|
self.program.model.execution_preferences = execution_preferences
|
167
166
|
|
168
|
-
def _execute_quantum_program(
|
169
|
-
self, operation: str, **kwargs: Any
|
170
|
-
) -> ResultsCollection:
|
171
|
-
self.program.model.classical_execution_code = generate_code_snippet(
|
172
|
-
operation, **kwargs
|
173
|
-
)
|
174
|
-
return execute(SerializedQuantumProgram(self.qprog)).result()
|
175
|
-
|
176
167
|
def sample(self, parameters: Optional[ExecutionParams] = None) -> ExecutionDetails:
|
177
168
|
"""
|
178
169
|
Samples the quantum program with the given parameters, if any.
|
@@ -180,16 +171,34 @@ class ExecutionSession:
|
|
180
171
|
Args:
|
181
172
|
parameters: The values to set for the parameters of the quantum program when sampling. Each key should be the name of a parameter in the quantum program (parameters of the main function), and the value should be the value to set for that parameter.
|
182
173
|
|
174
|
+
Returns:
|
175
|
+
The result of the sampling.
|
176
|
+
"""
|
177
|
+
job = self.submit_sample(parameters=parameters)
|
178
|
+
return job.get_sample_result()
|
179
|
+
|
180
|
+
def submit_sample(
|
181
|
+
self, parameters: Optional[ExecutionParams] = None
|
182
|
+
) -> ExecutionJob:
|
183
|
+
"""
|
184
|
+
Initiates an execution job with the `sample` primitive.
|
185
|
+
|
186
|
+
This is a non-blocking version of `sample`: it gets the same parameters and initiates the same execution job, but instead
|
187
|
+
of waiting for the result, it returns the job object immediately.
|
188
|
+
|
189
|
+
Args:
|
190
|
+
parameters: The values to set for the parameters of the quantum program when sampling. Each key should be the name of a parameter in the quantum program (parameters of the main function), and the value should be the value to set for that parameter.
|
183
191
|
|
184
192
|
Returns:
|
185
|
-
|
193
|
+
The execution job.
|
186
194
|
"""
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
195
|
+
self.program.model.classical_execution_code = generate_code_snippet(
|
196
|
+
SupportedPrimitives.SAMPLE, parameters=format_parameters(parameters)
|
197
|
+
)
|
198
|
+
self.program.execution_primitives_input = PrimitivesInput(
|
199
|
+
sample=[parameters] if parameters is not None else [{}]
|
192
200
|
)
|
201
|
+
return execute(SerializedQuantumProgram(self.qprog))
|
193
202
|
|
194
203
|
def batch_sample(self, parameters: List[ExecutionParams]) -> List[ExecutionDetails]:
|
195
204
|
"""
|
@@ -201,13 +210,27 @@ class ExecutionSession:
|
|
201
210
|
Returns:
|
202
211
|
List[ExecutionDetails]: The results of all the sampling iterations.
|
203
212
|
"""
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
213
|
+
job = self.submit_batch_sample(parameters=parameters)
|
214
|
+
return job.get_batch_sample_result()
|
215
|
+
|
216
|
+
def submit_batch_sample(self, parameters: List[ExecutionParams]) -> ExecutionJob:
|
217
|
+
"""
|
218
|
+
Initiates an execution job with the `batch_sample` primitive.
|
219
|
+
|
220
|
+
This is a non-blocking version of `batch_sample`: it gets the same parameters and initiates the same execution job, but instead
|
221
|
+
of waiting for the result, it returns the job object immediately.
|
222
|
+
|
223
|
+
Args:
|
224
|
+
parameters: A list of the parameters for each iteration. Each item is a dictionary where each key should be the name of a parameter in the quantum program (parameters of the main function), and the value should be the value to set for that parameter.
|
225
|
+
|
226
|
+
Returns:
|
227
|
+
The execution job.
|
228
|
+
"""
|
229
|
+
self.program.model.classical_execution_code = generate_code_snippet(
|
230
|
+
SupportedPrimitives.BATCH_SAMPLE, parameters=format_parameters(parameters)
|
231
|
+
)
|
232
|
+
self.program.execution_primitives_input = PrimitivesInput(sample=parameters)
|
233
|
+
return execute(SerializedQuantumProgram(self.qprog))
|
211
234
|
|
212
235
|
def estimate(
|
213
236
|
self, hamiltonian: Hamiltonian, parameters: Optional[ExecutionParams] = None
|
@@ -222,14 +245,31 @@ class ExecutionSession:
|
|
222
245
|
Returns:
|
223
246
|
EstimationResult: The result of the estimation.
|
224
247
|
"""
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
248
|
+
job = self.submit_estimate(hamiltonian=hamiltonian, parameters=parameters)
|
249
|
+
return job.get_estimate_result()
|
250
|
+
|
251
|
+
def submit_estimate(
|
252
|
+
self, hamiltonian: Hamiltonian, parameters: Optional[ExecutionParams] = None
|
253
|
+
) -> ExecutionJob:
|
254
|
+
"""
|
255
|
+
Initiates an execution job with the `estimate` primitive.
|
256
|
+
|
257
|
+
This is a non-blocking version of `estimate`: it gets the same parameters and initiates the same execution job, but instead
|
258
|
+
of waiting for the result, it returns the job object immediately.
|
259
|
+
|
260
|
+
Args:
|
261
|
+
hamiltonian: The Hamiltonian to estimate the expectation value of.
|
262
|
+
parameters: The values to set for the parameters of the quantum program when estimating. Each key should be the name of a parameter in the quantum program (parameters of the main function), and the value should be the value to set for that parameter.
|
263
|
+
|
264
|
+
Returns:
|
265
|
+
The execution job.
|
266
|
+
"""
|
267
|
+
self.program.model.classical_execution_code = generate_code_snippet(
|
268
|
+
SupportedPrimitives.ESTIMATE,
|
269
|
+
parameters=format_parameters(parameters),
|
270
|
+
hamiltonian=to_hamiltonian_str(hamiltonian),
|
232
271
|
)
|
272
|
+
return execute(SerializedQuantumProgram(self.qprog))
|
233
273
|
|
234
274
|
def batch_estimate(
|
235
275
|
self, hamiltonian: Hamiltonian, parameters: List[ExecutionParams]
|
@@ -244,11 +284,28 @@ class ExecutionSession:
|
|
244
284
|
Returns:
|
245
285
|
List[EstimationResult]: The results of all the estimation iterations.
|
246
286
|
"""
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
287
|
+
job = self.submit_batch_estimate(hamiltonian=hamiltonian, parameters=parameters)
|
288
|
+
return job.get_batch_estimate_result()
|
289
|
+
|
290
|
+
def submit_batch_estimate(
|
291
|
+
self, hamiltonian: Hamiltonian, parameters: List[ExecutionParams]
|
292
|
+
) -> ExecutionJob:
|
293
|
+
"""
|
294
|
+
Initiates an execution job with the `batch_estimate` primitive.
|
295
|
+
|
296
|
+
This is a non-blocking version of `batch_estimate`: it gets the same parameters and initiates the same execution job, but instead
|
297
|
+
of waiting for the result, it returns the job object immediately.
|
298
|
+
|
299
|
+
Args:
|
300
|
+
hamiltonian: The Hamiltonian to estimate the expectation value of.
|
301
|
+
parameters: A list of the parameters for each iteration. Each item is a dictionary where each key should be the name of a parameter in the quantum program (parameters of the main function), and the value should be the value to set for that parameter.
|
302
|
+
|
303
|
+
Returns:
|
304
|
+
The execution job.
|
305
|
+
"""
|
306
|
+
self.program.model.classical_execution_code = generate_code_snippet(
|
307
|
+
SupportedPrimitives.BATCH_ESTIMATE,
|
308
|
+
parameters=format_parameters(parameters),
|
309
|
+
hamiltonian=to_hamiltonian_str(hamiltonian),
|
310
|
+
)
|
311
|
+
return execute(SerializedQuantumProgram(self.qprog))
|
classiq/execution/jobs.py
CHANGED
@@ -3,10 +3,19 @@ from datetime import datetime
|
|
3
3
|
from typing import Any, List, Optional, Union
|
4
4
|
from urllib.parse import urljoin
|
5
5
|
|
6
|
-
from classiq.interface.exceptions import
|
6
|
+
from classiq.interface.exceptions import (
|
7
|
+
ClassiqAPIError,
|
8
|
+
ClassiqError,
|
9
|
+
)
|
7
10
|
from classiq.interface.execution.jobs import ExecutionJobDetailsV1
|
8
11
|
from classiq.interface.executor.execution_request import ExecutionJobDetails
|
9
12
|
from classiq.interface.executor.execution_result import ResultsCollection
|
13
|
+
from classiq.interface.executor.result import (
|
14
|
+
EstimationResult,
|
15
|
+
EstimationResults,
|
16
|
+
ExecutionDetails,
|
17
|
+
MultipleExecutionDetails,
|
18
|
+
)
|
10
19
|
from classiq.interface.jobs import JobStatus, JSONObject
|
11
20
|
from classiq.interface.server.routes import EXECUTION_JOBS_NON_VERSIONED_FULL_PATH
|
12
21
|
|
@@ -21,6 +30,13 @@ _JOB_DETAILS_VERSION = "v1"
|
|
21
30
|
_JOB_RESULT_VERSION = "v1"
|
22
31
|
|
23
32
|
|
33
|
+
class ClassiqExecutionResultError(ClassiqError):
|
34
|
+
def __init__(self, primitive: str) -> None:
|
35
|
+
super().__init__(
|
36
|
+
f"Execution job does not contain a single {primitive!r} result, make sure you use the 'get_*_result' method matching the primitive you executed. You can use the 'result' method to see the general result."
|
37
|
+
)
|
38
|
+
|
39
|
+
|
24
40
|
class ExecutionJob:
|
25
41
|
_details: _JobDetails
|
26
42
|
_result: Optional[ResultsCollection]
|
@@ -112,6 +128,84 @@ class ExecutionJob:
|
|
112
128
|
def result_value(self, *args: Any, **kwargs: Any) -> Any:
|
113
129
|
return self.result(*args, **kwargs)[0].value
|
114
130
|
|
131
|
+
def get_sample_result(self) -> ExecutionDetails:
|
132
|
+
"""
|
133
|
+
Returns the job's result as a single sample result after validation. If the result is not yet available, waits for it.
|
134
|
+
|
135
|
+
Returns:
|
136
|
+
The sample result of the execution job.
|
137
|
+
|
138
|
+
Raises:
|
139
|
+
ClassiqExecutionResultError: In case the result does not contain a single sample result.
|
140
|
+
ClassiqAPIError: In case the job has failed.
|
141
|
+
"""
|
142
|
+
results = self.result()
|
143
|
+
if len(results) != 1:
|
144
|
+
raise ClassiqExecutionResultError("sample")
|
145
|
+
|
146
|
+
result = results[0].value
|
147
|
+
if isinstance(result, ExecutionDetails):
|
148
|
+
return result
|
149
|
+
if isinstance(result, MultipleExecutionDetails) and len(result.details) == 1:
|
150
|
+
return result.details[0]
|
151
|
+
raise ClassiqExecutionResultError("sample")
|
152
|
+
|
153
|
+
def get_batch_sample_result(self) -> List[ExecutionDetails]:
|
154
|
+
"""
|
155
|
+
Returns the job's result as a single batch_sample result after validation. If the result is not yet available, waits for it.
|
156
|
+
|
157
|
+
Returns:
|
158
|
+
The batch_sample result of the execution job.
|
159
|
+
|
160
|
+
Raises:
|
161
|
+
ClassiqExecutionResultError: In case the result does not contain a single batch_sample result.
|
162
|
+
ClassiqAPIError: In case the job has failed.
|
163
|
+
"""
|
164
|
+
results = self.result()
|
165
|
+
if len(results) != 1 or not isinstance(
|
166
|
+
results[0].value, MultipleExecutionDetails
|
167
|
+
):
|
168
|
+
raise ClassiqExecutionResultError("batch_sample")
|
169
|
+
return results[0].value.details
|
170
|
+
|
171
|
+
def get_estimate_result(self) -> EstimationResult:
|
172
|
+
"""
|
173
|
+
Returns the job's result as a single estimate result after validation. If the result is not yet available, waits for it.
|
174
|
+
|
175
|
+
Returns:
|
176
|
+
The estimate result of the execution job.
|
177
|
+
|
178
|
+
Raises:
|
179
|
+
ClassiqExecutionResultError: In case the result does not contain a single estimate result.
|
180
|
+
ClassiqAPIError: In case the job has failed.
|
181
|
+
"""
|
182
|
+
results = self.result()
|
183
|
+
if len(results) != 1:
|
184
|
+
raise ClassiqExecutionResultError("estimate")
|
185
|
+
|
186
|
+
result = results[0].value
|
187
|
+
if isinstance(result, EstimationResult):
|
188
|
+
return result
|
189
|
+
if isinstance(result, EstimationResults) and len(result.results) == 1:
|
190
|
+
return result.results[0]
|
191
|
+
raise ClassiqExecutionResultError("estimate")
|
192
|
+
|
193
|
+
def get_batch_estimate_result(self) -> List[EstimationResult]:
|
194
|
+
"""
|
195
|
+
Returns the job's result as a single batch_estimate result after validation. If the result is not yet available, waits for it.
|
196
|
+
|
197
|
+
Returns:
|
198
|
+
The batch_estimate result of the execution job.
|
199
|
+
|
200
|
+
Raises:
|
201
|
+
ClassiqExecutionResultError: In case the result does not contain a single batch_estimate result.
|
202
|
+
ClassiqAPIError: In case the job has failed.
|
203
|
+
"""
|
204
|
+
results = self.result()
|
205
|
+
if len(results) != 1 or not isinstance(results[0].value, EstimationResults):
|
206
|
+
raise ClassiqExecutionResultError("batch_estimate")
|
207
|
+
return results[0].value.results
|
208
|
+
|
115
209
|
async def poll_async(self, timeout_sec: Optional[float] = None) -> None:
|
116
210
|
if not self.status.is_final():
|
117
211
|
await self._poll_job(timeout_sec=timeout_sec)
|
@@ -141,6 +235,19 @@ class ExecutionJob:
|
|
141
235
|
|
142
236
|
rename = syncify_function(rename_async)
|
143
237
|
|
238
|
+
async def cancel_async(self) -> None:
|
239
|
+
"""
|
240
|
+
Cancels the execution job. This implies the cancellation of any ongoing jobs
|
241
|
+
sent to the provider during this execution job.
|
242
|
+
|
243
|
+
The function returns without waiting to the actual cancellation. It is possible
|
244
|
+
to continue polling the job in order to ensure its cancellation, which might
|
245
|
+
not be immediate.
|
246
|
+
"""
|
247
|
+
await ApiWrapper.call_cancel_execution_job(self._job_id)
|
248
|
+
|
249
|
+
cancel = syncify_function(cancel_async)
|
250
|
+
|
144
251
|
@property
|
145
252
|
def ide_url(self) -> str:
|
146
253
|
base_url = client().config.ide
|
classiq/interface/_version.py
CHANGED
@@ -68,7 +68,6 @@ class AzureQuantumBackendNames(StrEnum):
|
|
68
68
|
IONQ_SIMULATOR = "ionq.simulator"
|
69
69
|
MICROSOFT_ESTIMATOR = "microsoft.estimator"
|
70
70
|
MICROSOFT_FULLSTATE_SIMULATOR = "microsoft.simulator.fullstate"
|
71
|
-
RIGETTI_ASPEN3 = "rigetti.qpu.aspen-m-3"
|
72
71
|
RIGETTI_SIMULATOR = "rigetti.sim.qvm"
|
73
72
|
RIGETTI_ANKAA2 = "rigetti.qpu.ankaa-2"
|
74
73
|
RIGETTI_ANKAA9 = "rigetti.qpu.ankaa-9q-1"
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import json
|
2
|
-
from typing import Any, Dict, Optional, Union
|
2
|
+
from typing import Any, Dict, Mapping, Optional, Tuple, Union
|
3
3
|
from uuid import UUID
|
4
4
|
|
5
5
|
from pydantic import BaseModel, Field
|
@@ -14,10 +14,12 @@ ParameterValue = Union[float, int, str, None]
|
|
14
14
|
|
15
15
|
class FunctionDebugInfo(BaseModel):
|
16
16
|
name: str
|
17
|
+
# Parameters describe classical parameters passed to function
|
17
18
|
# ParameterValue appears in type only for backwards compatibility
|
18
19
|
parameters: Dict[str, str] = Field(type=Dict[str, ParameterValue])
|
19
20
|
level: OperationLevel
|
20
21
|
is_allocate_or_free: bool = Field(default=False)
|
22
|
+
port_to_passed_variable_map: Dict[str, str] = Field(default_factory=dict)
|
21
23
|
|
22
24
|
@staticmethod
|
23
25
|
def param_controller(value: Any) -> str:
|
@@ -26,6 +28,26 @@ class FunctionDebugInfo(BaseModel):
|
|
26
28
|
except TypeError:
|
27
29
|
return repr(value)
|
28
30
|
|
31
|
+
def update_map_from_port_mapping(self, port_mapping: Mapping[str, str]) -> None:
|
32
|
+
new_port_to_passed_variable_map = self.port_to_passed_variable_map.copy()
|
33
|
+
for old_key, new_key in port_mapping.items():
|
34
|
+
if old_key in new_port_to_passed_variable_map:
|
35
|
+
new_port_to_passed_variable_map[new_key] = (
|
36
|
+
new_port_to_passed_variable_map.pop(old_key)
|
37
|
+
)
|
38
|
+
self.port_to_passed_variable_map = new_port_to_passed_variable_map
|
39
|
+
|
40
|
+
def update_map_from_inout_port_mapping(
|
41
|
+
self, port_mapping: Mapping[str, Tuple[str, str]]
|
42
|
+
) -> None:
|
43
|
+
new_port_to_passed_variable_map = self.port_to_passed_variable_map.copy()
|
44
|
+
for old_key, (new_key1, new_key2) in port_mapping.items():
|
45
|
+
if old_key in new_port_to_passed_variable_map:
|
46
|
+
value = new_port_to_passed_variable_map.pop(old_key)
|
47
|
+
new_port_to_passed_variable_map[new_key1] = value
|
48
|
+
new_port_to_passed_variable_map[new_key2] = value
|
49
|
+
self.port_to_passed_variable_map = new_port_to_passed_variable_map
|
50
|
+
|
29
51
|
|
30
52
|
class DebugInfoCollection(BaseModel):
|
31
53
|
# Pydantic only started supporting UUID as keys in Pydantic V2
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import abc
|
2
|
-
from typing import ClassVar, Iterable, Optional, Tuple
|
2
|
+
from typing import ClassVar, Final, Iterable, Optional, Tuple
|
3
3
|
|
4
4
|
import pydantic
|
5
5
|
|
@@ -9,7 +9,10 @@ from classiq.interface.generator.arith.machine_precision import (
|
|
9
9
|
from classiq.interface.generator.arith.register_user_input import RegisterArithmeticInfo
|
10
10
|
from classiq.interface.generator.function_params import FunctionParams
|
11
11
|
|
12
|
-
DEFAULT_GARBAGE_OUT_NAME: str = "extra_qubits"
|
12
|
+
DEFAULT_GARBAGE_OUT_NAME: Final[str] = "extra_qubits"
|
13
|
+
MODULO_WITH_FRACTION_PLACES_ERROR_MSG: Final[str] = (
|
14
|
+
"Modulo with fraction places not supported"
|
15
|
+
)
|
13
16
|
|
14
17
|
|
15
18
|
class ArithmeticOperationParams(FunctionParams):
|
@@ -21,6 +21,7 @@ from classiq.interface.exceptions import ClassiqValueError
|
|
21
21
|
from classiq.interface.generator.arith import argument_utils, number_utils
|
22
22
|
from classiq.interface.generator.arith.argument_utils import RegisterOrConst
|
23
23
|
from classiq.interface.generator.arith.arithmetic_operations import (
|
24
|
+
MODULO_WITH_FRACTION_PLACES_ERROR_MSG,
|
24
25
|
ArithmeticOperationParams,
|
25
26
|
)
|
26
27
|
from classiq.interface.generator.arith.ast_node_rewrite import (
|
@@ -391,17 +392,20 @@ class Subtractor(InplacableBinaryOpParams[RegisterOrConst, RegisterOrConst]):
|
|
391
392
|
arg=self.effective_right_arg,
|
392
393
|
output_size=self.negation_output_size,
|
393
394
|
inplace=self.should_inplace_negation,
|
395
|
+
bypass_bounds_validation=True,
|
394
396
|
)
|
395
397
|
negation_result = negation_params.result_register
|
396
398
|
if self.output_size is None and max(self.effective_right_arg.bounds) > 0:
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
399
|
+
bounds = (
|
400
|
+
-max(self.effective_right_arg.bounds),
|
401
|
+
-min(self.effective_right_arg.bounds),
|
402
|
+
)
|
403
|
+
negation_result = RegisterArithmeticInfo(
|
404
|
+
size=negation_result.size,
|
405
|
+
fraction_places=negation_result.fraction_places,
|
406
|
+
is_signed=True,
|
407
|
+
bounds=bounds,
|
408
|
+
bypass_bounds_validation=True,
|
405
409
|
)
|
406
410
|
adder_params = Adder(
|
407
411
|
left_arg=self.effective_left_arg,
|
@@ -465,12 +469,8 @@ class Multiplier(BinaryOpWithFloatInputs):
|
|
465
469
|
for right in argument_utils.bounds(args[1])
|
466
470
|
]
|
467
471
|
return (
|
468
|
-
number_utils.limit_fraction_places(
|
469
|
-
|
470
|
-
),
|
471
|
-
number_utils.limit_fraction_places(
|
472
|
-
max(extremal_values), machine_precision=machine_precision
|
473
|
-
),
|
472
|
+
number_utils.limit_fraction_places(min(extremal_values), machine_precision),
|
473
|
+
number_utils.limit_fraction_places(max(extremal_values), machine_precision),
|
474
474
|
)
|
475
475
|
|
476
476
|
def _get_result_register(self) -> RegisterArithmeticInfo:
|
@@ -482,6 +482,13 @@ class Multiplier(BinaryOpWithFloatInputs):
|
|
482
482
|
self.right_arg, self.machine_precision
|
483
483
|
)
|
484
484
|
bounds = self._get_bounds((left_arg, right_arg), self.machine_precision)
|
485
|
+
if self.output_size:
|
486
|
+
if fraction_places:
|
487
|
+
raise ValueError(MODULO_WITH_FRACTION_PLACES_ERROR_MSG)
|
488
|
+
max_bounds = RegisterArithmeticInfo.get_maximal_bounds(
|
489
|
+
size=self.output_size, is_signed=False, fraction_places=0
|
490
|
+
)
|
491
|
+
bounds = number_utils.bounds_cut(bounds, max_bounds)
|
485
492
|
|
486
493
|
return RegisterArithmeticInfo(
|
487
494
|
size=self.output_size
|
@@ -4,9 +4,10 @@ from typing import Any, Dict, Iterable
|
|
4
4
|
import pydantic
|
5
5
|
|
6
6
|
from classiq.interface.exceptions import ClassiqValueError
|
7
|
-
from classiq.interface.generator.arith import argument_utils
|
7
|
+
from classiq.interface.generator.arith import argument_utils, number_utils
|
8
8
|
from classiq.interface.generator.arith.argument_utils import RegisterOrConst
|
9
9
|
from classiq.interface.generator.arith.arithmetic_operations import (
|
10
|
+
MODULO_WITH_FRACTION_PLACES_ERROR_MSG,
|
10
11
|
ArithmeticOperationParams,
|
11
12
|
)
|
12
13
|
from classiq.interface.generator.arith.binary_ops import (
|
@@ -108,6 +109,13 @@ class Extremum(ArithmeticOperationParams):
|
|
108
109
|
argument_utils.upper_bound(eff_right_arg),
|
109
110
|
),
|
110
111
|
)
|
112
|
+
if self.output_size:
|
113
|
+
if fraction_places:
|
114
|
+
raise ValueError(MODULO_WITH_FRACTION_PLACES_ERROR_MSG)
|
115
|
+
max_bounds = RegisterArithmeticInfo.get_maximal_bounds(
|
116
|
+
size=self.output_size, is_signed=False, fraction_places=0
|
117
|
+
)
|
118
|
+
bounds = number_utils.bounds_cut(bounds, max_bounds)
|
111
119
|
return RegisterArithmeticInfo(
|
112
120
|
size=self.output_size or required_size,
|
113
121
|
fraction_places=fraction_places,
|
@@ -110,3 +110,9 @@ def limit_fraction_places(number: float, machine_precision: int) -> float:
|
|
110
110
|
fraction_part_size=orig_fractions - removed_fractions,
|
111
111
|
is_signed=number < 0,
|
112
112
|
)
|
113
|
+
|
114
|
+
|
115
|
+
def bounds_cut(
|
116
|
+
bounds1: Tuple[float, float], bounds2: Tuple[float, float]
|
117
|
+
) -> Tuple[float, float]:
|
118
|
+
return max(min(bounds1), min(bounds2)), min(max(bounds1), max(bounds2))
|