classiq 0.61.0__py3-none-any.whl → 0.63.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/__init__.py +3 -0
- classiq/_internals/api_wrapper.py +6 -26
- classiq/_internals/client.py +1 -9
- classiq/applications/chemistry/chemistry_model_constructor.py +1 -1
- classiq/applications/combinatorial_helpers/combinatorial_problem_utils.py +26 -8
- classiq/applications/combinatorial_helpers/optimization_model.py +13 -2
- classiq/applications/combinatorial_helpers/pyomo_utils.py +143 -13
- classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +1 -1
- classiq/applications/combinatorial_optimization/combinatorial_problem.py +58 -23
- classiq/applications/grover/grover_model_constructor.py +1 -1
- classiq/applications/libraries/qmci_library.py +2 -1
- classiq/execution/execution_session.py +66 -96
- classiq/execution/jobs.py +12 -10
- classiq/interface/_version.py +1 -1
- classiq/interface/backend/backend_preferences.py +26 -5
- classiq/interface/backend/pydantic_backend.py +1 -1
- classiq/interface/backend/quantum_backend_providers.py +3 -1
- classiq/interface/chemistry/operator.py +0 -204
- classiq/interface/execution/primitives.py +1 -0
- classiq/interface/generator/compiler_keywords.py +4 -0
- classiq/interface/generator/copy.py +47 -0
- classiq/interface/generator/function_param_list_without_self_reference.py +2 -0
- classiq/interface/generator/functions/type_name.py +6 -0
- classiq/interface/generator/generated_circuit_data.py +22 -7
- classiq/interface/generator/model/model.py +3 -0
- classiq/interface/generator/model/preferences/preferences.py +14 -1
- classiq/interface/generator/quantum_function_call.py +4 -2
- classiq/interface/generator/types/compilation_metadata.py +2 -1
- classiq/interface/model/handle_binding.py +50 -5
- classiq/interface/model/quantum_type.py +16 -0
- classiq/interface/server/routes.py +1 -3
- classiq/model_expansions/capturing/captured_vars.py +114 -28
- classiq/model_expansions/closure.py +25 -65
- classiq/model_expansions/function_builder.py +19 -9
- classiq/model_expansions/generative_functions.py +16 -2
- classiq/model_expansions/interpreter.py +110 -66
- classiq/model_expansions/model_tables.py +4 -0
- classiq/model_expansions/quantum_operations/call_emitter.py +83 -20
- classiq/model_expansions/quantum_operations/classicalif.py +1 -1
- classiq/model_expansions/quantum_operations/control.py +3 -10
- classiq/model_expansions/quantum_operations/emitter.py +3 -4
- classiq/model_expansions/quantum_operations/quantum_assignment_operation.py +1 -2
- classiq/model_expansions/quantum_operations/quantum_function_call.py +1 -1
- classiq/model_expansions/quantum_operations/repeat.py +4 -3
- classiq/model_expansions/quantum_operations/shallow_emitter.py +9 -3
- classiq/model_expansions/scope.py +9 -13
- classiq/model_expansions/scope_initialization.py +34 -25
- classiq/model_expansions/transformers/var_splitter.py +57 -7
- classiq/open_library/__init__.py +4 -0
- classiq/open_library/functions/__init__.py +130 -0
- classiq/{qmod/builtins → open_library}/functions/amplitude_estimation.py +2 -2
- classiq/{qmod/builtins → open_library}/functions/discrete_sine_cosine_transform.py +6 -4
- classiq/{qmod/builtins → open_library}/functions/grover.py +2 -2
- classiq/{qmod/builtins → open_library}/functions/linear_pauli_rotation.py +1 -1
- classiq/{qmod/builtins → open_library}/functions/modular_exponentiation.py +2 -2
- classiq/{qmod/builtins → open_library}/functions/qpe.py +2 -2
- classiq/{qmod/builtins → open_library}/functions/state_preparation.py +6 -149
- classiq/{qmod/builtins → open_library}/functions/swap_test.py +1 -1
- classiq/open_library/functions/utility_functions.py +81 -0
- classiq/{qmod/builtins → open_library}/functions/variational.py +1 -1
- classiq/qmod/builtins/functions/__init__.py +4 -130
- classiq/qmod/builtins/functions/allocation.py +150 -0
- classiq/qmod/builtins/functions/arithmetic.py +0 -34
- classiq/qmod/builtins/functions/operators.py +0 -6
- classiq/qmod/builtins/operations.py +19 -80
- classiq/qmod/create_model_function.py +8 -162
- classiq/qmod/generative.py +0 -16
- classiq/qmod/model_state_container.py +7 -0
- classiq/qmod/native/pretty_printer.py +10 -11
- classiq/qmod/pretty_print/pretty_printer.py +1 -1
- classiq/qmod/python_classical_type.py +1 -5
- classiq/qmod/qfunc.py +11 -12
- classiq/qmod/qmod_variable.py +1 -3
- classiq/qmod/quantum_expandable.py +23 -1
- classiq/qmod/quantum_function.py +69 -7
- {classiq-0.61.0.dist-info → classiq-0.63.0.dist-info}/METADATA +2 -1
- {classiq-0.61.0.dist-info → classiq-0.63.0.dist-info}/RECORD +82 -78
- classiq/qmod/builtins/functions/utility_functions.py +0 -43
- /classiq/{qmod/builtins → open_library}/functions/hea.py +0 -0
- /classiq/{qmod/builtins → open_library}/functions/qaoa_penalty.py +0 -0
- /classiq/{qmod/builtins → open_library}/functions/qft_functions.py +0 -0
- /classiq/{qmod/builtins → open_library}/functions/qsvt.py +0 -0
- {classiq-0.61.0.dist-info → classiq-0.63.0.dist-info}/WHEEL +0 -0
@@ -2,12 +2,12 @@ import json
|
|
2
2
|
import random
|
3
3
|
from functools import cached_property
|
4
4
|
from types import TracebackType
|
5
|
-
from typing import
|
5
|
+
from typing import Callable, Optional, Union, cast
|
6
6
|
|
7
7
|
import numpy as np
|
8
8
|
|
9
9
|
from classiq.interface.chemistry.operator import PauliOperator, pauli_integers_to_str
|
10
|
-
from classiq.interface.exceptions import ClassiqValueError
|
10
|
+
from classiq.interface.exceptions import ClassiqError, ClassiqValueError
|
11
11
|
from classiq.interface.execution.primitives import EstimateInput, PrimitivesInput
|
12
12
|
from classiq.interface.executor.execution_preferences import ExecutionPreferences
|
13
13
|
from classiq.interface.executor.result import (
|
@@ -15,15 +15,16 @@ from classiq.interface.executor.result import (
|
|
15
15
|
ExecutionDetails,
|
16
16
|
ParsedState,
|
17
17
|
)
|
18
|
+
from classiq.interface.generator.arith import number_utils
|
18
19
|
from classiq.interface.generator.functions.qmod_python_interface import QmodPyStruct
|
19
20
|
from classiq.interface.generator.quantum_program import QuantumProgram
|
21
|
+
from classiq.interface.model.quantum_type import QuantumBit, QuantumNumeric
|
20
22
|
|
21
23
|
from classiq._internals import async_utils
|
22
24
|
from classiq._internals.api_wrapper import ApiWrapper
|
23
25
|
from classiq._internals.client import client
|
24
26
|
from classiq.applications.combinatorial_helpers.pauli_helpers.pauli_utils import (
|
25
27
|
_pauli_dict_to_pauli_terms,
|
26
|
-
_pauli_terms_to_qmod,
|
27
28
|
)
|
28
29
|
from classiq.execution.jobs import ExecutionJob
|
29
30
|
from classiq.qmod.builtins import PauliTerm
|
@@ -42,16 +43,6 @@ ParsedExecutionParameters = Optional[
|
|
42
43
|
]
|
43
44
|
|
44
45
|
|
45
|
-
SAVE_RESULT = "\nsave({'result': result})\n"
|
46
|
-
|
47
|
-
|
48
|
-
class SupportedPrimitives:
|
49
|
-
SAMPLE = "sample"
|
50
|
-
BATCH_SAMPLE = "batch_sample"
|
51
|
-
ESTIMATE = "estimate"
|
52
|
-
BATCH_ESTIMATE = "batch_estimate"
|
53
|
-
|
54
|
-
|
55
46
|
def _deserialize_program(program: Program) -> QuantumProgram:
|
56
47
|
return (
|
57
48
|
program
|
@@ -67,10 +58,6 @@ def hamiltonian_to_pauli_terms(hamiltonian: Hamiltonian) -> list[PauliTerm]:
|
|
67
58
|
return _pauli_dict_to_pauli_terms(cast(list[QmodPyStruct], hamiltonian))
|
68
59
|
|
69
60
|
|
70
|
-
def to_hamiltonian_str(hamiltonian: Hamiltonian) -> str:
|
71
|
-
return _pauli_terms_to_qmod(hamiltonian_to_pauli_terms(hamiltonian))
|
72
|
-
|
73
|
-
|
74
61
|
def _hamiltonian_to_pauli_operator(hamiltonian: Hamiltonian) -> PauliOperator:
|
75
62
|
pauli_list = [
|
76
63
|
(
|
@@ -82,14 +69,6 @@ def _hamiltonian_to_pauli_operator(hamiltonian: Hamiltonian) -> PauliOperator:
|
|
82
69
|
return PauliOperator(pauli_list=pauli_list)
|
83
70
|
|
84
71
|
|
85
|
-
def serialize(
|
86
|
-
item: Union[float, int, tuple[int, ...], tuple[float, ...]]
|
87
|
-
) -> Union[str, list]:
|
88
|
-
if isinstance(item, tuple):
|
89
|
-
return list(item)
|
90
|
-
return str(item)
|
91
|
-
|
92
|
-
|
93
72
|
def parse_params(params: ExecutionParams) -> ParsedExecutionParams:
|
94
73
|
result = {}
|
95
74
|
for key, values in params.items():
|
@@ -104,48 +83,6 @@ def parse_params(params: ExecutionParams) -> ParsedExecutionParams:
|
|
104
83
|
return result
|
105
84
|
|
106
85
|
|
107
|
-
def format_parameters(execution_params: ExecutionParameters) -> str:
|
108
|
-
parsed_parameters: ParsedExecutionParameters = None
|
109
|
-
if execution_params is None:
|
110
|
-
return ""
|
111
|
-
if isinstance(execution_params, dict):
|
112
|
-
parsed_parameters = parse_params(execution_params)
|
113
|
-
|
114
|
-
elif isinstance(execution_params, list):
|
115
|
-
parsed_parameters = [
|
116
|
-
parse_params(ep) if isinstance(ep, dict) else ep for ep in execution_params
|
117
|
-
]
|
118
|
-
|
119
|
-
execution_params = cast(ExecutionParams, parsed_parameters)
|
120
|
-
return json.dumps(execution_params, default=serialize, indent=2)
|
121
|
-
|
122
|
-
|
123
|
-
def create_estimate_execution_code(operation: str, **kwargs: Any) -> str:
|
124
|
-
hamiltonian = kwargs.get("hamiltonian", "")
|
125
|
-
parameters = kwargs.get("parameters", "")
|
126
|
-
return f"\nresult = {operation}([{hamiltonian}], {parameters})" + SAVE_RESULT
|
127
|
-
|
128
|
-
|
129
|
-
def create_sample_execution_code(operation: str, **kwargs: Any) -> str:
|
130
|
-
parameters = kwargs.get("parameters", "")
|
131
|
-
return f"\nresult = {operation}({parameters})" + SAVE_RESULT
|
132
|
-
|
133
|
-
|
134
|
-
operation_handlers: dict[str, Callable[[str], str]] = {
|
135
|
-
"estimate": create_estimate_execution_code,
|
136
|
-
"batch_estimate": create_estimate_execution_code,
|
137
|
-
"sample": create_sample_execution_code,
|
138
|
-
"batch_sample": create_sample_execution_code,
|
139
|
-
}
|
140
|
-
|
141
|
-
|
142
|
-
def generate_code_snippet(operation: str, **kwargs: Any) -> str:
|
143
|
-
handler = operation_handlers.get(operation)
|
144
|
-
if handler:
|
145
|
-
return handler(operation, **kwargs)
|
146
|
-
raise ClassiqValueError(f"Unsupported operation type: {operation}")
|
147
|
-
|
148
|
-
|
149
86
|
class ExecutionSession:
|
150
87
|
"""
|
151
88
|
A session for executing a quantum program.
|
@@ -164,9 +101,8 @@ class ExecutionSession:
|
|
164
101
|
):
|
165
102
|
self.program: QuantumProgram = _deserialize_program(quantum_program)
|
166
103
|
self.update_execution_preferences(execution_preferences)
|
167
|
-
#
|
168
|
-
#
|
169
|
-
# because cmain is expected in some cases
|
104
|
+
# We never use classical_execution_code in ExecutionSession, and we don't want
|
105
|
+
# the conversion route to fail because cmain is expected in some cases
|
170
106
|
self.program.model.classical_execution_code = "dummy"
|
171
107
|
|
172
108
|
self._random_seed = self.program.model.execution_preferences.random_seed
|
@@ -197,13 +133,10 @@ class ExecutionSession:
|
|
197
133
|
ApiWrapper.call_convert_quantum_program(self.program, self._async_client)
|
198
134
|
)
|
199
135
|
|
200
|
-
def _execute(
|
201
|
-
|
202
|
-
) -> ExecutionJob:
|
203
|
-
execution_input = self._execution_input.copy()
|
204
|
-
execution_input["execution_preferences"]["random_seed"] = self._random_seed
|
136
|
+
def _execute(self, primitives_input: PrimitivesInput) -> ExecutionJob:
|
137
|
+
primitives_input.random_seed = self._random_seed
|
205
138
|
self._random_seed = self._rng.randint(0, 2**32 - 1)
|
206
|
-
execution_input
|
139
|
+
execution_input = self._execution_input.copy()
|
207
140
|
# The use of `model_dump_json` is necessary for complex numbers serialization
|
208
141
|
execution_input["primitives_input"] = json.loads(
|
209
142
|
primitives_input.model_dump_json()
|
@@ -256,13 +189,10 @@ class ExecutionSession:
|
|
256
189
|
Returns:
|
257
190
|
The execution job.
|
258
191
|
"""
|
259
|
-
classical_execution_code = generate_code_snippet(
|
260
|
-
SupportedPrimitives.SAMPLE, parameters=format_parameters(parameters)
|
261
|
-
)
|
262
192
|
execution_primitives_input = PrimitivesInput(
|
263
193
|
sample=[parse_params(parameters)] if parameters is not None else [{}]
|
264
194
|
)
|
265
|
-
return self._execute(
|
195
|
+
return self._execute(execution_primitives_input)
|
266
196
|
|
267
197
|
def batch_sample(self, parameters: list[ExecutionParams]) -> list[ExecutionDetails]:
|
268
198
|
"""
|
@@ -290,13 +220,10 @@ class ExecutionSession:
|
|
290
220
|
Returns:
|
291
221
|
The execution job.
|
292
222
|
"""
|
293
|
-
classical_execution_code = generate_code_snippet(
|
294
|
-
SupportedPrimitives.BATCH_SAMPLE, parameters=format_parameters(parameters)
|
295
|
-
)
|
296
223
|
execution_primitives_input = PrimitivesInput(
|
297
224
|
sample=[parse_params(params) for params in parameters]
|
298
225
|
)
|
299
|
-
return self._execute(
|
226
|
+
return self._execute(execution_primitives_input)
|
300
227
|
|
301
228
|
def estimate(
|
302
229
|
self, hamiltonian: Hamiltonian, parameters: Optional[ExecutionParams] = None
|
@@ -330,11 +257,6 @@ class ExecutionSession:
|
|
330
257
|
Returns:
|
331
258
|
The execution job.
|
332
259
|
"""
|
333
|
-
classical_execution_code = generate_code_snippet(
|
334
|
-
SupportedPrimitives.ESTIMATE,
|
335
|
-
parameters=format_parameters(parameters),
|
336
|
-
hamiltonian=to_hamiltonian_str(hamiltonian),
|
337
|
-
)
|
338
260
|
execution_primitives_input = PrimitivesInput(
|
339
261
|
estimate=EstimateInput(
|
340
262
|
hamiltonian=_hamiltonian_to_pauli_operator(hamiltonian),
|
@@ -343,7 +265,7 @@ class ExecutionSession:
|
|
343
265
|
),
|
344
266
|
)
|
345
267
|
)
|
346
|
-
return self._execute(
|
268
|
+
return self._execute(execution_primitives_input)
|
347
269
|
|
348
270
|
def batch_estimate(
|
349
271
|
self, hamiltonian: Hamiltonian, parameters: list[ExecutionParams]
|
@@ -377,18 +299,13 @@ class ExecutionSession:
|
|
377
299
|
Returns:
|
378
300
|
The execution job.
|
379
301
|
"""
|
380
|
-
classical_execution_code = generate_code_snippet(
|
381
|
-
SupportedPrimitives.BATCH_ESTIMATE,
|
382
|
-
parameters=format_parameters(parameters),
|
383
|
-
hamiltonian=to_hamiltonian_str(hamiltonian),
|
384
|
-
)
|
385
302
|
execution_primitives_input = PrimitivesInput(
|
386
303
|
estimate=EstimateInput(
|
387
304
|
hamiltonian=_hamiltonian_to_pauli_operator(hamiltonian),
|
388
305
|
parameters=[parse_params(params) for params in parameters],
|
389
306
|
)
|
390
307
|
)
|
391
|
-
return self._execute(
|
308
|
+
return self._execute(execution_primitives_input)
|
392
309
|
|
393
310
|
def estimate_cost(
|
394
311
|
self,
|
@@ -427,3 +344,56 @@ class ExecutionSession:
|
|
427
344
|
if costs.size == 0:
|
428
345
|
return np.nan
|
429
346
|
return float(np.average(costs))
|
347
|
+
|
348
|
+
def set_measured_state_filter(
|
349
|
+
self,
|
350
|
+
output_name: str,
|
351
|
+
condition: Callable,
|
352
|
+
) -> None:
|
353
|
+
"""
|
354
|
+
EXPERIMENTAL
|
355
|
+
|
356
|
+
When simulating on a statevector simulator, emulate the behavior of postprocessing
|
357
|
+
by discarding amplitudes for which their states are "undesirable".
|
358
|
+
|
359
|
+
Args:
|
360
|
+
output_name: The name of the register to filter
|
361
|
+
condition: Filter out values of the statevector for which this callable is False
|
362
|
+
"""
|
363
|
+
if "_execution_input" in self.__dict__:
|
364
|
+
raise ClassiqError(
|
365
|
+
"set_measured_state_filter must be called before use of the first primitive (sample, estimate...)"
|
366
|
+
)
|
367
|
+
|
368
|
+
if output_name not in self.program.model.circuit_outputs:
|
369
|
+
raise ClassiqValueError(f"{output_name} is not an output of the model")
|
370
|
+
output_type = self.program.model.circuit_output_types[output_name].quantum_types
|
371
|
+
|
372
|
+
legal_bitstrings = []
|
373
|
+
|
374
|
+
if isinstance(output_type, QuantumBit):
|
375
|
+
if condition(0):
|
376
|
+
legal_bitstrings.append("0")
|
377
|
+
if condition(1):
|
378
|
+
legal_bitstrings.append("1")
|
379
|
+
elif isinstance(output_type, QuantumNumeric):
|
380
|
+
size = output_type.size_in_bits
|
381
|
+
for i in range(2**size):
|
382
|
+
number_string = f"{i:0{size}b}"
|
383
|
+
val = number_utils.binary_to_float_or_int(
|
384
|
+
number_string,
|
385
|
+
output_type.fraction_digits_value,
|
386
|
+
output_type.sign_value,
|
387
|
+
)
|
388
|
+
if condition(val):
|
389
|
+
legal_bitstrings.append(number_string)
|
390
|
+
if len(legal_bitstrings) > 1:
|
391
|
+
raise NotImplementedError(
|
392
|
+
"Filtering is only supported on a single value per model output"
|
393
|
+
)
|
394
|
+
else:
|
395
|
+
raise NotImplementedError(
|
396
|
+
"Filtering is only supported on QuantumBit and QuantumNumeric"
|
397
|
+
)
|
398
|
+
|
399
|
+
self.program.model.register_filter_bitstrings[output_name] = legal_bitstrings
|
classiq/execution/jobs.py
CHANGED
@@ -19,18 +19,15 @@ from classiq.interface.executor.result import (
|
|
19
19
|
MultipleExecutionDetails,
|
20
20
|
)
|
21
21
|
from classiq.interface.jobs import JobStatus, JSONObject
|
22
|
-
from classiq.interface.server.routes import
|
22
|
+
from classiq.interface.server.routes import EXECUTION_JOBS_FULL_PATH
|
23
23
|
|
24
|
-
from classiq._internals.api_wrapper import
|
24
|
+
from classiq._internals.api_wrapper import ApiWrapper
|
25
25
|
from classiq._internals.async_utils import syncify_function
|
26
26
|
from classiq._internals.client import client
|
27
27
|
from classiq._internals.jobs import JobID, JobPoller
|
28
28
|
|
29
29
|
_JobDetails = Union[ExecutionJobDetails, ExecutionJobDetailsV1]
|
30
30
|
|
31
|
-
_JOB_DETAILS_VERSION = "v1"
|
32
|
-
_JOB_RESULT_VERSION = "v1"
|
33
|
-
|
34
31
|
|
35
32
|
class ClassiqExecutionResultError(ClassiqError):
|
36
33
|
def __init__(self, primitive: str) -> None:
|
@@ -112,7 +109,15 @@ class ExecutionJob:
|
|
112
109
|
)
|
113
110
|
return cls(details)
|
114
111
|
|
115
|
-
|
112
|
+
# `syncify_function` doesn't work well for class methods, so I wrote `from_id`
|
113
|
+
# explicitly
|
114
|
+
@classmethod
|
115
|
+
def from_id(
|
116
|
+
cls,
|
117
|
+
id: str,
|
118
|
+
_http_client: Optional[httpx.AsyncClient] = None,
|
119
|
+
) -> "ExecutionJob":
|
120
|
+
return syncify_function(cls.from_id_async)(id, _http_client=_http_client)
|
116
121
|
|
117
122
|
@property
|
118
123
|
def _job_id(self) -> JobID:
|
@@ -134,7 +139,6 @@ class ExecutionJob:
|
|
134
139
|
self._result = (
|
135
140
|
await ApiWrapper.call_get_execution_job_result(
|
136
141
|
job_id=self._job_id,
|
137
|
-
version=_JOB_RESULT_VERSION,
|
138
142
|
http_client=_http_client,
|
139
143
|
)
|
140
144
|
).results
|
@@ -265,9 +269,7 @@ class ExecutionJob:
|
|
265
269
|
return None
|
266
270
|
|
267
271
|
poller = JobPoller(
|
268
|
-
base_url=
|
269
|
-
use_versioned_url=False,
|
270
|
-
additional_headers={CLASSIQ_ACCEPT_HEADER: _JOB_DETAILS_VERSION},
|
272
|
+
base_url=EXECUTION_JOBS_FULL_PATH,
|
271
273
|
)
|
272
274
|
await poller.poll(
|
273
275
|
job_id=self._job_id,
|
classiq/interface/_version.py
CHANGED
@@ -161,6 +161,20 @@ class ClassiqBackendPreferences(BackendPreferences):
|
|
161
161
|
def is_nvidia_backend(self) -> bool:
|
162
162
|
return self.backend_name in list(ClassiqNvidiaBackendNames)
|
163
163
|
|
164
|
+
# CAD-25390
|
165
|
+
@pydantic.field_validator("backend_name")
|
166
|
+
@classmethod
|
167
|
+
def _validate_nvidia_name_backwards_compatibility(cls, backend_name: str) -> str:
|
168
|
+
if backend_name == "nvidia_state_vector_simulator":
|
169
|
+
warnings.warn(
|
170
|
+
"The name 'nvidia_state_vector_simulator' is deprecated and "
|
171
|
+
"will be removed soon, no earlier than January 12th 2025. "
|
172
|
+
"Please use ClassiqNvidiaBackendNames.SIMULATOR instead",
|
173
|
+
ClassiqDeprecationWarning,
|
174
|
+
stacklevel=2,
|
175
|
+
)
|
176
|
+
return backend_name
|
177
|
+
|
164
178
|
|
165
179
|
class AwsBackendPreferences(BackendPreferences):
|
166
180
|
"""
|
@@ -196,12 +210,19 @@ class AwsBackendPreferences(BackendPreferences):
|
|
196
210
|
backend_service_provider: ProviderTypeVendor.AMAZON_BRAKET = pydantic.Field(
|
197
211
|
default=ProviderVendor.AMAZON_BRAKET
|
198
212
|
)
|
199
|
-
aws_role_arn:
|
200
|
-
|
213
|
+
aws_role_arn: Optional[str] = pydantic.Field(
|
214
|
+
default=None,
|
215
|
+
description="ARN of the role to be assumed for execution on your Braket account.",
|
216
|
+
)
|
217
|
+
s3_bucket_name: Optional[str] = pydantic.Field(
|
218
|
+
default=None, description="S3 Bucket Name"
|
219
|
+
)
|
220
|
+
s3_folder: Optional[str] = pydantic.Field(
|
221
|
+
default=None, description="S3 Folder Path Within The S3 Bucket"
|
201
222
|
)
|
202
|
-
|
203
|
-
|
204
|
-
description="
|
223
|
+
run_through_classiq: bool = pydantic.Field(
|
224
|
+
default=False,
|
225
|
+
description="Run through Classiq's credentials while using user's allocated budget.",
|
205
226
|
)
|
206
227
|
|
207
228
|
@pydantic.field_validator("s3_bucket_name", mode="before")
|
@@ -8,7 +8,7 @@ _IONQ_API_KEY_LENGTH: int = 32
|
|
8
8
|
_ALICE_BOB_API_KEY_LENGTH: int = 72
|
9
9
|
INVALID_API_KEY: str = _IONQ_API_KEY_LENGTH * "a"
|
10
10
|
INVALID_EMAIL_OQC: str = "aa@aa.aa"
|
11
|
-
INVALID_PASSWORD_OQC: str = "Aa1!Aa1!"
|
11
|
+
INVALID_PASSWORD_OQC: str = "Aa1!Aa1!" # noqa: S105
|
12
12
|
|
13
13
|
EXECUTION_PARAMETER_PATTERN = "[_a-z][_a-z0-9]*"
|
14
14
|
|
@@ -170,7 +170,8 @@ class ClassiqNvidiaBackendNames(StrEnum):
|
|
170
170
|
Classiq's Nvidia simulator backend names.
|
171
171
|
"""
|
172
172
|
|
173
|
-
SIMULATOR = "
|
173
|
+
SIMULATOR = "nvidia_simulator"
|
174
|
+
SIMULATOR_STATEVECTOR = "nvidia_simulator_statevector"
|
174
175
|
|
175
176
|
|
176
177
|
class IntelBackendNames(StrEnum):
|
@@ -186,6 +187,7 @@ class GoogleNvidiaBackendNames(StrEnum):
|
|
186
187
|
"""
|
187
188
|
|
188
189
|
CUQUANTUM = "cuquantum"
|
190
|
+
CUQUANTUM_STATEVECTOR = "cuquantum_statevector"
|
189
191
|
|
190
192
|
|
191
193
|
class AliceBobBackendNames(StrEnum):
|
@@ -260,210 +260,6 @@ class PauliOperator(HashablePydanticBaseModel, VersionedModel):
|
|
260
260
|
model_config = ConfigDict(frozen=True)
|
261
261
|
|
262
262
|
|
263
|
-
class PauliOperatorV1(HashablePydanticBaseModel):
|
264
|
-
"""
|
265
|
-
Specification of a Pauli sum operator.
|
266
|
-
"""
|
267
|
-
|
268
|
-
pauli_list: PydanticPauliList = pydantic.Field(
|
269
|
-
description="A list of tuples each containing a pauli string comprised of I,X,Y,Z characters and a complex coefficient; for example [('IZ', 0.1), ('XY', 0.2)].",
|
270
|
-
)
|
271
|
-
is_hermitian: bool = pydantic.Field(default=False)
|
272
|
-
has_complex_coefficients: bool = pydantic.Field(default=True)
|
273
|
-
|
274
|
-
def show(self) -> str:
|
275
|
-
if self.is_hermitian:
|
276
|
-
# If the operator is hermitian then the coefficients must be numeric
|
277
|
-
return "\n".join(
|
278
|
-
f"{summand[1].real:+.3f} * {summand[0]}" for summand in self.pauli_list # type: ignore[union-attr]
|
279
|
-
)
|
280
|
-
return "\n".join(
|
281
|
-
f"+({summand[1]:+.3f}) * {summand[0]}" for summand in self.pauli_list
|
282
|
-
)
|
283
|
-
|
284
|
-
@pydantic.field_validator("pauli_list", mode="before")
|
285
|
-
@classmethod
|
286
|
-
def _validate_pauli_monomials(
|
287
|
-
cls, pauli_list: PydanticPauliList
|
288
|
-
) -> PydanticPauliList:
|
289
|
-
validated_pauli_list = []
|
290
|
-
for monomial in pauli_list:
|
291
|
-
# Validate the length
|
292
|
-
_PauliMonomialLengthValidator(monomial=monomial) # type: ignore[call-arg]
|
293
|
-
coeff = cls._validate_monomial_coefficient(monomial[1])
|
294
|
-
parsed_monomial = _PauliMonomialParser(string=monomial[0], coeff=coeff)
|
295
|
-
validated_pauli_list.append((parsed_monomial.string, parsed_monomial.coeff))
|
296
|
-
return validated_pauli_list
|
297
|
-
|
298
|
-
@staticmethod
|
299
|
-
def _validate_monomial_coefficient(
|
300
|
-
coeff: Union[sympy.Expr, ParameterComplexType]
|
301
|
-
) -> ParameterComplexType:
|
302
|
-
if isinstance(coeff, str):
|
303
|
-
validate_expression_str(coeff)
|
304
|
-
elif isinstance(coeff, sympy.Expr):
|
305
|
-
coeff = str(coeff)
|
306
|
-
return coeff
|
307
|
-
|
308
|
-
@pydantic.field_validator("pauli_list", mode="after")
|
309
|
-
@classmethod
|
310
|
-
def _validate_pauli_list(cls, pauli_list: PydanticPauliList) -> PydanticPauliList:
|
311
|
-
if not all_equal(len(summand[0]) for summand in pauli_list):
|
312
|
-
raise ClassiqValueError("Pauli strings have incompatible lengths.")
|
313
|
-
return pauli_list
|
314
|
-
|
315
|
-
@pydantic.model_validator(mode="before")
|
316
|
-
@classmethod
|
317
|
-
def _validate_hermitianity(cls, v: dict[str, Any]) -> dict[str, Any]:
|
318
|
-
pauli_list = cast(PydanticPauliList, v.get("pauli_list"))
|
319
|
-
if all(isinstance(summand[1], complex) for summand in pauli_list):
|
320
|
-
v["is_hermitian"] = all(
|
321
|
-
np.isclose(complex(summand[1]).real, summand[1])
|
322
|
-
for summand in pauli_list
|
323
|
-
)
|
324
|
-
if v["is_hermitian"]:
|
325
|
-
v["has_complex_coefficients"] = False
|
326
|
-
v["pauli_list"] = [
|
327
|
-
(summand[0], complex(summand[1]).real) for summand in pauli_list
|
328
|
-
]
|
329
|
-
else:
|
330
|
-
v["has_complex_coefficients"] = not all(
|
331
|
-
np.isclose(complex(summand[1]).real, summand[1])
|
332
|
-
for summand in pauli_list
|
333
|
-
if isinstance(summand[1], complex)
|
334
|
-
)
|
335
|
-
return v
|
336
|
-
|
337
|
-
def __mul__(self, coefficient: complex) -> "PauliOperatorV1":
|
338
|
-
multiplied_ising = [
|
339
|
-
(monomial[0], self._multiply_monomial_coefficient(monomial[1], coefficient))
|
340
|
-
for monomial in self.pauli_list
|
341
|
-
]
|
342
|
-
return self.__class__(pauli_list=multiplied_ising)
|
343
|
-
|
344
|
-
@staticmethod
|
345
|
-
def _multiply_monomial_coefficient(
|
346
|
-
monomial_coefficient: ParameterComplexType, coefficient: complex
|
347
|
-
) -> ParameterComplexType:
|
348
|
-
if isinstance(monomial_coefficient, ParameterType):
|
349
|
-
return str(sympy.sympify(monomial_coefficient) * coefficient)
|
350
|
-
return monomial_coefficient * coefficient
|
351
|
-
|
352
|
-
@property
|
353
|
-
def is_commutative(self) -> bool:
|
354
|
-
return all(
|
355
|
-
self._is_sub_pauli_commutative(
|
356
|
-
[summand[0][qubit_num] for summand in self.pauli_list]
|
357
|
-
)
|
358
|
-
for qubit_num in range(self.num_qubits)
|
359
|
-
)
|
360
|
-
|
361
|
-
@staticmethod
|
362
|
-
def _is_sub_pauli_commutative(qubit_pauli_string: Union[list[str], str]) -> bool:
|
363
|
-
unique_paulis = set(qubit_pauli_string) - {"I"}
|
364
|
-
return len(unique_paulis) <= 1
|
365
|
-
|
366
|
-
@property
|
367
|
-
def num_qubits(self) -> int:
|
368
|
-
return len(self.pauli_list[0][0])
|
369
|
-
|
370
|
-
def to_matrix(self) -> np.ndarray:
|
371
|
-
if not all(isinstance(summand[1], complex) for summand in self.pauli_list):
|
372
|
-
raise ClassiqValueError(
|
373
|
-
"Supporting only Hamiltonian with numeric coefficients."
|
374
|
-
)
|
375
|
-
return sum(
|
376
|
-
cast(complex, summand[1]) * to_pauli_matrix(summand[0])
|
377
|
-
for summand in self.pauli_list
|
378
|
-
) # type: ignore[return-value]
|
379
|
-
|
380
|
-
@staticmethod
|
381
|
-
def _extend_pauli_string(
|
382
|
-
pauli_string: PydanticPauliMonomialStr, num_extra_qubits: int
|
383
|
-
) -> PydanticPauliMonomialStr:
|
384
|
-
return "I" * num_extra_qubits + pauli_string
|
385
|
-
|
386
|
-
def extend(self, num_extra_qubits: int) -> "PauliOperatorV1":
|
387
|
-
new_pauli_list = [
|
388
|
-
(self._extend_pauli_string(pauli_string, num_extra_qubits), coeff)
|
389
|
-
for (pauli_string, coeff) in self.pauli_list
|
390
|
-
]
|
391
|
-
return self.model_copy(update={"pauli_list": new_pauli_list}, deep=True)
|
392
|
-
|
393
|
-
@staticmethod
|
394
|
-
def _reorder_pauli_string(
|
395
|
-
pauli_string: PydanticPauliMonomialStr,
|
396
|
-
order: Collection[int],
|
397
|
-
new_num_qubits: int,
|
398
|
-
) -> PydanticPauliMonomialStr:
|
399
|
-
reversed_pauli_string = pauli_string[::-1]
|
400
|
-
reversed_new_pauli_string = ["I"] * new_num_qubits
|
401
|
-
|
402
|
-
for logical_pos, actual_pos in enumerate(order):
|
403
|
-
reversed_new_pauli_string[actual_pos] = reversed_pauli_string[logical_pos]
|
404
|
-
|
405
|
-
return "".join(reversed(reversed_new_pauli_string))
|
406
|
-
|
407
|
-
@staticmethod
|
408
|
-
def _validate_reorder(
|
409
|
-
order: Collection[int],
|
410
|
-
num_qubits: int,
|
411
|
-
num_extra_qubits: int,
|
412
|
-
) -> None:
|
413
|
-
if num_extra_qubits < 0:
|
414
|
-
raise ClassiqValueError("Number of extra qubits cannot be negative")
|
415
|
-
|
416
|
-
if len(order) != num_qubits:
|
417
|
-
raise ClassiqValueError("The qubits order doesn't match the Pauli operator")
|
418
|
-
|
419
|
-
if len(order) != len(set(order)):
|
420
|
-
raise ClassiqValueError("The qubits order is not one-to-one")
|
421
|
-
|
422
|
-
if not all(pos < num_qubits + num_extra_qubits for pos in order):
|
423
|
-
raise ClassiqValueError(
|
424
|
-
"The qubits order contains qubits which do no exist"
|
425
|
-
)
|
426
|
-
|
427
|
-
@classmethod
|
428
|
-
def reorder(
|
429
|
-
cls,
|
430
|
-
operator: "PauliOperatorV1",
|
431
|
-
order: Collection[int],
|
432
|
-
num_extra_qubits: int = 0,
|
433
|
-
) -> "PauliOperatorV1":
|
434
|
-
cls._validate_reorder(order, operator.num_qubits, num_extra_qubits)
|
435
|
-
|
436
|
-
new_num_qubits = operator.num_qubits + num_extra_qubits
|
437
|
-
new_pauli_list = [
|
438
|
-
(cls._reorder_pauli_string(pauli_string, order, new_num_qubits), coeff)
|
439
|
-
for pauli_string, coeff in operator.pauli_list
|
440
|
-
]
|
441
|
-
return cls(pauli_list=new_pauli_list)
|
442
|
-
|
443
|
-
@classmethod
|
444
|
-
def from_unzipped_lists(
|
445
|
-
cls,
|
446
|
-
operators: list[list["Pauli"]],
|
447
|
-
coefficients: Optional[list[complex]] = None,
|
448
|
-
) -> "PauliOperatorV1":
|
449
|
-
if coefficients is None:
|
450
|
-
coefficients = [1] * len(operators)
|
451
|
-
|
452
|
-
if len(operators) != len(coefficients):
|
453
|
-
raise ClassiqValueError(
|
454
|
-
f"The number of coefficients ({len(coefficients)}) must be equal to the number of pauli operators ({len(operators)})"
|
455
|
-
)
|
456
|
-
|
457
|
-
return cls(
|
458
|
-
pauli_list=[
|
459
|
-
(pauli_integers_to_str(op), coeff)
|
460
|
-
for op, coeff in zip(operators, coefficients)
|
461
|
-
]
|
462
|
-
)
|
463
|
-
|
464
|
-
model_config = ConfigDict(frozen=True)
|
465
|
-
|
466
|
-
|
467
263
|
# This class validates the length of a monomial.
|
468
264
|
@pydantic.dataclasses.dataclass
|
469
265
|
class _PauliMonomialLengthValidator:
|
@@ -15,3 +15,4 @@ class EstimateInput(BaseModel, json_encoders=CUSTOM_ENCODERS):
|
|
15
15
|
class PrimitivesInput(BaseModel, json_encoders=CUSTOM_ENCODERS):
|
16
16
|
sample: Optional[list[Arguments]] = Field(default=None)
|
17
17
|
estimate: Optional[EstimateInput] = Field(default=None)
|
18
|
+
random_seed: Optional[int] = Field(default=None)
|