classiq 0.74.0__py3-none-any.whl → 0.76.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 +8 -1
- classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +2 -0
- classiq/applications/combinatorial_optimization/combinatorial_problem.py +4 -4
- classiq/applications/qnn/gradients/quantum_gradient.py +3 -5
- classiq/applications/qnn/gradients/simple_quantum_gradient.py +2 -2
- classiq/applications/qnn/qlayer.py +23 -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 +18 -13
- 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 +46 -22
- classiq/interface/generator/expressions/proxies/classical/qmod_struct_instance.py +7 -0
- classiq/interface/generator/expressions/proxies/classical/utils.py +14 -13
- classiq/interface/generator/expressions/proxies/quantum/qmod_qscalar_proxy.py +9 -2
- classiq/interface/generator/expressions/proxies/quantum/qmod_sized_proxy.py +4 -1
- classiq/interface/generator/expressions/sympy_supported_expressions.py +1 -0
- classiq/interface/generator/functions/classical_type.py +36 -1
- classiq/interface/generator/functions/type_name.py +32 -5
- classiq/interface/generator/functions/type_qualifier.py +15 -0
- classiq/interface/generator/generated_circuit_data.py +11 -25
- classiq/interface/generator/model/preferences/preferences.py +7 -0
- classiq/interface/generator/quantum_program.py +5 -19
- classiq/interface/generator/synthesis_metadata/synthesis_execution_data.py +10 -13
- classiq/interface/helpers/backward_compatibility.py +9 -0
- classiq/interface/helpers/datastructures.py +6 -0
- classiq/interface/helpers/versioned_model.py +12 -0
- classiq/interface/interface_version.py +1 -1
- classiq/interface/model/handle_binding.py +12 -0
- classiq/interface/model/port_declaration.py +1 -2
- classiq/interface/model/quantum_lambda_function.py +2 -1
- classiq/interface/model/statement_block.py +9 -1
- classiq/interface/model/within_apply_operation.py +12 -0
- classiq/interface/server/routes.py +6 -0
- classiq/model_expansions/atomic_expression_functions_defs.py +82 -23
- classiq/model_expansions/capturing/captured_vars.py +2 -0
- classiq/model_expansions/closure.py +18 -0
- classiq/model_expansions/evaluators/argument_types.py +6 -5
- classiq/model_expansions/evaluators/classical_type_inference.py +17 -6
- classiq/model_expansions/evaluators/parameter_types.py +26 -13
- classiq/model_expansions/evaluators/type_type_match.py +2 -2
- classiq/model_expansions/expression_evaluator.py +1 -1
- classiq/model_expansions/generative_functions.py +66 -33
- classiq/model_expansions/interpreters/base_interpreter.py +27 -19
- classiq/model_expansions/interpreters/frontend_generative_interpreter.py +26 -0
- classiq/model_expansions/interpreters/generative_interpreter.py +25 -1
- classiq/model_expansions/quantum_operations/allocate.py +27 -11
- classiq/model_expansions/quantum_operations/assignment_result_processor.py +220 -19
- 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 +14 -12
- 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 -8
- classiq/model_expansions/quantum_operations/expression_evaluator.py +1 -0
- classiq/model_expansions/quantum_operations/handle_evaluator.py +1 -0
- classiq/model_expansions/quantum_operations/quantum_function_call.py +4 -3
- classiq/model_expansions/scope.py +10 -7
- 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 +48 -8
- classiq/model_expansions/utils/handles_collector.py +1 -1
- classiq/model_expansions/visitors/symbolic_param_inference.py +197 -0
- classiq/model_expansions/visitors/variable_references.py +45 -9
- 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/declaration_inferrer.py +19 -7
- classiq/qmod/generative.py +9 -1
- classiq/qmod/native/expression_to_qmod.py +4 -0
- classiq/qmod/native/pretty_printer.py +8 -3
- classiq/qmod/pretty_print/pretty_printer.py +1 -1
- classiq/qmod/python_classical_type.py +4 -5
- classiq/qmod/qmod_constant.py +15 -7
- classiq/qmod/qmod_variable.py +30 -2
- classiq/qmod/quantum_function.py +19 -6
- 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 +34 -2
- classiq/qmod/write_qmod.py +5 -1
- classiq/synthesis.py +17 -31
- classiq/visualization.py +35 -0
- {classiq-0.74.0.dist-info → classiq-0.76.0.dist-info}/METADATA +1 -1
- {classiq-0.74.0.dist-info → classiq-0.76.0.dist-info}/RECORD +96 -91
- {classiq-0.74.0.dist-info → classiq-0.76.0.dist-info}/WHEEL +1 -1
@@ -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,
|
@@ -178,7 +183,8 @@ def _evaluate_qarray_in_quantum_symbol(
|
|
178
183
|
"length",
|
179
184
|
param_name,
|
180
185
|
)
|
181
|
-
|
186
|
+
if new_length is not None:
|
187
|
+
set_length(type_to_update, new_length)
|
182
188
|
return type_to_update
|
183
189
|
|
184
190
|
|
@@ -194,7 +200,8 @@ def _evaluate_qnum_in_quantum_symbol(
|
|
194
200
|
"sign",
|
195
201
|
param_name,
|
196
202
|
)
|
197
|
-
|
203
|
+
if new_is_sign is not None:
|
204
|
+
type_to_update.is_signed = Expression(expr=str(new_is_sign))
|
198
205
|
if type_to_update.fraction_digits is not None:
|
199
206
|
new_fraction_digits = _eval_expr(
|
200
207
|
type_to_update.fraction_digits,
|
@@ -204,7 +211,8 @@ def _evaluate_qnum_in_quantum_symbol(
|
|
204
211
|
"fraction digits",
|
205
212
|
param_name,
|
206
213
|
)
|
207
|
-
|
214
|
+
if new_fraction_digits is not None:
|
215
|
+
type_to_update.fraction_digits = Expression(expr=str(new_fraction_digits))
|
208
216
|
if type_to_update.size is not None:
|
209
217
|
new_size = _eval_expr(
|
210
218
|
type_to_update.size,
|
@@ -214,7 +222,8 @@ def _evaluate_qnum_in_quantum_symbol(
|
|
214
222
|
"size",
|
215
223
|
param_name,
|
216
224
|
)
|
217
|
-
|
225
|
+
if new_size is not None:
|
226
|
+
set_size(type_to_update, new_size, param_name)
|
218
227
|
return type_to_update
|
219
228
|
|
220
229
|
|
@@ -228,17 +237,21 @@ def _eval_expr(
|
|
228
237
|
type_name: str,
|
229
238
|
attr_name: str,
|
230
239
|
param_name: str,
|
231
|
-
) -> _EXPR_TYPE:
|
240
|
+
) -> Optional[_EXPR_TYPE]:
|
232
241
|
val = evaluate_classical_expression(expression, scope).value
|
233
242
|
if expected_type is int and isinstance(val, float):
|
234
243
|
val = int(val)
|
235
|
-
if
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
244
|
+
if isinstance(val, expected_type):
|
245
|
+
return val
|
246
|
+
if isinstance(val, AnyClassicalValue) or (
|
247
|
+
isinstance(val, sympy.Basic) and len(val.free_symbols) > 0
|
248
|
+
):
|
249
|
+
return None
|
250
|
+
raise ClassiqExpansionError(
|
251
|
+
f"When inferring the type of parameter {param_name!r}: "
|
252
|
+
f"{type_name} {attr_name} must be {expected_type.__name__}, got "
|
253
|
+
f"{str(val)!r}"
|
254
|
+
)
|
242
255
|
|
243
256
|
|
244
257
|
def _evaluate_qstruct_in_quantum_symbol(
|
@@ -85,7 +85,7 @@ def _check_classical_type_match(
|
|
85
85
|
) -> None:
|
86
86
|
if (
|
87
87
|
not isinstance(op_param, AnonClassicalParameterDeclaration)
|
88
|
-
or decl_param.classical_type.
|
89
|
-
!= op_param.classical_type.
|
88
|
+
or decl_param.classical_type.clear_flags()
|
89
|
+
!= op_param.classical_type.clear_flags()
|
90
90
|
):
|
91
91
|
raise ClassiqExpansionError(error_message)
|
@@ -73,7 +73,7 @@ def evaluate(
|
|
73
73
|
try:
|
74
74
|
sympify_result = sympify(sympy_expr, locals=model_locals)
|
75
75
|
except (TypeError, IndexError) as e:
|
76
|
-
raise ClassiqExpansionError(str(e)) from
|
76
|
+
raise ClassiqExpansionError(str(e)) from e
|
77
77
|
except AttributeError as e:
|
78
78
|
if isinstance(e.obj, EnumMeta):
|
79
79
|
raise ClassiqExpansionError(
|
@@ -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,33 +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
|
+
},
|
96
|
+
)
|
97
|
+
if isinstance(value, list):
|
98
|
+
return SymbolicList(
|
99
|
+
[translate_classical_ast_arg_to_python_qmod(item) for item in value]
|
73
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
|
-
|
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)
|
84
121
|
return CParamStruct(
|
85
|
-
expr=param.name,
|
86
|
-
struct_type=Struct(name=classical_value.struct_declaration.name),
|
87
|
-
qmodule=QMODULE,
|
88
|
-
)
|
89
|
-
if isinstance(classical_value, ClassicalProxy):
|
90
|
-
return create_param(
|
91
|
-
str(classical_value.handle), get_proxy_type(classical_value), QMODULE
|
122
|
+
expr=param.name, struct_type=classical_type, qmodule=QMODULE
|
92
123
|
)
|
93
|
-
|
94
|
-
return get_sdk_compatible_python_object(classical_value)
|
124
|
+
return translate_classical_ast_arg_to_python_qmod(value)
|
95
125
|
|
96
126
|
|
97
127
|
class _InterpreterExpandable(QFunc):
|
@@ -111,15 +141,18 @@ class _InterpreterExpandable(QFunc):
|
|
111
141
|
for name, func in self._qmodule.native_defs.items()
|
112
142
|
if name not in self._interpreter._top_level_scope
|
113
143
|
}
|
144
|
+
generative_functions = self._qmodule.generative_functions
|
114
145
|
self._interpreter.update_declarative_functions(
|
115
146
|
declarative_functions, self._qmodule
|
116
147
|
)
|
117
|
-
self._interpreter.update_generative_functions(
|
118
|
-
self._qmodule.generative_functions
|
119
|
-
)
|
148
|
+
self._interpreter.update_generative_functions(generative_functions)
|
120
149
|
func_decls = self._get_function_declarations()
|
121
150
|
for dec_func in declarative_functions.values():
|
122
151
|
resolve_function_calls(dec_func, func_decls)
|
152
|
+
self._interpreter.infer_symbolic_parameters(
|
153
|
+
list(declarative_functions.values()),
|
154
|
+
[func.func_decl for func in generative_functions.values()],
|
155
|
+
)
|
123
156
|
resolve_function_calls(dummy_function, func_decls)
|
124
157
|
stmt = dummy_function.body[-1]
|
125
158
|
with generative_mode_context(False):
|
@@ -134,14 +167,14 @@ class _InterpreterExpandable(QFunc):
|
|
134
167
|
name=name,
|
135
168
|
positional_arg_declarations=value.positional_arg_declarations,
|
136
169
|
)
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
):
|
170
|
+
continue
|
171
|
+
op_param = self._interpreter._builder.current_function.parameters_dict.get(
|
172
|
+
name
|
173
|
+
)
|
174
|
+
if isinstance(op_param, QuantumOperandDeclaration):
|
142
175
|
scope_func_decls[name] = QuantumFunctionDeclaration(
|
143
176
|
name=name,
|
144
|
-
positional_arg_declarations=
|
177
|
+
positional_arg_declarations=op_param.positional_arg_declarations,
|
145
178
|
)
|
146
179
|
return (
|
147
180
|
nameables_to_dict(self._interpreter._get_function_declarations())
|
@@ -155,7 +188,7 @@ def emit_generative_statements(
|
|
155
188
|
args: list[Evaluated],
|
156
189
|
) -> None:
|
157
190
|
python_qmod_args = [
|
158
|
-
translate_ast_arg_to_python_qmod(param, arg)
|
191
|
+
translate_ast_arg_to_python_qmod(param, arg.value)
|
159
192
|
for param, arg in zip(operation.positional_arg_declarations, args)
|
160
193
|
]
|
161
194
|
with _InterpreterExpandable(interpreter):
|
@@ -6,17 +6,20 @@ 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
|
-
from classiq.interface.debug_info.debug_info import
|
11
|
+
from classiq.interface.debug_info.debug_info import (
|
12
|
+
new_function_debug_info_by_node,
|
13
|
+
)
|
13
14
|
from classiq.interface.exceptions import (
|
14
15
|
ClassiqError,
|
15
16
|
ClassiqExpansionError,
|
16
17
|
ClassiqInternalExpansionError,
|
17
18
|
)
|
19
|
+
from classiq.interface.generator.expressions.atomic_expression_functions import (
|
20
|
+
CLASSICAL_ATTRIBUTES,
|
21
|
+
)
|
18
22
|
from classiq.interface.generator.expressions.expression import Expression
|
19
|
-
from classiq.interface.generator.generated_circuit_data import OperationLevel
|
20
23
|
from classiq.interface.generator.types.compilation_metadata import CompilationMetadata
|
21
24
|
from classiq.interface.model.handle_binding import (
|
22
25
|
FieldHandleBinding,
|
@@ -56,11 +59,13 @@ from classiq.model_expansions.scope_initialization import (
|
|
56
59
|
)
|
57
60
|
from classiq.model_expansions.utils.counted_name_allocator import CountedNameAllocator
|
58
61
|
from classiq.model_expansions.visitors.variable_references import VarRefCollector
|
62
|
+
from classiq.qmod.builtins.constants import __all__ as builtin_constants
|
59
63
|
from classiq.qmod.builtins.enums import BUILTIN_ENUM_DECLARATIONS
|
60
64
|
from classiq.qmod.builtins.structs import BUILTIN_STRUCT_DECLARATIONS
|
61
65
|
from classiq.qmod.model_state_container import QMODULE
|
62
66
|
from classiq.qmod.semantics.error_manager import ErrorManager
|
63
67
|
from classiq.qmod.semantics.validation.model_validation import validate_model
|
68
|
+
from classiq.qmod.utilities import qmod_val_to_expr_str
|
64
69
|
|
65
70
|
|
66
71
|
class BaseInterpreter:
|
@@ -125,7 +130,11 @@ class BaseInterpreter:
|
|
125
130
|
classical_execution_code=self._model.classical_execution_code,
|
126
131
|
execution_preferences=self._model.execution_preferences,
|
127
132
|
functions=list(self._expanded_functions.values()),
|
128
|
-
constants=
|
133
|
+
constants=[
|
134
|
+
const
|
135
|
+
for name, const in QMODULE.constants.items()
|
136
|
+
if name not in builtin_constants
|
137
|
+
],
|
129
138
|
enums=[
|
130
139
|
enum_decl
|
131
140
|
for name, enum_decl in QMODULE.enum_decls.items()
|
@@ -156,14 +165,17 @@ class BaseInterpreter:
|
|
156
165
|
@evaluate.register
|
157
166
|
def evaluate_classical_expression(self, expression: Expression) -> Evaluated:
|
158
167
|
expr = evaluate_classical_expression(expression, self._builder.current_scope)
|
159
|
-
if not isinstance(expr.value, sympy.Basic):
|
160
|
-
return expr
|
161
168
|
vrc = VarRefCollector(ignore_duplicated_handles=True, unevaluated=True)
|
162
|
-
vrc.visit(ast.parse(
|
169
|
+
vrc.visit(ast.parse(qmod_val_to_expr_str(expr.value)))
|
163
170
|
for handle in vrc.var_handles:
|
164
171
|
if handle.name in self._builder.current_scope and isinstance(
|
165
|
-
self._builder.current_scope[handle.name], QuantumSymbol
|
172
|
+
self._builder.current_scope[handle.name].value, QuantumSymbol
|
166
173
|
):
|
174
|
+
if (
|
175
|
+
isinstance(handle, FieldHandleBinding)
|
176
|
+
and handle.field in CLASSICAL_ATTRIBUTES
|
177
|
+
):
|
178
|
+
handle = handle.base_handle
|
167
179
|
self.evaluate(handle)
|
168
180
|
return expr
|
169
181
|
|
@@ -237,20 +249,16 @@ class BaseInterpreter:
|
|
237
249
|
if source_ref is not None
|
238
250
|
else nullcontext()
|
239
251
|
)
|
240
|
-
|
241
|
-
self._model.debug_info[statement.uuid] = FunctionDebugInfo(
|
242
|
-
name="",
|
243
|
-
parameters={},
|
244
|
-
level=OperationLevel.QMOD_STATEMENT,
|
245
|
-
statement_type=None,
|
246
|
-
is_allocate_or_free=False,
|
247
|
-
is_inverse=False,
|
248
|
-
port_to_passed_variable_map={},
|
249
|
-
node=statement._as_back_ref(),
|
250
|
-
)
|
252
|
+
self.add_to_debug_info(statement)
|
251
253
|
with error_context, self._builder.source_ref_context(source_ref):
|
252
254
|
self.emit(statement)
|
253
255
|
|
256
|
+
def add_to_debug_info(self, statement: QuantumStatement) -> None:
|
257
|
+
if statement.uuid not in self._model.debug_info:
|
258
|
+
self._model.debug_info[statement.uuid] = new_function_debug_info_by_node(
|
259
|
+
statement # type: ignore[arg-type]
|
260
|
+
)
|
261
|
+
|
254
262
|
def _expand_operation(self, operation: Closure) -> OperationContext:
|
255
263
|
with self._builder.operation_context(operation) as context:
|
256
264
|
if isinstance(operation, FunctionClosure) and (
|
@@ -4,21 +4,47 @@ import os
|
|
4
4
|
from pydantic import ValidationError
|
5
5
|
|
6
6
|
from classiq.interface.exceptions import ClassiqError
|
7
|
+
from classiq.interface.model.allocate import Allocate
|
8
|
+
from classiq.interface.model.bind_operation import BindOperation
|
9
|
+
from classiq.interface.model.native_function_definition import NativeFunctionDefinition
|
7
10
|
from classiq.interface.model.quantum_function_call import QuantumFunctionCall
|
11
|
+
from classiq.interface.model.quantum_function_declaration import (
|
12
|
+
NamedParamsQuantumFunctionDeclaration,
|
13
|
+
)
|
8
14
|
from classiq.interface.source_reference import SourceReference
|
9
15
|
|
10
16
|
from classiq.model_expansions.closure import FunctionClosure, GenerativeFunctionClosure
|
11
17
|
from classiq.model_expansions.interpreters.generative_interpreter import (
|
12
18
|
GenerativeInterpreter,
|
13
19
|
)
|
20
|
+
from classiq.model_expansions.quantum_operations import BindEmitter
|
21
|
+
from classiq.model_expansions.quantum_operations.allocate import AllocateEmitter
|
14
22
|
from classiq.model_expansions.quantum_operations.quantum_function_call import (
|
15
23
|
DeclarativeQuantumFunctionCallEmitter,
|
16
24
|
)
|
17
25
|
from classiq.model_expansions.scope import Scope
|
26
|
+
from classiq.model_expansions.visitors.symbolic_param_inference import (
|
27
|
+
SymbolicParamInference,
|
28
|
+
)
|
18
29
|
from classiq.qmod.model_state_container import QMODULE
|
19
30
|
|
20
31
|
|
21
32
|
class FrontendGenerativeInterpreter(GenerativeInterpreter):
|
33
|
+
def infer_symbolic_parameters(
|
34
|
+
self,
|
35
|
+
functions: list[NativeFunctionDefinition],
|
36
|
+
additional_signatures: (
|
37
|
+
list[NamedParamsQuantumFunctionDeclaration] | None
|
38
|
+
) = None,
|
39
|
+
) -> None:
|
40
|
+
SymbolicParamInference(functions, additional_signatures).infer()
|
41
|
+
|
42
|
+
def emit_allocate(self, allocate: Allocate) -> None:
|
43
|
+
AllocateEmitter(self, allow_symbolic_size=True).emit(allocate)
|
44
|
+
|
45
|
+
def emit_bind(self, bind: BindOperation) -> None:
|
46
|
+
BindEmitter(self, allow_symbolic_size=True).emit(bind)
|
47
|
+
|
22
48
|
def emit_quantum_function_call(self, call: QuantumFunctionCall) -> None:
|
23
49
|
DeclarativeQuantumFunctionCallEmitter(self).emit(call)
|
24
50
|
|
@@ -61,7 +61,11 @@ from classiq.model_expansions.quantum_operations.allocate import AllocateEmitter
|
|
61
61
|
from classiq.model_expansions.quantum_operations.assignment_result_processor import (
|
62
62
|
AssignmentResultProcessor,
|
63
63
|
)
|
64
|
-
from classiq.model_expansions.quantum_operations.block_evaluator import
|
64
|
+
from classiq.model_expansions.quantum_operations.block_evaluator import (
|
65
|
+
BlockEvaluator,
|
66
|
+
IfElimination,
|
67
|
+
RepeatElimination,
|
68
|
+
)
|
65
69
|
from classiq.model_expansions.quantum_operations.composite_emitter import (
|
66
70
|
CompositeEmitter,
|
67
71
|
)
|
@@ -93,6 +97,18 @@ class GenerativeInterpreter(BaseInterpreter):
|
|
93
97
|
add_generative_functions_to_scope(
|
94
98
|
generative_functions, self._top_level_scope, override_atomic=True
|
95
99
|
)
|
100
|
+
self.infer_symbolic_parameters(
|
101
|
+
model.functions, [gen_func.func_decl for gen_func in generative_functions]
|
102
|
+
)
|
103
|
+
|
104
|
+
def infer_symbolic_parameters(
|
105
|
+
self,
|
106
|
+
functions: list[NativeFunctionDefinition],
|
107
|
+
additional_signatures: (
|
108
|
+
list[NamedParamsQuantumFunctionDeclaration] | None
|
109
|
+
) = None,
|
110
|
+
) -> None:
|
111
|
+
pass
|
96
112
|
|
97
113
|
def evaluate_lambda(self, function: QuantumLambdaFunction) -> Evaluated:
|
98
114
|
func_decl = NamedParamsQuantumFunctionDeclaration(
|
@@ -140,10 +156,16 @@ class GenerativeInterpreter(BaseInterpreter):
|
|
140
156
|
QuantumFunctionCallEmitter(self).emit(call)
|
141
157
|
|
142
158
|
@emit.register
|
159
|
+
def _emit_allocate(self, allocate: Allocate) -> None:
|
160
|
+
return self.emit_allocate(allocate)
|
161
|
+
|
143
162
|
def emit_allocate(self, allocate: Allocate) -> None:
|
144
163
|
AllocateEmitter(self).emit(allocate)
|
145
164
|
|
146
165
|
@emit.register
|
166
|
+
def _emit_bind(self, bind: BindOperation) -> None:
|
167
|
+
self.emit_bind(bind)
|
168
|
+
|
147
169
|
def emit_bind(self, bind: BindOperation) -> None:
|
148
170
|
BindEmitter(self).emit(bind)
|
149
171
|
|
@@ -198,6 +220,7 @@ class GenerativeInterpreter(BaseInterpreter):
|
|
198
220
|
self,
|
199
221
|
[
|
200
222
|
ExpressionEvaluator(self, "condition"),
|
223
|
+
IfElimination(self),
|
201
224
|
BlockEvaluator(
|
202
225
|
self,
|
203
226
|
CLASSICAL_IF_OPERATOR_NAME,
|
@@ -231,6 +254,7 @@ class GenerativeInterpreter(BaseInterpreter):
|
|
231
254
|
self,
|
232
255
|
[
|
233
256
|
ExpressionEvaluator(self, "count"),
|
257
|
+
RepeatElimination(self),
|
234
258
|
RepeatBlockEvaluator(self, REPEAT_OPERATOR_NAME, "body"),
|
235
259
|
],
|
236
260
|
).emit(repeat)
|
@@ -1,7 +1,13 @@
|
|
1
|
+
from typing import TYPE_CHECKING
|
2
|
+
|
3
|
+
import sympy
|
4
|
+
|
1
5
|
from classiq.interface.debug_info.debug_info import FunctionDebugInfo
|
2
6
|
from classiq.interface.exceptions import ClassiqValueError
|
3
7
|
from classiq.interface.generator.expressions.expression import Expression
|
4
|
-
from classiq.interface.generator.
|
8
|
+
from classiq.interface.generator.expressions.proxies.classical.any_classical_value import (
|
9
|
+
AnyClassicalValue,
|
10
|
+
)
|
5
11
|
from classiq.interface.model.allocate import Allocate
|
6
12
|
from classiq.interface.model.handle_binding import NestedHandleBinding
|
7
13
|
from classiq.interface.model.quantum_type import QuantumBitvector
|
@@ -10,8 +16,17 @@ from classiq.model_expansions.evaluators.quantum_type_utils import copy_type_inf
|
|
10
16
|
from classiq.model_expansions.quantum_operations.emitter import Emitter
|
11
17
|
from classiq.model_expansions.scope import QuantumSymbol
|
12
18
|
|
19
|
+
if TYPE_CHECKING:
|
20
|
+
from classiq.model_expansions.interpreters.base_interpreter import BaseInterpreter
|
21
|
+
|
13
22
|
|
14
23
|
class AllocateEmitter(Emitter[Allocate]):
|
24
|
+
def __init__(
|
25
|
+
self, interpreter: "BaseInterpreter", allow_symbolic_size: bool = False
|
26
|
+
) -> None:
|
27
|
+
super().__init__(interpreter)
|
28
|
+
self._allow_symbolic_size = allow_symbolic_size
|
29
|
+
|
15
30
|
def emit(self, allocate: Allocate, /) -> bool:
|
16
31
|
target: QuantumSymbol = self._interpreter.evaluate(allocate.target).as_type(
|
17
32
|
QuantumSymbol
|
@@ -22,10 +37,10 @@ class AllocateEmitter(Emitter[Allocate]):
|
|
22
37
|
f"Cannot allocate partial quantum variable {str(target.handle)!r}"
|
23
38
|
)
|
24
39
|
|
25
|
-
|
40
|
+
size_expr = self._get_var_size(target, allocate.size)
|
26
41
|
allocate = allocate.model_copy(
|
27
42
|
update=dict(
|
28
|
-
size=Expression(expr=
|
43
|
+
size=Expression(expr=size_expr),
|
29
44
|
target=target.handle,
|
30
45
|
back_ref=allocate.uuid,
|
31
46
|
)
|
@@ -34,27 +49,31 @@ class AllocateEmitter(Emitter[Allocate]):
|
|
34
49
|
self.emit_statement(allocate)
|
35
50
|
return True
|
36
51
|
|
37
|
-
def _get_var_size(self, target: QuantumSymbol, size: Expression | None) ->
|
52
|
+
def _get_var_size(self, target: QuantumSymbol, size: Expression | None) -> str:
|
38
53
|
if size is None:
|
39
54
|
if not target.quantum_type.is_evaluated:
|
40
55
|
raise ClassiqValueError(
|
41
56
|
f"Could not infer the size of variable {str(target.handle)!r}"
|
42
57
|
)
|
43
|
-
return target.quantum_type.size_in_bits
|
58
|
+
return str(target.quantum_type.size_in_bits)
|
44
59
|
|
45
60
|
size_value = self._interpreter.evaluate(size).value
|
61
|
+
if self._allow_symbolic_size and isinstance(
|
62
|
+
size_value, (sympy.Basic, AnyClassicalValue)
|
63
|
+
):
|
64
|
+
return str(size_value)
|
46
65
|
if not isinstance(size_value, (int, float)):
|
47
66
|
raise ClassiqValueError(
|
48
67
|
f"The number of allocated qubits must be an integer. Got "
|
49
68
|
f"{str(size_value)!r}"
|
50
69
|
)
|
51
|
-
|
70
|
+
size_expr = str(size_value)
|
52
71
|
copy_type_information(
|
53
|
-
QuantumBitvector(length=Expression(expr=
|
72
|
+
QuantumBitvector(length=Expression(expr=size_expr)),
|
54
73
|
target.quantum_type,
|
55
74
|
str(target.handle),
|
56
75
|
)
|
57
|
-
return
|
76
|
+
return size_expr
|
58
77
|
|
59
78
|
def _register_debug_info(self, allocate: Allocate) -> None:
|
60
79
|
if (
|
@@ -67,9 +86,6 @@ class AllocateEmitter(Emitter[Allocate]):
|
|
67
86
|
parameters["num_qubits"] = allocate.size.expr
|
68
87
|
self._debug_info[allocate.uuid] = FunctionDebugInfo(
|
69
88
|
name="allocate",
|
70
|
-
parameters=parameters,
|
71
|
-
level=OperationLevel.QMOD_STATEMENT,
|
72
|
-
is_allocate_or_free=True,
|
73
89
|
port_to_passed_variable_map={"ARG": str(allocate.target)},
|
74
90
|
node=allocate._as_back_ref(),
|
75
91
|
)
|