classiq 0.75.0__py3-none-any.whl → 0.77.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 +36 -0
- classiq/analyzer/show_interactive_hack.py +58 -2
- classiq/applications/chemistry/chemistry_model_constructor.py +15 -7
- classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +11 -1
- classiq/applications/combinatorial_optimization/combinatorial_problem.py +8 -7
- classiq/applications/qnn/gradients/quantum_gradient.py +3 -5
- classiq/applications/qnn/gradients/simple_quantum_gradient.py +2 -2
- classiq/applications/qnn/qlayer.py +14 -19
- classiq/applications/qnn/types.py +1 -4
- classiq/execution/__init__.py +3 -0
- classiq/execution/execution_session.py +3 -16
- classiq/execution/qnn.py +2 -2
- classiq/execution/user_budgets.py +38 -0
- classiq/executor.py +7 -19
- classiq/interface/_version.py +1 -1
- classiq/interface/debug_info/debug_info.py +16 -2
- classiq/interface/executor/user_budget.py +56 -0
- classiq/interface/generator/application_apis/finance_declarations.py +3 -0
- classiq/interface/generator/expressions/atomic_expression_functions.py +3 -0
- classiq/interface/generator/expressions/proxies/classical/any_classical_value.py +30 -124
- classiq/interface/generator/expressions/proxies/classical/classical_array_proxy.py +45 -21
- classiq/interface/generator/expressions/proxies/classical/qmod_struct_instance.py +7 -0
- classiq/interface/generator/expressions/proxies/classical/utils.py +12 -11
- classiq/interface/generator/expressions/proxies/quantum/qmod_qarray_proxy.py +6 -15
- classiq/interface/generator/expressions/proxies/quantum/qmod_qscalar_proxy.py +22 -6
- classiq/interface/generator/expressions/proxies/quantum/qmod_sized_proxy.py +9 -4
- classiq/interface/generator/expressions/sympy_supported_expressions.py +1 -0
- classiq/interface/generator/functions/classical_type.py +6 -1
- classiq/interface/generator/functions/type_name.py +7 -2
- classiq/interface/generator/functions/type_qualifier.py +15 -0
- classiq/interface/generator/model/preferences/preferences.py +7 -0
- classiq/interface/generator/quantum_program.py +5 -19
- classiq/interface/helpers/backward_compatibility.py +9 -0
- classiq/interface/helpers/datastructures.py +6 -0
- classiq/interface/model/handle_binding.py +8 -0
- classiq/interface/model/model.py +3 -6
- classiq/interface/model/port_declaration.py +1 -2
- classiq/interface/model/quantum_function_call.py +31 -1
- classiq/interface/model/quantum_lambda_function.py +2 -1
- classiq/interface/model/quantum_statement.py +14 -1
- classiq/interface/server/routes.py +6 -0
- classiq/interface/source_reference.py +7 -2
- classiq/model_expansions/atomic_expression_functions_defs.py +62 -19
- classiq/model_expansions/capturing/captured_vars.py +18 -6
- classiq/model_expansions/closure.py +5 -0
- classiq/model_expansions/evaluators/arg_type_match.py +2 -2
- classiq/model_expansions/evaluators/argument_types.py +3 -3
- classiq/model_expansions/evaluators/classical_expression.py +9 -9
- classiq/model_expansions/evaluators/classical_type_inference.py +17 -6
- classiq/model_expansions/evaluators/parameter_types.py +45 -24
- classiq/model_expansions/expression_evaluator.py +21 -12
- classiq/model_expansions/function_builder.py +45 -0
- classiq/model_expansions/generative_functions.py +62 -35
- classiq/model_expansions/interpreters/base_interpreter.py +32 -7
- classiq/model_expansions/interpreters/frontend_generative_interpreter.py +9 -3
- classiq/model_expansions/interpreters/generative_interpreter.py +17 -5
- classiq/model_expansions/quantum_operations/allocate.py +8 -3
- classiq/model_expansions/quantum_operations/assignment_result_processor.py +221 -20
- classiq/model_expansions/quantum_operations/bind.py +54 -30
- classiq/model_expansions/quantum_operations/block_evaluator.py +42 -0
- classiq/model_expansions/quantum_operations/call_emitter.py +35 -18
- classiq/model_expansions/quantum_operations/composite_emitter.py +1 -1
- classiq/model_expansions/quantum_operations/declarative_call_emitter.py +23 -9
- classiq/model_expansions/quantum_operations/emitter.py +21 -9
- classiq/model_expansions/quantum_operations/quantum_function_call.py +4 -3
- classiq/model_expansions/scope.py +63 -10
- classiq/model_expansions/sympy_conversion/arithmetics.py +18 -0
- classiq/model_expansions/sympy_conversion/expression_to_sympy.py +2 -0
- classiq/model_expansions/sympy_conversion/sympy_to_python.py +10 -1
- classiq/model_expansions/transformers/model_renamer.py +45 -7
- classiq/model_expansions/utils/handles_collector.py +1 -1
- classiq/model_expansions/visitors/symbolic_param_inference.py +3 -3
- classiq/model_expansions/visitors/variable_references.py +45 -9
- classiq/open_library/functions/lookup_table.py +1 -1
- classiq/open_library/functions/state_preparation.py +1 -1
- classiq/qmod/builtins/functions/allocation.py +2 -2
- classiq/qmod/builtins/functions/arithmetic.py +14 -12
- classiq/qmod/builtins/functions/standard_gates.py +23 -23
- classiq/qmod/create_model_function.py +21 -3
- classiq/qmod/declaration_inferrer.py +19 -7
- classiq/qmod/generative.py +9 -1
- classiq/qmod/global_declarative_switch.py +19 -0
- classiq/qmod/native/expression_to_qmod.py +4 -0
- classiq/qmod/native/pretty_printer.py +12 -3
- classiq/qmod/pretty_print/pretty_printer.py +5 -1
- classiq/qmod/python_classical_type.py +4 -5
- classiq/qmod/qfunc.py +31 -23
- classiq/qmod/qmod_constant.py +15 -7
- classiq/qmod/qmod_variable.py +7 -1
- classiq/qmod/quantum_expandable.py +29 -1
- classiq/qmod/quantum_function.py +45 -25
- classiq/qmod/semantics/lambdas.py +6 -2
- classiq/qmod/semantics/validation/main_validation.py +17 -4
- classiq/qmod/symbolic.py +8 -19
- classiq/qmod/symbolic_expr.py +26 -0
- classiq/qmod/write_qmod.py +36 -10
- classiq/synthesis.py +24 -37
- classiq/visualization.py +35 -0
- {classiq-0.75.0.dist-info → classiq-0.77.0.dist-info}/METADATA +1 -1
- {classiq-0.75.0.dist-info → classiq-0.77.0.dist-info}/RECORD +101 -96
- {classiq-0.75.0.dist-info → classiq-0.77.0.dist-info}/WHEEL +0 -0
@@ -1,6 +1,9 @@
|
|
1
1
|
from typing import Any, Union
|
2
2
|
|
3
3
|
from classiq.interface.exceptions import ClassiqExpansionError
|
4
|
+
from classiq.interface.generator.expressions.proxies.classical.any_classical_value import (
|
5
|
+
AnyClassicalValue,
|
6
|
+
)
|
4
7
|
from classiq.interface.generator.expressions.proxies.classical.classical_array_proxy import (
|
5
8
|
ClassicalArrayProxy,
|
6
9
|
)
|
@@ -17,6 +20,7 @@ from classiq.interface.generator.functions.classical_type import (
|
|
17
20
|
)
|
18
21
|
from classiq.interface.generator.functions.type_name import TypeName
|
19
22
|
from classiq.interface.generator.types.struct_declaration import StructDeclaration
|
23
|
+
from classiq.interface.helpers.backward_compatibility import zip_strict
|
20
24
|
|
21
25
|
|
22
26
|
def infer_classical_type(val: Any, classical_type: ClassicalType) -> ClassicalType:
|
@@ -33,7 +37,7 @@ def _infer_classical_struct_type(val: Any, classical_type: TypeName) -> Classica
|
|
33
37
|
decl = val.struct_declaration
|
34
38
|
new_fields = {
|
35
39
|
field_name: infer_classical_type(field_val, field_type)
|
36
|
-
for (field_name, field_val), field_type in
|
40
|
+
for (field_name, field_val), field_type in zip_strict(
|
37
41
|
val.fields.items(),
|
38
42
|
decl.variables.values(),
|
39
43
|
strict=True,
|
@@ -49,13 +53,20 @@ def _infer_classical_struct_type(val: Any, classical_type: TypeName) -> Classica
|
|
49
53
|
def _infer_classical_array_type(
|
50
54
|
val: Any, classical_type: Union[ClassicalArray, ClassicalList]
|
51
55
|
) -> ClassicalType:
|
52
|
-
if isinstance(val,
|
53
|
-
val_length = len(val)
|
54
|
-
elif isinstance(val, ClassicalArrayProxy):
|
56
|
+
if isinstance(val, ClassicalArrayProxy):
|
55
57
|
val_length = val.length
|
58
|
+
elif isinstance(val, list):
|
59
|
+
val_length = len(val)
|
60
|
+
elif isinstance(val, AnyClassicalValue):
|
61
|
+
return classical_type
|
56
62
|
else:
|
57
63
|
raise ClassiqExpansionError(f"Array expected, got {str(val)!r}")
|
58
|
-
if
|
64
|
+
if (
|
65
|
+
isinstance(classical_type, ClassicalArray)
|
66
|
+
and isinstance(val_length, int)
|
67
|
+
and isinstance(classical_type.size, int)
|
68
|
+
and val_length != classical_type.size
|
69
|
+
):
|
59
70
|
raise ClassiqExpansionError(
|
60
71
|
f"Type mismatch: Argument has {val_length} items but "
|
61
72
|
f"{classical_type.size} expected"
|
@@ -63,7 +74,7 @@ def _infer_classical_array_type(
|
|
63
74
|
return ClassicalArray(
|
64
75
|
element_type=(
|
65
76
|
infer_classical_type(val[0], classical_type.element_type)
|
66
|
-
if val_length > 0
|
77
|
+
if not isinstance(val_length, int) or val_length > 0
|
67
78
|
else classical_type.element_type
|
68
79
|
),
|
69
80
|
size=val_length,
|
@@ -1,10 +1,15 @@
|
|
1
|
-
from typing import TypeVar, Union
|
1
|
+
from typing import Optional, TypeVar, Union
|
2
|
+
|
3
|
+
import sympy
|
2
4
|
|
3
5
|
from classiq.interface.exceptions import (
|
4
6
|
ClassiqExpansionError,
|
5
7
|
ClassiqInternalExpansionError,
|
6
8
|
)
|
7
9
|
from classiq.interface.generator.expressions.expression import Expression
|
10
|
+
from classiq.interface.generator.expressions.proxies.classical.any_classical_value import (
|
11
|
+
AnyClassicalValue,
|
12
|
+
)
|
8
13
|
from classiq.interface.generator.functions.concrete_types import ConcreteQuantumType
|
9
14
|
from classiq.interface.generator.functions.port_declaration import (
|
10
15
|
PortDeclarationDirection,
|
@@ -15,6 +20,7 @@ from classiq.interface.generator.functions.type_name import (
|
|
15
20
|
from classiq.interface.model.classical_parameter_declaration import (
|
16
21
|
ClassicalParameterDeclaration,
|
17
22
|
)
|
23
|
+
from classiq.interface.model.handle_binding import HandleBinding
|
18
24
|
from classiq.interface.model.port_declaration import PortDeclaration
|
19
25
|
from classiq.interface.model.quantum_function_declaration import (
|
20
26
|
PositionalArg,
|
@@ -41,7 +47,12 @@ from classiq.model_expansions.evaluators.quantum_type_utils import (
|
|
41
47
|
set_length,
|
42
48
|
set_size,
|
43
49
|
)
|
44
|
-
from classiq.model_expansions.scope import
|
50
|
+
from classiq.model_expansions.scope import (
|
51
|
+
Evaluated,
|
52
|
+
QuantumSymbol,
|
53
|
+
QuantumVariable,
|
54
|
+
Scope,
|
55
|
+
)
|
45
56
|
|
46
57
|
|
47
58
|
def evaluate_parameter_types_from_args(
|
@@ -78,10 +89,10 @@ def _update_scope(
|
|
78
89
|
return
|
79
90
|
if parameter.direction is PortDeclarationDirection.Output:
|
80
91
|
return
|
81
|
-
|
92
|
+
quantum_var = argument.as_type(QuantumVariable)
|
82
93
|
casted_argument = _cast(
|
83
94
|
parameter.quantum_type,
|
84
|
-
|
95
|
+
quantum_var.quantum_type,
|
85
96
|
parameter.name,
|
86
97
|
)
|
87
98
|
closure.scope[parameter.name] = Evaluated(
|
@@ -115,12 +126,12 @@ def _update_operand_signature_environment(
|
|
115
126
|
|
116
127
|
|
117
128
|
def _cast(
|
118
|
-
|
129
|
+
parameter_type: QuantumType, argument_type: QuantumType, param_name: str
|
119
130
|
) -> QuantumSymbol:
|
120
|
-
updated_quantum_type =
|
121
|
-
_inject_quantum_arg_info_to_type(updated_quantum_type,
|
131
|
+
updated_quantum_type = parameter_type.model_copy()
|
132
|
+
_inject_quantum_arg_info_to_type(updated_quantum_type, argument_type, param_name)
|
122
133
|
return QuantumSymbol(
|
123
|
-
handle=
|
134
|
+
handle=HandleBinding(name=param_name), quantum_type=updated_quantum_type
|
124
135
|
)
|
125
136
|
|
126
137
|
|
@@ -143,7 +154,9 @@ def _evaluate_type_from_arg(
|
|
143
154
|
)
|
144
155
|
if parameter.direction != PortDeclarationDirection.Output:
|
145
156
|
updated_quantum_type = _inject_quantum_arg_info_to_type(
|
146
|
-
updated_quantum_type,
|
157
|
+
updated_quantum_type,
|
158
|
+
argument.as_type(QuantumVariable).quantum_type,
|
159
|
+
parameter.name,
|
147
160
|
)
|
148
161
|
return parameter.model_copy(update={"quantum_type": updated_quantum_type})
|
149
162
|
|
@@ -178,7 +191,8 @@ def _evaluate_qarray_in_quantum_symbol(
|
|
178
191
|
"length",
|
179
192
|
param_name,
|
180
193
|
)
|
181
|
-
|
194
|
+
if new_length is not None:
|
195
|
+
set_length(type_to_update, new_length)
|
182
196
|
return type_to_update
|
183
197
|
|
184
198
|
|
@@ -194,7 +208,8 @@ def _evaluate_qnum_in_quantum_symbol(
|
|
194
208
|
"sign",
|
195
209
|
param_name,
|
196
210
|
)
|
197
|
-
|
211
|
+
if new_is_sign is not None:
|
212
|
+
type_to_update.is_signed = Expression(expr=str(new_is_sign))
|
198
213
|
if type_to_update.fraction_digits is not None:
|
199
214
|
new_fraction_digits = _eval_expr(
|
200
215
|
type_to_update.fraction_digits,
|
@@ -204,7 +219,8 @@ def _evaluate_qnum_in_quantum_symbol(
|
|
204
219
|
"fraction digits",
|
205
220
|
param_name,
|
206
221
|
)
|
207
|
-
|
222
|
+
if new_fraction_digits is not None:
|
223
|
+
type_to_update.fraction_digits = Expression(expr=str(new_fraction_digits))
|
208
224
|
if type_to_update.size is not None:
|
209
225
|
new_size = _eval_expr(
|
210
226
|
type_to_update.size,
|
@@ -214,7 +230,8 @@ def _evaluate_qnum_in_quantum_symbol(
|
|
214
230
|
"size",
|
215
231
|
param_name,
|
216
232
|
)
|
217
|
-
|
233
|
+
if new_size is not None:
|
234
|
+
set_size(type_to_update, new_size, param_name)
|
218
235
|
return type_to_update
|
219
236
|
|
220
237
|
|
@@ -228,17 +245,21 @@ def _eval_expr(
|
|
228
245
|
type_name: str,
|
229
246
|
attr_name: str,
|
230
247
|
param_name: str,
|
231
|
-
) -> _EXPR_TYPE:
|
248
|
+
) -> Optional[_EXPR_TYPE]:
|
232
249
|
val = evaluate_classical_expression(expression, scope).value
|
233
250
|
if expected_type is int and isinstance(val, float):
|
234
251
|
val = int(val)
|
235
|
-
if
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
252
|
+
if isinstance(val, expected_type):
|
253
|
+
return val
|
254
|
+
if isinstance(val, AnyClassicalValue) or (
|
255
|
+
isinstance(val, sympy.Basic) and len(val.free_symbols) > 0
|
256
|
+
):
|
257
|
+
return None
|
258
|
+
raise ClassiqExpansionError(
|
259
|
+
f"When inferring the type of parameter {param_name!r}: "
|
260
|
+
f"{type_name} {attr_name} must be {expected_type.__name__}, got "
|
261
|
+
f"{str(val)!r}"
|
262
|
+
)
|
242
263
|
|
243
264
|
|
244
265
|
def _evaluate_qstruct_in_quantum_symbol(
|
@@ -267,11 +288,11 @@ def evaluate_types_in_quantum_symbols(
|
|
267
288
|
|
268
289
|
|
269
290
|
def _inject_quantum_arg_info_to_type(
|
270
|
-
parameter_type: QuantumType,
|
291
|
+
parameter_type: QuantumType, argument_type: QuantumType, param_name: str
|
271
292
|
) -> QuantumType:
|
272
|
-
if
|
293
|
+
if argument_type.has_size_in_bits:
|
273
294
|
copy_type_information(
|
274
|
-
|
295
|
+
argument_type,
|
275
296
|
parameter_type,
|
276
297
|
param_name,
|
277
298
|
)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import ast
|
2
2
|
from collections.abc import Mapping
|
3
3
|
from enum import EnumMeta
|
4
|
-
from typing import Any
|
4
|
+
from typing import Any
|
5
5
|
|
6
6
|
from sympy import SympifyError, sympify
|
7
7
|
|
@@ -50,11 +50,25 @@ def evaluate_constants_as_python(constants: list[Constant]) -> dict[str, Any]:
|
|
50
50
|
}
|
51
51
|
|
52
52
|
|
53
|
+
def _quick_eval(expr: str) -> Any:
|
54
|
+
try:
|
55
|
+
return int(expr)
|
56
|
+
except ValueError:
|
57
|
+
pass
|
58
|
+
try:
|
59
|
+
return float(expr)
|
60
|
+
except ValueError:
|
61
|
+
pass
|
62
|
+
return None
|
63
|
+
|
64
|
+
|
53
65
|
def evaluate(
|
54
|
-
expr: Expression,
|
55
|
-
locals_dict: Mapping[str, EvaluatedExpression],
|
56
|
-
uninitialized_locals: Optional[set[str]] = None,
|
66
|
+
expr: Expression, locals_dict: Mapping[str, EvaluatedExpression]
|
57
67
|
) -> EvaluatedExpression:
|
68
|
+
val = _quick_eval(expr.expr)
|
69
|
+
if val is not None:
|
70
|
+
return EvaluatedExpression(value=val)
|
71
|
+
|
58
72
|
model_locals: dict[str, ExpressionValue] = {}
|
59
73
|
model_locals.update(ATOMIC_EXPRESSION_FUNCTIONS)
|
60
74
|
model_locals.update(
|
@@ -65,15 +79,14 @@ def evaluate(
|
|
65
79
|
)
|
66
80
|
# locals override builtin-functions
|
67
81
|
model_locals.update({name: expr.value for name, expr in locals_dict.items()})
|
68
|
-
uninitialized_locals = uninitialized_locals or set()
|
69
82
|
|
70
|
-
_validate_undefined_vars(expr.expr, model_locals
|
83
|
+
_validate_undefined_vars(expr.expr, model_locals)
|
71
84
|
|
72
85
|
sympy_expr = translate_to_sympy(expr.expr)
|
73
86
|
try:
|
74
87
|
sympify_result = sympify(sympy_expr, locals=model_locals)
|
75
88
|
except (TypeError, IndexError) as e:
|
76
|
-
raise ClassiqExpansionError(str(e)) from
|
89
|
+
raise ClassiqExpansionError(str(e)) from e
|
77
90
|
except AttributeError as e:
|
78
91
|
if isinstance(e.obj, EnumMeta):
|
79
92
|
raise ClassiqExpansionError(
|
@@ -95,11 +108,8 @@ def evaluate(
|
|
95
108
|
|
96
109
|
|
97
110
|
def _validate_undefined_vars(
|
98
|
-
expr: str,
|
99
|
-
model_locals: dict[str, ExpressionValue],
|
100
|
-
uninitialized_locals: Optional[set[str]],
|
111
|
+
expr: str, model_locals: dict[str, ExpressionValue]
|
101
112
|
) -> None:
|
102
|
-
uninitialized_locals = uninitialized_locals or set()
|
103
113
|
id_visitor = _VarsCollector()
|
104
114
|
id_visitor.visit(ast.parse(expr))
|
105
115
|
identifiers = id_visitor.vars
|
@@ -108,7 +118,6 @@ def _validate_undefined_vars(
|
|
108
118
|
- model_locals.keys()
|
109
119
|
- set(SYMPY_SUPPORTED_EXPRESSIONS)
|
110
120
|
- set(symbolic.__all__)
|
111
|
-
- uninitialized_locals
|
112
121
|
)
|
113
122
|
|
114
123
|
if len(undefined_vars) == 1:
|
@@ -11,12 +11,15 @@ from classiq.interface.generator.compiler_keywords import (
|
|
11
11
|
from classiq.interface.generator.functions.builtins.internal_operators import (
|
12
12
|
WITHIN_APPLY_NAME,
|
13
13
|
)
|
14
|
+
from classiq.interface.generator.functions.type_qualifier import TypeQualifier
|
14
15
|
from classiq.interface.model.model import MAIN_FUNCTION_NAME
|
15
16
|
from classiq.interface.model.native_function_definition import (
|
16
17
|
NativeFunctionDefinition,
|
17
18
|
)
|
19
|
+
from classiq.interface.model.port_declaration import PortDeclaration
|
18
20
|
from classiq.interface.model.quantum_function_declaration import (
|
19
21
|
PositionalArg,
|
22
|
+
QuantumOperandDeclaration,
|
20
23
|
)
|
21
24
|
from classiq.interface.model.quantum_statement import QuantumStatement
|
22
25
|
from classiq.interface.model.variable_declaration_statement import (
|
@@ -212,6 +215,7 @@ class OperationBuilder:
|
|
212
215
|
self, function_context: FunctionContext, params: Sequence[PositionalArg]
|
213
216
|
) -> NativeFunctionDefinition:
|
214
217
|
name = self._get_expanded_function_name(function_context)
|
218
|
+
self._override_type_qualifier(function_context, params)
|
215
219
|
|
216
220
|
return NativeFunctionDefinition(
|
217
221
|
name=name,
|
@@ -244,3 +248,44 @@ class OperationBuilder:
|
|
244
248
|
raise ClassiqInternalExpansionError("Could not allocate function name")
|
245
249
|
|
246
250
|
return name
|
251
|
+
|
252
|
+
def _override_type_qualifier(
|
253
|
+
self, function_context: FunctionContext, params: Sequence[PositionalArg]
|
254
|
+
) -> None:
|
255
|
+
"""
|
256
|
+
The type qualifier can be changed according to the operand passed to the
|
257
|
+
function. For example,
|
258
|
+
apply_to_all(X, q) --> q will be QFree after expansion
|
259
|
+
apply_to_all(H, q) --> q will be Quantum after expansion
|
260
|
+
This also holds for the intermediate lambda created during the expansion.
|
261
|
+
|
262
|
+
We don't override the type qualifier if it's explicitly specified (QFree or
|
263
|
+
Const), neither in the function declaration nor in the operand declaration.
|
264
|
+
"""
|
265
|
+
|
266
|
+
if function_context.is_lambda:
|
267
|
+
self._update_type_qualifiers(params)
|
268
|
+
return
|
269
|
+
|
270
|
+
orig_name = function_context.name
|
271
|
+
if orig_name == MAIN_FUNCTION_NAME or orig_name not in self.current_scope:
|
272
|
+
return
|
273
|
+
|
274
|
+
orig_func = self.current_scope[orig_name].value
|
275
|
+
if not any(
|
276
|
+
isinstance(param_decl, QuantumOperandDeclaration)
|
277
|
+
for param_decl in orig_func.positional_arg_declarations
|
278
|
+
):
|
279
|
+
return
|
280
|
+
|
281
|
+
self._update_type_qualifiers(params)
|
282
|
+
|
283
|
+
@staticmethod
|
284
|
+
def _update_type_qualifiers(params: Sequence[PositionalArg]) -> None:
|
285
|
+
# only override the qualifier if it's unspecified (not QFree or Const)
|
286
|
+
for param in params:
|
287
|
+
if (
|
288
|
+
isinstance(param, PortDeclaration)
|
289
|
+
and param.type_qualifier is TypeQualifier.Quantum
|
290
|
+
):
|
291
|
+
param.type_qualifier = TypeQualifier.Inferred
|
@@ -16,7 +16,7 @@ from classiq.interface.generator.expressions.proxies.classical.utils import (
|
|
16
16
|
get_proxy_type,
|
17
17
|
)
|
18
18
|
from classiq.interface.generator.functions.type_name import Struct
|
19
|
-
from classiq.interface.helpers.datastructures import
|
19
|
+
from classiq.interface.helpers.datastructures import LenList
|
20
20
|
from classiq.interface.helpers.pydantic_model_helpers import nameables_to_dict
|
21
21
|
from classiq.interface.model.native_function_definition import NativeFunctionDefinition
|
22
22
|
from classiq.interface.model.port_declaration import PortDeclaration
|
@@ -31,16 +31,18 @@ from classiq.model_expansions.closure import (
|
|
31
31
|
FunctionClosure,
|
32
32
|
GenerativeClosure,
|
33
33
|
)
|
34
|
-
from classiq.model_expansions.scope import Evaluated
|
34
|
+
from classiq.model_expansions.scope import Evaluated
|
35
35
|
from classiq.qmod.generative import generative_mode_context, set_frontend_interpreter
|
36
36
|
from classiq.qmod.model_state_container import QMODULE
|
37
37
|
from classiq.qmod.qmod_parameter import CParamStruct, create_param
|
38
|
-
from classiq.qmod.qmod_variable import _create_qvar_for_qtype
|
38
|
+
from classiq.qmod.qmod_variable import QScalar, _create_qvar_for_qtype
|
39
39
|
from classiq.qmod.quantum_expandable import (
|
40
40
|
QTerminalCallable,
|
41
41
|
)
|
42
42
|
from classiq.qmod.quantum_function import QFunc
|
43
43
|
from classiq.qmod.semantics.annotation.call_annotation import resolve_function_calls
|
44
|
+
from classiq.qmod.symbolic_expr import SymbolicExpr, SymbolicSubscriptAndField
|
45
|
+
from classiq.qmod.utilities import qmod_val_to_expr_str
|
44
46
|
|
45
47
|
if TYPE_CHECKING:
|
46
48
|
from classiq.model_expansions.interpreters.generative_interpreter import (
|
@@ -65,36 +67,61 @@ def _unwrap_traceback_frame(e: Exception) -> Exception:
|
|
65
67
|
return e.with_traceback(back_tb)
|
66
68
|
|
67
69
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
70
|
+
class SymbolicList(LenList):
|
71
|
+
def __getitem__(self, index: Any) -> Any:
|
72
|
+
if isinstance(index, (QScalar, SymbolicExpr)) or (
|
73
|
+
isinstance(index, slice)
|
74
|
+
and any(
|
75
|
+
isinstance(slice_part, (QScalar, SymbolicExpr))
|
76
|
+
for slice_part in (index.start, index.stop, index.step)
|
77
|
+
)
|
78
|
+
):
|
79
|
+
return SymbolicSubscriptAndField(
|
80
|
+
qmod_val_to_expr_str(self), is_quantum=False
|
81
|
+
)[index]
|
82
|
+
try:
|
83
|
+
return super().__getitem__(index)
|
84
|
+
except (IndexError, TypeError) as e:
|
85
|
+
raise _unwrap_traceback_frame(e) from None
|
86
|
+
|
87
|
+
|
88
|
+
def translate_classical_ast_arg_to_python_qmod(value: Any) -> Any:
|
89
|
+
if isinstance(value, QmodStructInstance):
|
90
|
+
return QmodStructInstance(
|
91
|
+
value.struct_declaration,
|
92
|
+
{
|
93
|
+
field_name: translate_classical_ast_arg_to_python_qmod(field_value)
|
94
|
+
for field_name, field_value in value.fields.items()
|
95
|
+
},
|
73
96
|
)
|
97
|
+
if isinstance(value, list):
|
98
|
+
return SymbolicList(
|
99
|
+
[translate_classical_ast_arg_to_python_qmod(item) for item in value]
|
100
|
+
)
|
101
|
+
if isinstance(value, ClassicalProxy):
|
102
|
+
return create_param(str(value.handle), get_proxy_type(value), QMODULE)
|
103
|
+
|
104
|
+
return value
|
105
|
+
|
106
|
+
|
107
|
+
def translate_ast_arg_to_python_qmod(param: PositionalArg, value: Any) -> Any:
|
108
|
+
if isinstance(param, PortDeclaration):
|
109
|
+
return _create_qvar_for_qtype(value.quantum_type, value.handle)
|
74
110
|
if isinstance(param, QuantumOperandDeclaration):
|
75
111
|
if not param.is_list or not param.is_generative:
|
76
112
|
return QTerminalCallable(param)
|
77
113
|
inner_decl = param.model_copy(update={"is_list": False})
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
struct_type=Struct(name=classical_value.struct_declaration.name),
|
88
|
-
qmodule=QMODULE,
|
89
|
-
)
|
90
|
-
else:
|
91
|
-
return get_sdk_compatible_python_object(dict(classical_value.fields))
|
92
|
-
if isinstance(classical_value, ClassicalProxy):
|
93
|
-
return create_param(
|
94
|
-
str(classical_value.handle), get_proxy_type(classical_value), QMODULE
|
114
|
+
return [QTerminalCallable(inner_decl, index_=idx) for idx in range(len(value))]
|
115
|
+
if (
|
116
|
+
isinstance(value, QmodStructInstance)
|
117
|
+
and param.classical_type.is_purely_declarative
|
118
|
+
):
|
119
|
+
classical_type = Struct(name=value.struct_declaration.name)
|
120
|
+
classical_type.set_classical_struct_decl(value.struct_declaration)
|
121
|
+
return CParamStruct(
|
122
|
+
expr=param.name, struct_type=classical_type, qmodule=QMODULE
|
95
123
|
)
|
96
|
-
|
97
|
-
return get_sdk_compatible_python_object(classical_value)
|
124
|
+
return translate_classical_ast_arg_to_python_qmod(value)
|
98
125
|
|
99
126
|
|
100
127
|
class _InterpreterExpandable(QFunc):
|
@@ -107,7 +134,7 @@ class _InterpreterExpandable(QFunc):
|
|
107
134
|
dummy_function = NativeFunctionDefinition(
|
108
135
|
name=current_operation.name,
|
109
136
|
positional_arg_declarations=current_operation.positional_arg_declarations,
|
110
|
-
body=
|
137
|
+
body=[stmt],
|
111
138
|
)
|
112
139
|
declarative_functions = {
|
113
140
|
name: func
|
@@ -140,14 +167,14 @@ class _InterpreterExpandable(QFunc):
|
|
140
167
|
name=name,
|
141
168
|
positional_arg_declarations=value.positional_arg_declarations,
|
142
169
|
)
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
):
|
170
|
+
continue
|
171
|
+
op_param = self._interpreter._builder.current_function.parameters_dict.get(
|
172
|
+
name
|
173
|
+
)
|
174
|
+
if isinstance(op_param, QuantumOperandDeclaration):
|
148
175
|
scope_func_decls[name] = QuantumFunctionDeclaration(
|
149
176
|
name=name,
|
150
|
-
positional_arg_declarations=
|
177
|
+
positional_arg_declarations=op_param.positional_arg_declarations,
|
151
178
|
)
|
152
179
|
return (
|
153
180
|
nameables_to_dict(self._interpreter._get_function_declarations())
|
@@ -161,7 +188,7 @@ def emit_generative_statements(
|
|
161
188
|
args: list[Evaluated],
|
162
189
|
) -> None:
|
163
190
|
python_qmod_args = [
|
164
|
-
translate_ast_arg_to_python_qmod(param, arg)
|
191
|
+
translate_ast_arg_to_python_qmod(param, arg.value)
|
165
192
|
for param, arg in zip(operation.positional_arg_declarations, args)
|
166
193
|
]
|
167
194
|
with _InterpreterExpandable(interpreter):
|
@@ -6,7 +6,6 @@ from contextlib import nullcontext
|
|
6
6
|
from functools import singledispatchmethod
|
7
7
|
from typing import Any, cast
|
8
8
|
|
9
|
-
import sympy
|
10
9
|
from pydantic import ValidationError
|
11
10
|
|
12
11
|
from classiq.interface.debug_info.debug_info import (
|
@@ -17,11 +16,15 @@ from classiq.interface.exceptions import (
|
|
17
16
|
ClassiqExpansionError,
|
18
17
|
ClassiqInternalExpansionError,
|
19
18
|
)
|
19
|
+
from classiq.interface.generator.expressions.atomic_expression_functions import (
|
20
|
+
CLASSICAL_ATTRIBUTES,
|
21
|
+
)
|
20
22
|
from classiq.interface.generator.expressions.expression import Expression
|
21
23
|
from classiq.interface.generator.types.compilation_metadata import CompilationMetadata
|
22
24
|
from classiq.interface.model.handle_binding import (
|
23
25
|
FieldHandleBinding,
|
24
26
|
HandleBinding,
|
27
|
+
HandlesList,
|
25
28
|
SlicedHandleBinding,
|
26
29
|
SubscriptHandleBinding,
|
27
30
|
)
|
@@ -49,7 +52,12 @@ from classiq.model_expansions.function_builder import (
|
|
49
52
|
OperationBuilder,
|
50
53
|
OperationContext,
|
51
54
|
)
|
52
|
-
from classiq.model_expansions.scope import
|
55
|
+
from classiq.model_expansions.scope import (
|
56
|
+
Evaluated,
|
57
|
+
QuantumSymbol,
|
58
|
+
QuantumSymbolList,
|
59
|
+
Scope,
|
60
|
+
)
|
53
61
|
from classiq.model_expansions.scope_initialization import (
|
54
62
|
add_entry_point_params_to_scope,
|
55
63
|
init_builtin_types,
|
@@ -57,11 +65,13 @@ from classiq.model_expansions.scope_initialization import (
|
|
57
65
|
)
|
58
66
|
from classiq.model_expansions.utils.counted_name_allocator import CountedNameAllocator
|
59
67
|
from classiq.model_expansions.visitors.variable_references import VarRefCollector
|
68
|
+
from classiq.qmod.builtins.constants import __all__ as builtin_constants
|
60
69
|
from classiq.qmod.builtins.enums import BUILTIN_ENUM_DECLARATIONS
|
61
70
|
from classiq.qmod.builtins.structs import BUILTIN_STRUCT_DECLARATIONS
|
62
71
|
from classiq.qmod.model_state_container import QMODULE
|
63
72
|
from classiq.qmod.semantics.error_manager import ErrorManager
|
64
73
|
from classiq.qmod.semantics.validation.model_validation import validate_model
|
74
|
+
from classiq.qmod.utilities import qmod_val_to_expr_str
|
65
75
|
|
66
76
|
|
67
77
|
class BaseInterpreter:
|
@@ -126,7 +136,11 @@ class BaseInterpreter:
|
|
126
136
|
classical_execution_code=self._model.classical_execution_code,
|
127
137
|
execution_preferences=self._model.execution_preferences,
|
128
138
|
functions=list(self._expanded_functions.values()),
|
129
|
-
constants=
|
139
|
+
constants=[
|
140
|
+
const
|
141
|
+
for name, const in QMODULE.constants.items()
|
142
|
+
if name not in builtin_constants
|
143
|
+
],
|
130
144
|
enums=[
|
131
145
|
enum_decl
|
132
146
|
for name, enum_decl in QMODULE.enum_decls.items()
|
@@ -157,14 +171,17 @@ class BaseInterpreter:
|
|
157
171
|
@evaluate.register
|
158
172
|
def evaluate_classical_expression(self, expression: Expression) -> Evaluated:
|
159
173
|
expr = evaluate_classical_expression(expression, self._builder.current_scope)
|
160
|
-
if not isinstance(expr.value, sympy.Basic):
|
161
|
-
return expr
|
162
174
|
vrc = VarRefCollector(ignore_duplicated_handles=True, unevaluated=True)
|
163
|
-
vrc.visit(ast.parse(
|
175
|
+
vrc.visit(ast.parse(qmod_val_to_expr_str(expr.value)))
|
164
176
|
for handle in vrc.var_handles:
|
165
177
|
if handle.name in self._builder.current_scope and isinstance(
|
166
|
-
self._builder.current_scope[handle.name], QuantumSymbol
|
178
|
+
self._builder.current_scope[handle.name].value, QuantumSymbol
|
167
179
|
):
|
180
|
+
if (
|
181
|
+
isinstance(handle, FieldHandleBinding)
|
182
|
+
and handle.field in CLASSICAL_ATTRIBUTES
|
183
|
+
):
|
184
|
+
handle = handle.base_handle
|
168
185
|
self.evaluate(handle)
|
169
186
|
return expr
|
170
187
|
|
@@ -222,6 +239,14 @@ class BaseInterpreter:
|
|
222
239
|
)
|
223
240
|
return Evaluated(value=fields[field_name])
|
224
241
|
|
242
|
+
@evaluate.register
|
243
|
+
def evaluate_handles_list(self, handles_list: HandlesList) -> Evaluated:
|
244
|
+
return Evaluated(
|
245
|
+
value=QuantumSymbolList.from_symbols(
|
246
|
+
[self.evaluate(handle).value for handle in handles_list.handles]
|
247
|
+
)
|
248
|
+
)
|
249
|
+
|
225
250
|
@abstractmethod
|
226
251
|
def emit(self, statement: QuantumStatement) -> None:
|
227
252
|
pass
|
@@ -1,10 +1,12 @@
|
|
1
1
|
import inspect
|
2
2
|
import os
|
3
|
+
from typing import Optional
|
3
4
|
|
4
5
|
from pydantic import ValidationError
|
5
6
|
|
6
7
|
from classiq.interface.exceptions import ClassiqError
|
7
8
|
from classiq.interface.model.allocate import Allocate
|
9
|
+
from classiq.interface.model.bind_operation import BindOperation
|
8
10
|
from classiq.interface.model.native_function_definition import NativeFunctionDefinition
|
9
11
|
from classiq.interface.model.quantum_function_call import QuantumFunctionCall
|
10
12
|
from classiq.interface.model.quantum_function_declaration import (
|
@@ -16,6 +18,7 @@ from classiq.model_expansions.closure import FunctionClosure, GenerativeFunction
|
|
16
18
|
from classiq.model_expansions.interpreters.generative_interpreter import (
|
17
19
|
GenerativeInterpreter,
|
18
20
|
)
|
21
|
+
from classiq.model_expansions.quantum_operations import BindEmitter
|
19
22
|
from classiq.model_expansions.quantum_operations.allocate import AllocateEmitter
|
20
23
|
from classiq.model_expansions.quantum_operations.quantum_function_call import (
|
21
24
|
DeclarativeQuantumFunctionCallEmitter,
|
@@ -31,15 +34,18 @@ class FrontendGenerativeInterpreter(GenerativeInterpreter):
|
|
31
34
|
def infer_symbolic_parameters(
|
32
35
|
self,
|
33
36
|
functions: list[NativeFunctionDefinition],
|
34
|
-
additional_signatures:
|
35
|
-
list[NamedParamsQuantumFunctionDeclaration]
|
36
|
-
|
37
|
+
additional_signatures: Optional[
|
38
|
+
list[NamedParamsQuantumFunctionDeclaration]
|
39
|
+
] = None,
|
37
40
|
) -> None:
|
38
41
|
SymbolicParamInference(functions, additional_signatures).infer()
|
39
42
|
|
40
43
|
def emit_allocate(self, allocate: Allocate) -> None:
|
41
44
|
AllocateEmitter(self, allow_symbolic_size=True).emit(allocate)
|
42
45
|
|
46
|
+
def emit_bind(self, bind: BindOperation) -> None:
|
47
|
+
BindEmitter(self, allow_symbolic_size=True).emit(bind)
|
48
|
+
|
43
49
|
def emit_quantum_function_call(self, call: QuantumFunctionCall) -> None:
|
44
50
|
DeclarativeQuantumFunctionCallEmitter(self).emit(call)
|
45
51
|
|