classiq 0.67.0__py3-none-any.whl → 0.69.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 +9 -9
- classiq/_internals/async_utils.py +1 -1
- classiq/_internals/authentication/password_manager.py +1 -1
- classiq/_internals/client.py +1 -1
- classiq/applications/combinatorial_optimization/combinatorial_problem.py +8 -11
- classiq/applications/qnn/gradients/quantum_gradient.py +1 -1
- classiq/applications/qnn/gradients/simple_quantum_gradient.py +1 -1
- classiq/applications/qnn/torch_utils.py +1 -1
- classiq/execution/execution_session.py +7 -3
- classiq/execution/jobs.py +2 -5
- classiq/executor.py +7 -2
- classiq/interface/_version.py +1 -1
- classiq/interface/ast_node.py +1 -1
- classiq/interface/backend/quantum_backend_providers.py +2 -3
- classiq/interface/chemistry/operator.py +12 -8
- classiq/interface/debug_info/back_ref_util.py +22 -0
- classiq/interface/debug_info/debug_info.py +26 -21
- classiq/interface/executor/optimizer_preferences.py +1 -0
- classiq/interface/generator/arith/arithmetic.py +96 -1
- classiq/interface/generator/arith/arithmetic_expression_parser.py +1 -1
- classiq/interface/generator/arith/arithmetic_param_getters.py +3 -3
- classiq/interface/generator/functions/classical_type.py +12 -1
- classiq/interface/generator/generated_circuit_data.py +64 -23
- classiq/interface/generator/quantum_program.py +18 -1
- classiq/interface/generator/types/builtin_enum_declarations.py +1 -0
- classiq/interface/generator/types/enum_declaration.py +45 -3
- classiq/interface/ide/visual_model.py +0 -2
- classiq/interface/model/classical_if.py +2 -2
- classiq/interface/model/control.py +2 -2
- classiq/interface/model/invert.py +2 -2
- classiq/interface/model/power.py +2 -2
- classiq/interface/model/quantum_function_call.py +4 -0
- classiq/interface/model/quantum_statement.py +1 -1
- classiq/interface/model/repeat.py +2 -2
- classiq/interface/model/statement_block.py +1 -1
- classiq/interface/model/within_apply_operation.py +2 -2
- classiq/interface/server/routes.py +0 -6
- classiq/model_expansions/generative_functions.py +4 -3
- classiq/model_expansions/interpreters/generative_interpreter.py +78 -18
- classiq/model_expansions/quantum_operations/allocate.py +3 -1
- classiq/model_expansions/quantum_operations/assignment_result_processor.py +52 -0
- classiq/model_expansions/quantum_operations/bind.py +2 -1
- classiq/model_expansions/quantum_operations/block_evaluator.py +76 -0
- classiq/model_expansions/quantum_operations/call_emitter.py +0 -13
- classiq/model_expansions/quantum_operations/classicalif.py +5 -4
- classiq/model_expansions/quantum_operations/composite_emitter.py +27 -0
- classiq/model_expansions/quantum_operations/emitter.py +16 -2
- classiq/model_expansions/quantum_operations/expression_evaluator.py +33 -0
- classiq/model_expansions/quantum_operations/handle_evaluator.py +28 -0
- classiq/model_expansions/quantum_operations/quantum_function_call.py +3 -2
- classiq/model_expansions/quantum_operations/repeat.py +2 -1
- classiq/model_expansions/quantum_operations/variable_decleration.py +2 -1
- classiq/model_expansions/scope_initialization.py +5 -19
- classiq/model_expansions/sympy_conversion/expression_to_sympy.py +1 -1
- classiq/open_library/functions/__init__.py +1 -2
- classiq/open_library/functions/amplitude_amplification.py +11 -12
- classiq/open_library/functions/discrete_sine_cosine_transform.py +17 -14
- classiq/open_library/functions/grover.py +7 -11
- classiq/open_library/functions/hea.py +3 -3
- classiq/open_library/functions/modular_exponentiation.py +17 -33
- classiq/open_library/functions/qft_functions.py +2 -2
- classiq/open_library/functions/qsvt.py +8 -8
- classiq/open_library/functions/state_preparation.py +16 -17
- classiq/open_library/functions/swap_test.py +1 -1
- classiq/open_library/functions/utility_functions.py +12 -4
- classiq/qmod/builtins/classical_functions.py +24 -7
- classiq/qmod/builtins/enums.py +1 -0
- classiq/qmod/builtins/functions/__init__.py +2 -0
- classiq/qmod/builtins/functions/chemistry.py +6 -38
- classiq/qmod/builtins/functions/exponentiation.py +24 -0
- classiq/qmod/builtins/operations.py +26 -11
- classiq/qmod/cparam.py +32 -5
- classiq/qmod/python_classical_type.py +10 -4
- classiq/qmod/quantum_callable.py +2 -1
- classiq/qmod/quantum_expandable.py +30 -6
- classiq/qmod/quantum_function.py +3 -2
- classiq/qmod/semantics/error_manager.py +1 -1
- classiq/qmod/semantics/validation/types_validation.py +1 -1
- classiq/qmod/symbolic.py +2 -1
- classiq/qmod/utilities.py +31 -2
- classiq/qmod/write_qmod.py +10 -7
- classiq/synthesis.py +25 -9
- {classiq-0.67.0.dist-info → classiq-0.69.0.dist-info}/METADATA +1 -1
- {classiq-0.67.0.dist-info → classiq-0.69.0.dist-info}/RECORD +85 -81
- classiq/interface/execution/jobs.py +0 -31
- classiq/model_expansions/quantum_operations/shallow_emitter.py +0 -166
- {classiq-0.67.0.dist-info → classiq-0.69.0.dist-info}/WHEEL +0 -0
@@ -3,6 +3,7 @@ from typing import Any, Optional, Protocol, TypeVar
|
|
3
3
|
|
4
4
|
import httpx
|
5
5
|
import pydantic
|
6
|
+
from pydantic.main import IncEx
|
6
7
|
|
7
8
|
import classiq.interface.executor.execution_result
|
8
9
|
import classiq.interface.pyomo_extension
|
@@ -21,10 +22,6 @@ from classiq.interface.execution.iqcc import (
|
|
21
22
|
IQCCProbeAuthData,
|
22
23
|
IQCCProbeAuthResponse,
|
23
24
|
)
|
24
|
-
from classiq.interface.execution.jobs import (
|
25
|
-
ExecutionJobDetailsV1,
|
26
|
-
ExecutionJobsQueryResultsV1,
|
27
|
-
)
|
28
25
|
from classiq.interface.execution.primitives import PrimitivesInput
|
29
26
|
from classiq.interface.executor import execution_request
|
30
27
|
from classiq.interface.generator import quantum_program as generator_result
|
@@ -72,11 +69,12 @@ class ApiWrapper:
|
|
72
69
|
model: pydantic.BaseModel,
|
73
70
|
use_versioned_url: bool = True,
|
74
71
|
http_client: Optional[httpx.AsyncClient] = None,
|
72
|
+
exclude: Optional[IncEx] = None,
|
75
73
|
) -> dict:
|
76
74
|
# TODO: we can't use model.dict() - it doesn't serialize complex class.
|
77
75
|
# This was added because JSON serializer doesn't serialize complex type, and pydantic does.
|
78
76
|
# We should add support for smarter json serialization.
|
79
|
-
body = json.loads(model.model_dump_json())
|
77
|
+
body = json.loads(model.model_dump_json(exclude=exclude))
|
80
78
|
return await cls._call_task(
|
81
79
|
http_method,
|
82
80
|
url,
|
@@ -135,6 +133,7 @@ class ApiWrapper:
|
|
135
133
|
url=routes.EXECUTION_SESSIONS_PREFIX,
|
136
134
|
model=circuit,
|
137
135
|
http_client=http_client,
|
136
|
+
exclude={"debug_info"},
|
138
137
|
)
|
139
138
|
return raw_result["id"]
|
140
139
|
|
@@ -164,6 +163,7 @@ class ApiWrapper:
|
|
164
163
|
url=routes.CONVERSION_GENERATED_CIRCUIT_TO_EXECUTION_INPUT_FULL,
|
165
164
|
model=circuit,
|
166
165
|
http_client=http_client,
|
166
|
+
exclude={"debug_info"},
|
167
167
|
)
|
168
168
|
|
169
169
|
@classmethod
|
@@ -214,7 +214,7 @@ class ApiWrapper:
|
|
214
214
|
job_id: JobID,
|
215
215
|
name: str,
|
216
216
|
http_client: Optional[httpx.AsyncClient] = None,
|
217
|
-
) ->
|
217
|
+
) -> execution_request.ExecutionJobDetails:
|
218
218
|
data = await cls._call_task(
|
219
219
|
http_method=HTTPMethod.PATCH,
|
220
220
|
url=f"{routes.EXECUTION_JOBS_FULL_PATH}/{job_id.job_id}",
|
@@ -223,7 +223,7 @@ class ApiWrapper:
|
|
223
223
|
},
|
224
224
|
http_client=http_client,
|
225
225
|
)
|
226
|
-
return
|
226
|
+
return execution_request.ExecutionJobDetails.model_validate(data)
|
227
227
|
|
228
228
|
@classmethod
|
229
229
|
async def call_cancel_execution_job(
|
@@ -244,7 +244,7 @@ class ApiWrapper:
|
|
244
244
|
offset: int,
|
245
245
|
limit: int,
|
246
246
|
http_client: Optional[httpx.AsyncClient] = None,
|
247
|
-
) ->
|
247
|
+
) -> execution_request.ExecutionJobsQueryResults:
|
248
248
|
data = await cls._call_task(
|
249
249
|
http_method=HTTPMethod.GET,
|
250
250
|
url=f"{routes.EXECUTION_JOBS_FULL_PATH}",
|
@@ -254,7 +254,7 @@ class ApiWrapper:
|
|
254
254
|
},
|
255
255
|
http_client=http_client,
|
256
256
|
)
|
257
|
-
return
|
257
|
+
return execution_request.ExecutionJobsQueryResults.model_validate(data)
|
258
258
|
|
259
259
|
@classmethod
|
260
260
|
async def call_analysis_task(
|
@@ -58,7 +58,7 @@ def enable_jupyter_notebook() -> None:
|
|
58
58
|
|
59
59
|
|
60
60
|
def _make_iterable_interval(
|
61
|
-
interval_sec: Union[SupportsFloat, Iterable[SupportsFloat]]
|
61
|
+
interval_sec: Union[SupportsFloat, Iterable[SupportsFloat]],
|
62
62
|
) -> Iterable[float]:
|
63
63
|
if isinstance(interval_sec, Iterable):
|
64
64
|
return map(float, interval_sec)
|
@@ -117,7 +117,7 @@ class DummyPasswordManager(PasswordManager):
|
|
117
117
|
|
118
118
|
class FilePasswordManager(PasswordManager):
|
119
119
|
_CLASSIQ_CREDENTIALS_FILE_PATH: str = "{}/.classiq-credentials".format(
|
120
|
-
os.getenv("HOME")
|
120
|
+
os.getenv("CLASSIQ_DIR", os.getenv("HOME"))
|
121
121
|
)
|
122
122
|
|
123
123
|
def __init__(self) -> None:
|
classiq/_internals/client.py
CHANGED
@@ -89,7 +89,7 @@ P = ParamSpec("P")
|
|
89
89
|
|
90
90
|
|
91
91
|
def try_again_on_failure(
|
92
|
-
func: Callable[P, Awaitable[Ret]]
|
92
|
+
func: Callable[P, Awaitable[Ret]],
|
93
93
|
) -> Callable[P, Awaitable[Ret]]:
|
94
94
|
def check_approved_api_error(error_message: str) -> bool:
|
95
95
|
for approved_api_error in APPROVED_API_ERROR_MESSAGES_FOR_RESTART:
|
@@ -24,7 +24,6 @@ from classiq.open_library.functions.utility_functions import (
|
|
24
24
|
from classiq.qmod.builtins.functions import RX
|
25
25
|
from classiq.qmod.builtins.operations import allocate, phase, repeat
|
26
26
|
from classiq.qmod.cparam import CReal
|
27
|
-
from classiq.qmod.create_model_function import create_model
|
28
27
|
from classiq.qmod.qfunc import qfunc
|
29
28
|
from classiq.qmod.qmod_parameter import CArray
|
30
29
|
from classiq.qmod.qmod_variable import Output, QVar
|
@@ -75,17 +74,15 @@ class CombinatorialProblem:
|
|
75
74
|
hadamard_transform(v)
|
76
75
|
repeat(
|
77
76
|
self.num_layers_,
|
78
|
-
lambda i: [
|
79
|
-
phase(
|
80
|
-
-self.cost_func(v), params[i]
|
81
|
-
), # type:ignore[func-returns-value]
|
77
|
+
lambda i: [
|
78
|
+
phase(-self.cost_func(v), params[i]),
|
82
79
|
apply_to_all(lambda q: RX(params[self.num_layers_ + i], q), v),
|
83
80
|
],
|
84
81
|
)
|
85
82
|
|
86
|
-
self.model_ = create_model(
|
87
|
-
|
88
|
-
) # type:ignore[assignment]
|
83
|
+
self.model_ = main.create_model(
|
84
|
+
constraints=constraints, preferences=preferences
|
85
|
+
).get_model() # type:ignore[assignment]
|
89
86
|
return self.model_ # type:ignore[return-value]
|
90
87
|
|
91
88
|
def get_qprog(self) -> SerializedQuantumProgram:
|
@@ -199,13 +196,13 @@ def execute_qaoa(
|
|
199
196
|
hadamard_transform(v)
|
200
197
|
repeat(
|
201
198
|
num_layers,
|
202
|
-
lambda i: [
|
203
|
-
phase(-cost_func(v), params[i]),
|
199
|
+
lambda i: [
|
200
|
+
phase(-cost_func(v), params[i]),
|
204
201
|
apply_to_all(lambda q: RX(params[num_layers + i], q), v),
|
205
202
|
],
|
206
203
|
)
|
207
204
|
|
208
|
-
model = create_model(
|
205
|
+
model = main.create_model().get_model()
|
209
206
|
qprog = synthesize(model)
|
210
207
|
|
211
208
|
with ExecutionSession(qprog, execution_preferences) as es:
|
@@ -71,7 +71,7 @@ class SimpleQuantumGradient(QuantumGradient):
|
|
71
71
|
post_process: PostProcessFunction,
|
72
72
|
epsilon: float = EPSILON,
|
73
73
|
*args: Any,
|
74
|
-
**kwargs: Any
|
74
|
+
**kwargs: Any,
|
75
75
|
) -> None:
|
76
76
|
super().__init__(quantum_program, execute, post_process)
|
77
77
|
self._epsilon = epsilon
|
@@ -94,7 +94,7 @@ def iter_inputs_weights(
|
|
94
94
|
post_process: PostProcessFunction,
|
95
95
|
*,
|
96
96
|
expected_shape: Shape = (),
|
97
|
-
requires_grad: Optional[bool] = None
|
97
|
+
requires_grad: Optional[bool] = None,
|
98
98
|
) -> Tensor:
|
99
99
|
if is_single_layer_circuit(weights):
|
100
100
|
iter_weights = torch.reshape(weights, (1, weights.shape[0]))
|
@@ -1,4 +1,3 @@
|
|
1
|
-
import json
|
2
1
|
import random
|
3
2
|
from types import TracebackType
|
4
3
|
from typing import Callable, Optional, Union, cast
|
@@ -16,7 +15,10 @@ from classiq.interface.executor.result import (
|
|
16
15
|
)
|
17
16
|
from classiq.interface.generator.arith import number_utils
|
18
17
|
from classiq.interface.generator.functions.qmod_python_interface import QmodPyStruct
|
19
|
-
from classiq.interface.generator.quantum_program import
|
18
|
+
from classiq.interface.generator.quantum_program import (
|
19
|
+
OMIT_DEBUG_INFO_FLAG,
|
20
|
+
QuantumProgram,
|
21
|
+
)
|
20
22
|
from classiq.interface.model.quantum_type import QuantumBit, QuantumNumeric
|
21
23
|
|
22
24
|
from classiq._internals import async_utils
|
@@ -46,7 +48,9 @@ def _deserialize_program(program: Program) -> QuantumProgram:
|
|
46
48
|
return (
|
47
49
|
program
|
48
50
|
if isinstance(program, QuantumProgram)
|
49
|
-
else QuantumProgram.
|
51
|
+
else QuantumProgram.model_validate_json(
|
52
|
+
program, context={OMIT_DEBUG_INFO_FLAG: True}
|
53
|
+
)
|
50
54
|
)
|
51
55
|
|
52
56
|
|
classiq/execution/jobs.py
CHANGED
@@ -9,7 +9,6 @@ from classiq.interface.exceptions import (
|
|
9
9
|
ClassiqAPIError,
|
10
10
|
ClassiqError,
|
11
11
|
)
|
12
|
-
from classiq.interface.execution.jobs import ExecutionJobDetailsV1
|
13
12
|
from classiq.interface.executor.execution_request import ExecutionJobDetails, JobCost
|
14
13
|
from classiq.interface.executor.execution_result import ResultsCollection
|
15
14
|
from classiq.interface.executor.result import (
|
@@ -26,8 +25,6 @@ from classiq._internals.async_utils import syncify_function
|
|
26
25
|
from classiq._internals.client import client
|
27
26
|
from classiq._internals.jobs import JobID, JobPoller
|
28
27
|
|
29
|
-
_JobDetails = Union[ExecutionJobDetails, ExecutionJobDetailsV1]
|
30
|
-
|
31
28
|
|
32
29
|
class ClassiqExecutionResultError(ClassiqError):
|
33
30
|
def __init__(self, primitive: str) -> None:
|
@@ -37,10 +34,10 @@ class ClassiqExecutionResultError(ClassiqError):
|
|
37
34
|
|
38
35
|
|
39
36
|
class ExecutionJob:
|
40
|
-
_details:
|
37
|
+
_details: ExecutionJobDetails
|
41
38
|
_result: Optional[ResultsCollection]
|
42
39
|
|
43
|
-
def __init__(self, details:
|
40
|
+
def __init__(self, details: ExecutionJobDetails) -> None:
|
44
41
|
self._details = details
|
45
42
|
self._result = None
|
46
43
|
|
classiq/executor.py
CHANGED
@@ -10,7 +10,10 @@ from classiq.interface.executor.execution_preferences import ExecutionPreference
|
|
10
10
|
from classiq.interface.executor.quantum_code import QuantumCode
|
11
11
|
from classiq.interface.executor.quantum_instruction_set import QuantumInstructionSet
|
12
12
|
from classiq.interface.executor.result import ExecutionDetails
|
13
|
-
from classiq.interface.generator.quantum_program import
|
13
|
+
from classiq.interface.generator.quantum_program import (
|
14
|
+
OMIT_DEBUG_INFO_FLAG,
|
15
|
+
QuantumProgram,
|
16
|
+
)
|
14
17
|
|
15
18
|
from classiq._internals import async_utils
|
16
19
|
from classiq._internals.api_wrapper import ApiWrapper
|
@@ -27,7 +30,9 @@ BackendPreferencesAndResult: TypeAlias = tuple[
|
|
27
30
|
def _parse_serialized_qprog(
|
28
31
|
quantum_program: SerializedQuantumProgram,
|
29
32
|
) -> QuantumProgram:
|
30
|
-
return QuantumProgram.model_validate_json(
|
33
|
+
return QuantumProgram.model_validate_json(
|
34
|
+
quantum_program, context={OMIT_DEBUG_INFO_FLAG: True}
|
35
|
+
)
|
31
36
|
|
32
37
|
|
33
38
|
async def execute_async(quantum_program: SerializedQuantumProgram) -> ExecutionJob:
|
classiq/interface/_version.py
CHANGED
classiq/interface/ast_node.py
CHANGED
@@ -20,7 +20,7 @@ class ASTNode(HashablePydanticBaseModel):
|
|
20
20
|
return self
|
21
21
|
|
22
22
|
|
23
|
-
def
|
23
|
+
def reset_lists(
|
24
24
|
ast_node: ASTNodeType, statement_block_fields: list[str]
|
25
25
|
) -> ASTNodeType:
|
26
26
|
return ast_node.model_copy(update={field: [] for field in statement_block_fields})
|
@@ -172,15 +172,14 @@ class ClassiqNvidiaBackendNames(StrEnum):
|
|
172
172
|
|
173
173
|
SIMULATOR = "nvidia_simulator"
|
174
174
|
SIMULATOR_STATEVECTOR = "nvidia_simulator_statevector"
|
175
|
+
BRAKET_NVIDIA_SIMULATOR = "braket_nvidia_simulator"
|
176
|
+
BRAKET_NVIDIA_SIMULATOR_STATEVECTOR = "braket_nvidia_simulator_statevector"
|
175
177
|
|
176
178
|
|
177
179
|
class IntelBackendNames(StrEnum):
|
178
180
|
SIMULATOR = "intel_qsdk_simulator"
|
179
181
|
|
180
182
|
|
181
|
-
AllClassiqBackendNames = Union[ClassiqSimulatorBackendNames, ClassiqNvidiaBackendNames]
|
182
|
-
|
183
|
-
|
184
183
|
class GoogleNvidiaBackendNames(StrEnum):
|
185
184
|
"""
|
186
185
|
Google backend names which Classiq Supports running on.
|
@@ -1,5 +1,6 @@
|
|
1
1
|
from collections.abc import Collection
|
2
2
|
from functools import reduce
|
3
|
+
from itertools import combinations
|
3
4
|
from typing import (
|
4
5
|
Any,
|
5
6
|
Optional,
|
@@ -69,7 +70,7 @@ class PauliOperator(HashablePydanticBaseModel, VersionedModel):
|
|
69
70
|
|
70
71
|
@staticmethod
|
71
72
|
def _validate_monomial_coefficient(
|
72
|
-
coeff: Union[sympy.Expr, ParameterComplexType]
|
73
|
+
coeff: Union[sympy.Expr, ParameterComplexType],
|
73
74
|
) -> ParameterComplexType:
|
74
75
|
if isinstance(coeff, str):
|
75
76
|
validate_expression_str(coeff)
|
@@ -144,16 +145,19 @@ class PauliOperator(HashablePydanticBaseModel, VersionedModel):
|
|
144
145
|
@property
|
145
146
|
def is_commutative(self) -> bool:
|
146
147
|
return all(
|
147
|
-
self.
|
148
|
-
|
149
|
-
)
|
150
|
-
for qubit_num in range(self.num_qubits)
|
148
|
+
self._do_paulis_commute(first[0], second[0])
|
149
|
+
for first, second in combinations(self.pauli_list, 2)
|
151
150
|
)
|
152
151
|
|
153
152
|
@staticmethod
|
154
|
-
def
|
155
|
-
|
156
|
-
|
153
|
+
def _do_paulis_commute(
|
154
|
+
first: PydanticPauliMonomialStr, second: PydanticPauliMonomialStr
|
155
|
+
) -> bool:
|
156
|
+
commute = True
|
157
|
+
for c1, c2 in zip(first, second):
|
158
|
+
if (c1 != "I") and (c2 != "I") and (c1 != c2):
|
159
|
+
commute = not commute
|
160
|
+
return commute
|
157
161
|
|
158
162
|
@property
|
159
163
|
def num_qubits(self) -> int:
|
@@ -0,0 +1,22 @@
|
|
1
|
+
from classiq.interface.model.allocate import Allocate
|
2
|
+
from classiq.interface.model.quantum_function_call import QuantumFunctionCall
|
3
|
+
from classiq.interface.model.statement_block import (
|
4
|
+
ConcreteQuantumStatement,
|
5
|
+
StatementBlock,
|
6
|
+
)
|
7
|
+
|
8
|
+
"""
|
9
|
+
This module contains helper functions to determine if a given quantum statement
|
10
|
+
is an allocation or free statement.
|
11
|
+
"""
|
12
|
+
|
13
|
+
|
14
|
+
def is_allocate_or_free(concrete_quantum_statement: ConcreteQuantumStatement) -> bool:
|
15
|
+
return isinstance(concrete_quantum_statement, Allocate) or (
|
16
|
+
isinstance(concrete_quantum_statement, QuantumFunctionCall)
|
17
|
+
and concrete_quantum_statement.function == "free"
|
18
|
+
)
|
19
|
+
|
20
|
+
|
21
|
+
def is_allocate_or_free_by_backref(back_refs: StatementBlock) -> bool:
|
22
|
+
return len(back_refs) > 0 and is_allocate_or_free(back_refs[0])
|
@@ -1,34 +1,23 @@
|
|
1
|
-
import json
|
2
1
|
from collections.abc import Mapping
|
3
|
-
from typing import
|
2
|
+
from typing import Optional, Union
|
4
3
|
from uuid import UUID
|
5
4
|
|
6
5
|
from pydantic import BaseModel, Field
|
7
6
|
|
8
|
-
from classiq.interface.
|
7
|
+
from classiq.interface.debug_info import back_ref_util
|
9
8
|
from classiq.interface.generator.generated_circuit_data import (
|
10
9
|
FunctionDebugInfoInterface,
|
11
10
|
OperationLevel,
|
11
|
+
StatementType,
|
12
12
|
)
|
13
13
|
from classiq.interface.model.statement_block import ConcreteQuantumStatement
|
14
14
|
|
15
15
|
ParameterValue = Union[float, int, str, None]
|
16
16
|
|
17
17
|
|
18
|
-
class StatementType(StrEnum):
|
19
|
-
CONTROL = "control"
|
20
|
-
POWER = "power"
|
21
|
-
INVERT = "invert"
|
22
|
-
WITHIN_APPLY = "within_apply"
|
23
|
-
ASSIGNMENT = "assignment"
|
24
|
-
REPEAT = "repeat"
|
25
|
-
|
26
|
-
|
27
18
|
class FunctionDebugInfo(BaseModel):
|
28
19
|
name: str
|
29
|
-
|
30
|
-
parameters: dict[str, str]
|
31
|
-
level: OperationLevel
|
20
|
+
level: OperationLevel = Field(default=OperationLevel.UNKNOWN)
|
32
21
|
statement_type: Union[StatementType, None] = None
|
33
22
|
is_allocate_or_free: bool = Field(default=False)
|
34
23
|
is_inverse: bool = Field(default=False)
|
@@ -36,12 +25,13 @@ class FunctionDebugInfo(BaseModel):
|
|
36
25
|
port_to_passed_variable_map: dict[str, str] = Field(default_factory=dict)
|
37
26
|
node: Optional[ConcreteQuantumStatement] = None
|
38
27
|
|
39
|
-
@
|
40
|
-
def
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
28
|
+
@property
|
29
|
+
def is_allocate_or_free_(self) -> bool:
|
30
|
+
return (
|
31
|
+
back_ref_util.is_allocate_or_free(self.node)
|
32
|
+
if self.node is not None
|
33
|
+
else self.is_allocate_or_free
|
34
|
+
)
|
45
35
|
|
46
36
|
def update_map_from_port_mapping(self, port_mapping: Mapping[str, str]) -> None:
|
47
37
|
new_port_to_passed_variable_map = self.port_to_passed_variable_map.copy()
|
@@ -89,6 +79,21 @@ class DebugInfoCollection(BaseModel):
|
|
89
79
|
return self.blackbox_data.get(debug_info.name)
|
90
80
|
|
91
81
|
|
82
|
+
def get_back_refs(
|
83
|
+
debug_info: FunctionDebugInfo, collected_debug_info: DebugInfoCollection
|
84
|
+
) -> list[ConcreteQuantumStatement]:
|
85
|
+
back_refs: list[ConcreteQuantumStatement] = []
|
86
|
+
while (node := debug_info.node) is not None:
|
87
|
+
back_refs.insert(0, node)
|
88
|
+
if node.back_ref is None:
|
89
|
+
break
|
90
|
+
next_debug_info = collected_debug_info.get(node.back_ref)
|
91
|
+
if next_debug_info is None:
|
92
|
+
break
|
93
|
+
debug_info = next_debug_info
|
94
|
+
return back_refs
|
95
|
+
|
96
|
+
|
92
97
|
def new_function_debug_info_by_node(
|
93
98
|
node: ConcreteQuantumStatement,
|
94
99
|
) -> FunctionDebugInfo:
|
@@ -1,4 +1,5 @@
|
|
1
|
-
|
1
|
+
import ast
|
2
|
+
from typing import Any, Final, Optional, cast
|
2
3
|
|
3
4
|
import networkx as nx
|
4
5
|
import pydantic
|
@@ -95,6 +96,9 @@ def get_arithmetic_params(
|
|
95
96
|
machine_precision: int,
|
96
97
|
enable_target: bool = False,
|
97
98
|
) -> Arithmetic:
|
99
|
+
expr_str, var_types = _substitute_quantum_subscripts(
|
100
|
+
expr_str, var_types, machine_precision
|
101
|
+
)
|
98
102
|
return Arithmetic(
|
99
103
|
expression=expr_str,
|
100
104
|
definitions={
|
@@ -121,3 +125,94 @@ def compute_arithmetic_result_type(
|
|
121
125
|
return register_info_to_quantum_type(
|
122
126
|
arith_param.outputs[ARITHMETIC_EXPRESSION_RESULT_NAME]
|
123
127
|
)
|
128
|
+
|
129
|
+
|
130
|
+
def aggregate_numeric_types(
|
131
|
+
numeric_types: list[QuantumNumeric],
|
132
|
+
) -> RegisterArithmeticInfo:
|
133
|
+
if all(
|
134
|
+
numeric_type.size_in_bits == 1
|
135
|
+
and numeric_type.sign_value
|
136
|
+
and numeric_type.fraction_digits_value == 1
|
137
|
+
for numeric_type in numeric_types
|
138
|
+
):
|
139
|
+
return RegisterArithmeticInfo(size=1, is_signed=True, fraction_places=1)
|
140
|
+
int_size = max(
|
141
|
+
numeric_type.size_in_bits
|
142
|
+
- int(numeric_type.sign_value)
|
143
|
+
- numeric_type.fraction_digits_value
|
144
|
+
for numeric_type in numeric_types
|
145
|
+
)
|
146
|
+
is_signed = any(numeric_type.sign_value for numeric_type in numeric_types)
|
147
|
+
frac_size = max(
|
148
|
+
numeric_type.fraction_digits_value for numeric_type in numeric_types
|
149
|
+
)
|
150
|
+
total_size = int_size + int(is_signed) + frac_size
|
151
|
+
return RegisterArithmeticInfo(
|
152
|
+
size=total_size, is_signed=is_signed, fraction_places=frac_size
|
153
|
+
)
|
154
|
+
|
155
|
+
|
156
|
+
class _QuantumSubscriptRemover(ast.NodeTransformer):
|
157
|
+
def __init__(self, machine_precision: int) -> None:
|
158
|
+
self._machine_precision = machine_precision
|
159
|
+
self.substitutions_types: dict[str, QuantumNumeric] = {}
|
160
|
+
|
161
|
+
def visit_Call(self, node: ast.Call) -> ast.expr:
|
162
|
+
if not isinstance(node.func, ast.Name) or node.func.id != "Piecewise":
|
163
|
+
return node
|
164
|
+
items = [
|
165
|
+
cast(float, cast(ast.Num, cast(ast.Tuple, arg).elts[0]).value)
|
166
|
+
for arg in node.args
|
167
|
+
]
|
168
|
+
numeric_types = [
|
169
|
+
compute_arithmetic_result_type(str(num), {}, self._machine_precision)
|
170
|
+
for num in items
|
171
|
+
]
|
172
|
+
unified_numeric_type = register_info_to_quantum_type(
|
173
|
+
aggregate_numeric_types(numeric_types)
|
174
|
+
)
|
175
|
+
substitution_var_name = f"__lut__{len(self.substitutions_types)}__"
|
176
|
+
self.substitutions_types[substitution_var_name] = unified_numeric_type
|
177
|
+
return ast.Name(id=substitution_var_name)
|
178
|
+
|
179
|
+
|
180
|
+
class _NameCollector(ast.NodeVisitor):
|
181
|
+
def __init__(self) -> None:
|
182
|
+
self.names: set[str] = set()
|
183
|
+
|
184
|
+
def visit_Name(self, node: ast.Name) -> None:
|
185
|
+
self.names.add(node.id)
|
186
|
+
|
187
|
+
|
188
|
+
def _substitute_quantum_subscripts(
|
189
|
+
expr_str: str, var_types: dict[str, QuantumType], machine_precision: int
|
190
|
+
) -> tuple[str, dict[str, QuantumType]]:
|
191
|
+
"""
|
192
|
+
Remove quantum lookup expressions ([1, 2, 3, 4][n]) from an arithmetic expression
|
193
|
+
for the purpose of calculating its numeric attributes.
|
194
|
+
Each quantum lookup expression is replaced by a numeric value with equivalent
|
195
|
+
numeric properties.
|
196
|
+
|
197
|
+
Args:
|
198
|
+
expr_str: arithmetic expression
|
199
|
+
var_types: quantum variable type mapping
|
200
|
+
machine_precision: global machine precision
|
201
|
+
|
202
|
+
Returns:
|
203
|
+
1. the reduced expression
|
204
|
+
2. updated type mapping
|
205
|
+
"""
|
206
|
+
expr_ast = ast.parse(expr_str)
|
207
|
+
subscript_remover = _QuantumSubscriptRemover(machine_precision)
|
208
|
+
expr_ast = subscript_remover.visit(expr_ast)
|
209
|
+
var_types_substituted = var_types | subscript_remover.substitutions_types
|
210
|
+
expr_str_substituted = ast.unparse(expr_ast)
|
211
|
+
names_collector = _NameCollector()
|
212
|
+
names_collector.visit(ast.parse(expr_str_substituted))
|
213
|
+
var_types_substituted = {
|
214
|
+
var_name: var_type
|
215
|
+
for var_name, var_type in var_types_substituted.items()
|
216
|
+
if var_name in names_collector.names
|
217
|
+
}
|
218
|
+
return expr_str_substituted, var_types_substituted
|
@@ -59,7 +59,7 @@ def get_params(
|
|
59
59
|
machine_precision: int,
|
60
60
|
output_size: Optional[int] = None,
|
61
61
|
inplace_arg: Optional[str] = None,
|
62
|
-
target: Optional[RegisterArithmeticInfo] = None
|
62
|
+
target: Optional[RegisterArithmeticInfo] = None,
|
63
63
|
) -> ArithmeticOperationParams:
|
64
64
|
operation = id2op(node_id)
|
65
65
|
if target and not operation_allows_target(operation):
|
@@ -334,7 +334,7 @@ def logical_and_params_getter(
|
|
334
334
|
machine_precision: int,
|
335
335
|
output_size: Optional[int] = None,
|
336
336
|
inplace_arg: Optional[str] = None,
|
337
|
-
target: Optional[RegisterArithmeticInfo] = None
|
337
|
+
target: Optional[RegisterArithmeticInfo] = None,
|
338
338
|
) -> ArithmeticOperationParams:
|
339
339
|
return LogicalAnd(args=arg, target=target, machine_precision=machine_precision)
|
340
340
|
|
@@ -344,7 +344,7 @@ def logical_or_params_getter(
|
|
344
344
|
machine_precision: int,
|
345
345
|
output_size: Optional[int] = None,
|
346
346
|
inplace_arg: Optional[str] = None,
|
347
|
-
target: Optional[RegisterArithmeticInfo] = None
|
347
|
+
target: Optional[RegisterArithmeticInfo] = None,
|
348
348
|
) -> ArithmeticOperationParams:
|
349
349
|
return LogicalOr(args=arg, target=target, machine_precision=machine_precision)
|
350
350
|
|
@@ -1,8 +1,9 @@
|
|
1
1
|
from typing import TYPE_CHECKING, Any, Literal, Union
|
2
2
|
|
3
3
|
import pydantic
|
4
|
-
from pydantic import ConfigDict
|
4
|
+
from pydantic import ConfigDict, PrivateAttr
|
5
5
|
from sympy import IndexedBase, Symbol
|
6
|
+
from typing_extensions import Self
|
6
7
|
|
7
8
|
from classiq.interface.ast_node import HashableASTNode
|
8
9
|
from classiq.interface.generator.expressions.expression_types import RuntimeExpression
|
@@ -19,6 +20,8 @@ NamedSymbol = Union[IndexedBase, Symbol]
|
|
19
20
|
|
20
21
|
|
21
22
|
class ClassicalType(HashableASTNode):
|
23
|
+
_is_generative: bool = PrivateAttr(default=False)
|
24
|
+
|
22
25
|
def as_symbolic(self, name: str) -> Union[NamedSymbol, list[NamedSymbol]]:
|
23
26
|
return Symbol(name)
|
24
27
|
|
@@ -27,6 +30,14 @@ class ClassicalType(HashableASTNode):
|
|
27
30
|
def __str__(self) -> str:
|
28
31
|
return str(type(self).__name__)
|
29
32
|
|
33
|
+
def set_generative(self) -> Self:
|
34
|
+
self._is_generative = True
|
35
|
+
return self
|
36
|
+
|
37
|
+
@property
|
38
|
+
def is_generative(self) -> bool:
|
39
|
+
return self._is_generative
|
40
|
+
|
30
41
|
|
31
42
|
class Integer(ClassicalType):
|
32
43
|
kind: Literal["int"]
|