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,12 @@
|
|
1
|
+
from classiq.interface.exceptions import ClassiqExpansionError
|
1
2
|
from classiq.interface.generator.arith.arithmetic import compute_arithmetic_result_type
|
2
|
-
from classiq.interface.generator.
|
3
|
-
|
3
|
+
from classiq.interface.generator.expressions.expression import Expression
|
4
|
+
from classiq.interface.model.allocate import Allocate
|
5
|
+
from classiq.interface.model.bind_operation import BindOperation
|
6
|
+
from classiq.interface.model.handle_binding import (
|
7
|
+
ConcreteHandleBinding,
|
8
|
+
HandleBinding,
|
9
|
+
SubscriptHandleBinding,
|
4
10
|
)
|
5
11
|
from classiq.interface.model.quantum_expressions.arithmetic_operation import (
|
6
12
|
ArithmeticOperation,
|
@@ -9,6 +15,12 @@ from classiq.interface.model.quantum_expressions.arithmetic_operation import (
|
|
9
15
|
from classiq.interface.model.quantum_expressions.quantum_expression import (
|
10
16
|
QuantumAssignmentOperation,
|
11
17
|
)
|
18
|
+
from classiq.interface.model.quantum_function_call import QuantumFunctionCall
|
19
|
+
from classiq.interface.model.quantum_type import QuantumBitvector, QuantumNumeric
|
20
|
+
from classiq.interface.model.variable_declaration_statement import (
|
21
|
+
VariableDeclarationStatement,
|
22
|
+
)
|
23
|
+
from classiq.interface.model.within_apply_operation import WithinApply
|
12
24
|
|
13
25
|
from classiq.model_expansions.evaluators.quantum_type_utils import copy_type_information
|
14
26
|
from classiq.model_expansions.quantum_operations.arithmetic.explicit_boolean_expressions import (
|
@@ -18,24 +30,49 @@ from classiq.model_expansions.quantum_operations.arithmetic.explicit_boolean_exp
|
|
18
30
|
from classiq.model_expansions.quantum_operations.emitter import Emitter
|
19
31
|
from classiq.model_expansions.scope import QuantumSymbol
|
20
32
|
from classiq.model_expansions.transformers.ast_renamer import rename_variables
|
33
|
+
from classiq.qmod.builtins.functions.standard_gates import CX
|
21
34
|
|
22
35
|
|
23
36
|
class AssignmentResultProcessor(Emitter[QuantumAssignmentOperation]):
|
24
37
|
def emit(self, op: QuantumAssignmentOperation, /) -> bool:
|
25
|
-
if (
|
38
|
+
if not (
|
26
39
|
isinstance(op, ArithmeticOperation)
|
27
40
|
and op.operation_kind == ArithmeticOperationKind.Assignment
|
28
41
|
):
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
42
|
+
return False
|
43
|
+
|
44
|
+
result_symbol = self._interpreter.evaluate(op.result_var).as_type(QuantumSymbol)
|
45
|
+
result_type = result_symbol.quantum_type
|
46
|
+
|
47
|
+
validate_assignment_bool_expression(
|
48
|
+
result_symbol, op.expression.expr, op.operation_kind
|
49
|
+
)
|
50
|
+
convert_assignment_bool_expression(op)
|
51
|
+
|
52
|
+
inferred_result_type = self._infer_result_type(op)
|
53
|
+
if inferred_result_type is None:
|
54
|
+
return False
|
55
|
+
|
56
|
+
if not isinstance(result_type, QuantumNumeric):
|
57
|
+
copy_type_information(
|
58
|
+
inferred_result_type, result_symbol.quantum_type, str(op.result_var)
|
59
|
+
)
|
60
|
+
return False
|
61
|
+
|
62
|
+
self._copy_numeric_attributes(result_type, inferred_result_type)
|
63
|
+
if self._same_numeric_attributes(result_type, inferred_result_type):
|
64
|
+
return False
|
65
|
+
|
66
|
+
self._validate_declared_attributes(
|
67
|
+
result_type, inferred_result_type, str(op.result_var)
|
68
|
+
)
|
69
|
+
self._assign_to_inferred_var_and_bind(op, result_type, inferred_result_type)
|
70
|
+
return True
|
71
|
+
|
72
|
+
def _infer_result_type(self, op: ArithmeticOperation) -> QuantumNumeric | None:
|
38
73
|
expr = self._evaluate_expression(op.expression)
|
74
|
+
if len(self._get_classical_vars_in_expression(expr)):
|
75
|
+
return None
|
39
76
|
symbols = self._get_symbols_in_expression(expr)
|
40
77
|
expr_str = rename_variables(
|
41
78
|
expr.expr,
|
@@ -46,16 +83,180 @@ class AssignmentResultProcessor(Emitter[QuantumAssignmentOperation]):
|
|
46
83
|
expr_str = expr_str.replace(
|
47
84
|
symbol.handle.qmod_expr, symbol.handle.identifier
|
48
85
|
)
|
49
|
-
|
86
|
+
return compute_arithmetic_result_type(
|
50
87
|
expr_str,
|
51
88
|
{symbol.handle.identifier: symbol.quantum_type for symbol in symbols},
|
52
89
|
self._machine_precision,
|
53
90
|
)
|
54
|
-
result_symbol = self._interpreter.evaluate(op.result_var).as_type(QuantumSymbol)
|
55
91
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
92
|
+
@staticmethod
|
93
|
+
def _copy_numeric_attributes(
|
94
|
+
result_type: QuantumNumeric, inferred_result_type: QuantumNumeric
|
95
|
+
) -> None:
|
96
|
+
if not result_type.has_size_in_bits:
|
97
|
+
result_type.size = Expression(expr=str(inferred_result_type.size_in_bits))
|
98
|
+
if not result_type.has_sign:
|
99
|
+
result_type.is_signed = Expression(
|
100
|
+
expr=str(inferred_result_type.sign_value)
|
101
|
+
)
|
102
|
+
if not result_type.has_fraction_digits:
|
103
|
+
result_type.fraction_digits = Expression(
|
104
|
+
expr=str(inferred_result_type.fraction_digits_value)
|
105
|
+
)
|
106
|
+
|
107
|
+
@staticmethod
|
108
|
+
def _same_numeric_attributes(
|
109
|
+
result_type: QuantumNumeric, inferred_result_type: QuantumNumeric
|
110
|
+
) -> bool:
|
111
|
+
return (
|
112
|
+
result_type.size_in_bits == inferred_result_type.size_in_bits
|
113
|
+
and result_type.sign_value == inferred_result_type.sign_value
|
114
|
+
and result_type.fraction_digits_value
|
115
|
+
== inferred_result_type.fraction_digits_value
|
116
|
+
)
|
117
|
+
|
118
|
+
@classmethod
|
119
|
+
def _validate_declared_attributes(
|
120
|
+
cls, result_type: QuantumNumeric, inferred_result_type: QuantumNumeric, var: str
|
121
|
+
) -> None:
|
122
|
+
result_size, result_sign, result_fractions = (
|
123
|
+
result_type.size_in_bits,
|
124
|
+
result_type.sign_value,
|
125
|
+
result_type.fraction_digits_value,
|
126
|
+
)
|
127
|
+
inferred_size, inferred_sign, inferred_fractions = (
|
128
|
+
inferred_result_type.size_in_bits,
|
129
|
+
inferred_result_type.sign_value,
|
130
|
+
inferred_result_type.fraction_digits_value,
|
131
|
+
)
|
132
|
+
result_integers = result_size - result_fractions
|
133
|
+
inferred_integers = inferred_size - inferred_fractions
|
134
|
+
|
135
|
+
if (
|
136
|
+
(result_integers < inferred_integers)
|
137
|
+
or (result_fractions < inferred_fractions)
|
138
|
+
or (not result_sign and inferred_sign)
|
139
|
+
or (
|
140
|
+
result_sign
|
141
|
+
and not inferred_sign
|
142
|
+
and result_integers == inferred_integers
|
143
|
+
)
|
144
|
+
):
|
145
|
+
if (
|
146
|
+
not result_sign
|
147
|
+
and result_fractions == 0
|
148
|
+
and not inferred_sign
|
149
|
+
and inferred_fractions == 0
|
150
|
+
):
|
151
|
+
result_size_str = f"size {result_size}"
|
152
|
+
inferred_size_str = f"size {inferred_size}"
|
153
|
+
hint = f"Hint: increase the size in the declaration of {var!r} or omit it to enable automatic inference."
|
154
|
+
else:
|
155
|
+
result_size_str = f"size {result_size}, {'signed' if result_sign else 'unsigned'}, and {result_fractions} fraction digits"
|
156
|
+
inferred_size_str = f"size {inferred_size}, {'signed' if inferred_sign else 'unsigned'}, and {inferred_fractions} fraction digits"
|
157
|
+
hint = f"Hint: omit the numeric attributes from the declaration of {var!r} to enable automatic inference."
|
158
|
+
raise ClassiqExpansionError(
|
159
|
+
f"Cannot assign an expression with inferred {inferred_size_str} to variable {var!r} with declared {result_size_str}. {hint}"
|
160
|
+
)
|
161
|
+
|
162
|
+
@staticmethod
|
163
|
+
def _craft_size_string(size: int, is_signed: bool, fraction_digits: int) -> str:
|
164
|
+
extra = (
|
165
|
+
f", with {fraction_digits} fraction digits" if fraction_digits > 0 else ""
|
166
|
+
)
|
167
|
+
return f"{size} ({'signed' if is_signed else 'unsigned'}{extra})"
|
168
|
+
|
169
|
+
def _assign_to_inferred_var_and_bind(
|
170
|
+
self,
|
171
|
+
op: ArithmeticOperation,
|
172
|
+
result_type: QuantumNumeric,
|
173
|
+
inferred_result_type: QuantumNumeric,
|
174
|
+
) -> None:
|
175
|
+
handles: list[HandleBinding] = []
|
176
|
+
|
177
|
+
extra_fraction_digits = (
|
178
|
+
result_type.fraction_digits_value
|
179
|
+
- inferred_result_type.fraction_digits_value
|
180
|
+
)
|
181
|
+
if extra_fraction_digits > 0:
|
182
|
+
handles.append(
|
183
|
+
self._declare_qarray("extra_fraction_digits", extra_fraction_digits)
|
184
|
+
)
|
185
|
+
|
186
|
+
inferred_result_name = self._counted_name_allocator.allocate("inferred_result")
|
187
|
+
inferred_result_handle = HandleBinding(name=inferred_result_name)
|
188
|
+
self._interpreter.emit(
|
189
|
+
VariableDeclarationStatement(
|
190
|
+
name=inferred_result_name, quantum_type=inferred_result_type
|
191
|
+
)
|
192
|
+
)
|
193
|
+
handles.append(inferred_result_handle)
|
194
|
+
modified_op = op.model_copy(update={"result_var": inferred_result_handle})
|
195
|
+
self._interpreter.add_to_debug_info(modified_op)
|
196
|
+
self._interpreter.emit(modified_op)
|
197
|
+
|
198
|
+
result_integer_size = (
|
199
|
+
result_type.size_in_bits - result_type.fraction_digits_value
|
200
|
+
)
|
201
|
+
inferred_result_integer_size = (
|
202
|
+
inferred_result_type.size_in_bits
|
203
|
+
- inferred_result_type.fraction_digits_value
|
204
|
+
)
|
205
|
+
extra_integers = result_integer_size - inferred_result_integer_size
|
206
|
+
if extra_integers > 0:
|
207
|
+
handles.append(self._declare_qarray("extra_integers", extra_integers))
|
208
|
+
|
209
|
+
self._interpreter.emit(
|
210
|
+
BindOperation(in_handles=handles, out_handles=[op.result_var])
|
211
|
+
)
|
212
|
+
|
213
|
+
if (
|
214
|
+
result_type.sign_value
|
215
|
+
and inferred_result_type.sign_value
|
216
|
+
and extra_integers > 0
|
217
|
+
):
|
218
|
+
sign_idx = result_type.size_in_bits - extra_integers - 1
|
219
|
+
self._sign_extension(op.result_var, sign_idx, result_type.size_in_bits)
|
220
|
+
|
221
|
+
def _declare_qarray(
|
222
|
+
self,
|
223
|
+
prefix: str,
|
224
|
+
size: int,
|
225
|
+
allocate: bool = True,
|
226
|
+
) -> HandleBinding:
|
227
|
+
name = self._counted_name_allocator.allocate(prefix)
|
228
|
+
handle = HandleBinding(name=name)
|
229
|
+
quantum_type = QuantumBitvector(length=Expression(expr=str(size)))
|
230
|
+
self._interpreter.emit(
|
231
|
+
VariableDeclarationStatement(name=name, quantum_type=quantum_type)
|
232
|
+
)
|
233
|
+
if allocate:
|
234
|
+
self._interpreter.emit(Allocate(target=handle))
|
235
|
+
return handle
|
236
|
+
|
237
|
+
def _sign_extension(
|
238
|
+
self,
|
239
|
+
result_var: ConcreteHandleBinding,
|
240
|
+
sign_idx: int,
|
241
|
+
size: int,
|
242
|
+
) -> None:
|
243
|
+
aux = self._declare_qarray("inferred_result_aux", size, allocate=False)
|
244
|
+
self._interpreter.emit(
|
245
|
+
WithinApply(
|
246
|
+
compute=[BindOperation(in_handles=[result_var], out_handles=[aux])],
|
247
|
+
action=[
|
248
|
+
QuantumFunctionCall(
|
249
|
+
function=CX.func_decl.name,
|
250
|
+
positional_args=[
|
251
|
+
SubscriptHandleBinding(
|
252
|
+
base_handle=aux, index=Expression(expr=str(sign_idx))
|
253
|
+
),
|
254
|
+
SubscriptHandleBinding(
|
255
|
+
base_handle=aux, index=Expression(expr=str(idx))
|
256
|
+
),
|
257
|
+
],
|
258
|
+
)
|
259
|
+
for idx in range(sign_idx + 1, size)
|
260
|
+
],
|
261
|
+
)
|
61
262
|
)
|
@@ -1,3 +1,5 @@
|
|
1
|
+
from typing import TYPE_CHECKING
|
2
|
+
|
1
3
|
from classiq.interface.exceptions import (
|
2
4
|
ClassiqExpansionError,
|
3
5
|
ClassiqInternalExpansionError,
|
@@ -14,41 +16,21 @@ from classiq.model_expansions.evaluators.quantum_type_utils import (
|
|
14
16
|
from classiq.model_expansions.quantum_operations.emitter import Emitter
|
15
17
|
from classiq.model_expansions.scope import Evaluated, QuantumSymbol
|
16
18
|
|
19
|
+
if TYPE_CHECKING:
|
20
|
+
from classiq.model_expansions.interpreters.base_interpreter import BaseInterpreter
|
21
|
+
|
17
22
|
|
18
23
|
class BindEmitter(Emitter[BindOperation]):
|
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
|
+
|
19
30
|
def emit(self, bind: BindOperation, /) -> bool:
|
20
31
|
inputs, outputs = self._get_inputs_outputs(bind)
|
21
32
|
validate_bind_targets(bind, self._current_scope)
|
22
|
-
|
23
|
-
output for output in outputs if not output.quantum_type.has_size_in_bits
|
24
|
-
]
|
25
|
-
|
26
|
-
if len(unsized_outputs) > 1:
|
27
|
-
raise ClassiqExpansionError(
|
28
|
-
f"Cannot perform the split operation {bind.in_handles[0].name} -> {{{', '.join(out_handle.name for out_handle in bind.out_handles)}}}:\n"
|
29
|
-
f"Quantum variables {', '.join(str(out_handle.handle) for out_handle in unsized_outputs)} are used as bind outputs, but their size cannot be inferred."
|
30
|
-
)
|
31
|
-
|
32
|
-
input_size = sum(input.quantum_type.size_in_bits for input in inputs)
|
33
|
-
output_size = sum(
|
34
|
-
output.quantum_type.size_in_bits
|
35
|
-
for output in outputs
|
36
|
-
if output.quantum_type.has_size_in_bits
|
37
|
-
)
|
38
|
-
|
39
|
-
if len(unsized_outputs) == 1:
|
40
|
-
set_size(
|
41
|
-
unsized_outputs[0].quantum_type,
|
42
|
-
input_size - output_size,
|
43
|
-
str(unsized_outputs[0].handle),
|
44
|
-
)
|
45
|
-
|
46
|
-
else:
|
47
|
-
if input_size != output_size:
|
48
|
-
raise ClassiqExpansionError(
|
49
|
-
f"The total size for the input and output of the bind operation must be the same. The in size is {input_size} and the out size is {output_size}"
|
50
|
-
)
|
51
|
-
|
33
|
+
self._process_var_sizes(bind, inputs, outputs)
|
52
34
|
self.emit_statement(
|
53
35
|
BindOperation(
|
54
36
|
in_handles=bind.in_handles,
|
@@ -111,3 +93,45 @@ class BindEmitter(Emitter[BindOperation]):
|
|
111
93
|
f"{out.value.handle.name!r} on the right-hand side of a bind "
|
112
94
|
f"statement"
|
113
95
|
)
|
96
|
+
|
97
|
+
def _process_var_sizes(
|
98
|
+
self,
|
99
|
+
bind: BindOperation,
|
100
|
+
inputs: list[QuantumSymbol],
|
101
|
+
outputs: list[QuantumSymbol],
|
102
|
+
) -> None:
|
103
|
+
unsized_inputs = [
|
104
|
+
input for input in inputs if not input.quantum_type.has_size_in_bits
|
105
|
+
]
|
106
|
+
if len(unsized_inputs) > 0:
|
107
|
+
if self._allow_symbolic_size:
|
108
|
+
return
|
109
|
+
raise ClassiqInternalExpansionError("Uninitialized bind inputs")
|
110
|
+
|
111
|
+
unsized_outputs = [
|
112
|
+
output for output in outputs if not output.quantum_type.has_size_in_bits
|
113
|
+
]
|
114
|
+
if len(unsized_outputs) > 1:
|
115
|
+
if self._allow_symbolic_size:
|
116
|
+
return
|
117
|
+
raise ClassiqExpansionError(
|
118
|
+
f"Cannot perform the split operation {bind.in_handles[0].name} -> {{{', '.join(out_handle.name for out_handle in bind.out_handles)}}}:\n"
|
119
|
+
f"Quantum variables {', '.join(str(out_handle.handle) for out_handle in unsized_outputs)} are used as bind outputs, but their size cannot be inferred."
|
120
|
+
)
|
121
|
+
|
122
|
+
input_size = sum(input.quantum_type.size_in_bits for input in inputs)
|
123
|
+
output_size = sum(
|
124
|
+
output.quantum_type.size_in_bits
|
125
|
+
for output in outputs
|
126
|
+
if output.quantum_type.has_size_in_bits
|
127
|
+
)
|
128
|
+
if len(unsized_outputs) == 1:
|
129
|
+
set_size(
|
130
|
+
unsized_outputs[0].quantum_type,
|
131
|
+
input_size - output_size,
|
132
|
+
str(unsized_outputs[0].handle),
|
133
|
+
)
|
134
|
+
elif input_size != output_size:
|
135
|
+
raise ClassiqExpansionError(
|
136
|
+
f"The total size for the input and output of the bind operation must be the same. The in size is {input_size} and the out size is {output_size}"
|
137
|
+
)
|
@@ -1,8 +1,14 @@
|
|
1
1
|
from collections.abc import Sequence
|
2
2
|
from typing import TYPE_CHECKING
|
3
3
|
|
4
|
+
from classiq.interface.generator.functions.builtins.internal_operators import (
|
5
|
+
CLASSICAL_IF_OPERATOR_NAME,
|
6
|
+
REPEAT_OPERATOR_NAME,
|
7
|
+
)
|
8
|
+
from classiq.interface.model.classical_if import ClassicalIf
|
4
9
|
from classiq.interface.model.quantum_function_declaration import PositionalArg
|
5
10
|
from classiq.interface.model.quantum_statement import QuantumOperation, QuantumStatement
|
11
|
+
from classiq.interface.model.repeat import Repeat
|
6
12
|
|
7
13
|
from classiq.model_expansions.closure import Closure
|
8
14
|
from classiq.model_expansions.quantum_operations.emitter import Emitter
|
@@ -88,3 +94,39 @@ class BlockEvaluator(Emitter[QuantumOperation]):
|
|
88
94
|
|
89
95
|
def get_scope(self, op: QuantumOperation) -> Scope:
|
90
96
|
return Scope(parent=self._current_scope)
|
97
|
+
|
98
|
+
|
99
|
+
class IfElimination(BlockEvaluator):
|
100
|
+
def __init__(self, interpreter: "BaseInterpreter") -> None:
|
101
|
+
super().__init__(interpreter, CLASSICAL_IF_OPERATOR_NAME, "then", "else_")
|
102
|
+
|
103
|
+
def emit(self, op: ClassicalIf, /) -> bool: # type:ignore[override]
|
104
|
+
cond = op.condition.value.value
|
105
|
+
if not isinstance(cond, bool):
|
106
|
+
return False
|
107
|
+
if op.is_generative():
|
108
|
+
if cond:
|
109
|
+
then_block = op.get_generative_block("then")
|
110
|
+
op.clear_generative_blocks()
|
111
|
+
op.set_generative_block("then", then_block)
|
112
|
+
elif not op.has_generative_block("else_"):
|
113
|
+
op.clear_generative_blocks()
|
114
|
+
else:
|
115
|
+
else_block = op.get_generative_block("else_")
|
116
|
+
op.clear_generative_blocks()
|
117
|
+
op.set_generative_block("else_", else_block)
|
118
|
+
else:
|
119
|
+
if cond:
|
120
|
+
op = op.model_copy(update={"else_": []})
|
121
|
+
else:
|
122
|
+
op = op.model_copy(update={"then": []})
|
123
|
+
return super().emit(op)
|
124
|
+
|
125
|
+
|
126
|
+
class RepeatElimination(BlockEvaluator):
|
127
|
+
def __init__(self, interpreter: "BaseInterpreter") -> None:
|
128
|
+
super().__init__(interpreter, REPEAT_OPERATOR_NAME, "body")
|
129
|
+
|
130
|
+
def emit(self, op: Repeat, /) -> bool: # type:ignore[override]
|
131
|
+
count = op.count.value.value
|
132
|
+
return isinstance(count, int) and count == 0
|
@@ -10,7 +10,10 @@ from uuid import UUID
|
|
10
10
|
|
11
11
|
import sympy
|
12
12
|
|
13
|
-
from classiq.interface.debug_info.debug_info import
|
13
|
+
from classiq.interface.debug_info.debug_info import (
|
14
|
+
FunctionDebugInfo,
|
15
|
+
calculate_port_to_passed_variable_mapping,
|
16
|
+
)
|
14
17
|
from classiq.interface.exceptions import ClassiqExpansionError
|
15
18
|
from classiq.interface.generator.expressions.proxies.classical.any_classical_value import (
|
16
19
|
AnyClassicalValue,
|
@@ -24,7 +27,6 @@ from classiq.interface.generator.expressions.proxies.classical.qmod_struct_insta
|
|
24
27
|
from classiq.interface.generator.functions.port_declaration import (
|
25
28
|
PortDeclarationDirection,
|
26
29
|
)
|
27
|
-
from classiq.interface.generator.generated_circuit_data import OperationLevel
|
28
30
|
from classiq.interface.model.classical_parameter_declaration import (
|
29
31
|
ClassicalParameterDeclaration,
|
30
32
|
)
|
@@ -63,7 +65,6 @@ from classiq.model_expansions.quantum_operations.emitter import (
|
|
63
65
|
from classiq.model_expansions.scope import Evaluated, QuantumSymbol, Scope
|
64
66
|
from classiq.model_expansions.transformers.var_splitter import VarSplitter
|
65
67
|
from classiq.model_expansions.utils.text_utils import are, readable_list, s
|
66
|
-
from classiq.qmod.builtins.functions import free
|
67
68
|
from classiq.qmod.semantics.validation.signature_validation import (
|
68
69
|
validate_function_signature,
|
69
70
|
)
|
@@ -92,13 +93,15 @@ def _validate_cloning(evaluated_args: list[Evaluated]) -> None:
|
|
92
93
|
|
93
94
|
|
94
95
|
def _is_symbolic(arg: Any) -> bool:
|
96
|
+
if isinstance(arg, (ClassicalProxy, AnyClassicalValue)):
|
97
|
+
return True
|
95
98
|
if isinstance(arg, list):
|
96
99
|
return any(_is_symbolic(item) for item in arg)
|
97
100
|
if isinstance(arg, QmodStructInstance):
|
98
101
|
return any(_is_symbolic(item) for item in arg.fields.values())
|
99
102
|
if isinstance(arg, sympy.Basic):
|
100
103
|
return len(arg.free_symbols) > 0
|
101
|
-
return
|
104
|
+
return False
|
102
105
|
|
103
106
|
|
104
107
|
class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSplitter):
|
@@ -188,17 +191,16 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
|
|
188
191
|
positional_args=new_positional_args,
|
189
192
|
back_ref=self._get_back_ref(propagated_debug_info),
|
190
193
|
)
|
191
|
-
is_allocate_or_free = new_call.func_name == free.func_decl.name
|
192
194
|
|
193
|
-
port_to_passed_variable_map =
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
195
|
+
port_to_passed_variable_map = calculate_port_to_passed_variable_mapping(
|
196
|
+
new_positional_arg_decls,
|
197
|
+
[
|
198
|
+
arg.value.handle if isinstance(arg.value, QuantumSymbol) else None
|
199
|
+
for arg in evaluated_args
|
200
|
+
],
|
201
|
+
)
|
198
202
|
self._debug_info[new_call.uuid] = FunctionDebugInfo(
|
199
203
|
name=new_call.func_name,
|
200
|
-
level=OperationLevel.QMOD_FUNCTION_CALL,
|
201
|
-
is_allocate_or_free=is_allocate_or_free,
|
202
204
|
port_to_passed_variable_map=port_to_passed_variable_map,
|
203
205
|
node=new_call._as_back_ref(),
|
204
206
|
)
|
@@ -1,5 +1,5 @@
|
|
1
1
|
from itertools import chain
|
2
|
-
from typing import TYPE_CHECKING, Generic
|
2
|
+
from typing import TYPE_CHECKING, Generic, Optional
|
3
3
|
|
4
4
|
from classiq.interface.generator.functions.port_declaration import (
|
5
5
|
PortDeclarationDirection,
|
@@ -34,13 +34,24 @@ class DeclarativeCallEmitter(
|
|
34
34
|
|
35
35
|
if self._is_function_purely_declarative(
|
36
36
|
function
|
37
|
-
) and self._are_args_purely_declarative(args):
|
37
|
+
) and self._are_args_purely_declarative(args, function.name):
|
38
38
|
self._interpreter.add_purely_declarative_function(function)
|
39
39
|
return False
|
40
40
|
|
41
41
|
return True
|
42
42
|
|
43
|
-
def _is_function_purely_declarative(
|
43
|
+
def _is_function_purely_declarative(
|
44
|
+
self, function: FunctionClosure, seen_funcs: Optional[set[str]] = None
|
45
|
+
) -> bool:
|
46
|
+
if seen_funcs is None:
|
47
|
+
seen_funcs = set()
|
48
|
+
if function.name in seen_funcs:
|
49
|
+
return True
|
50
|
+
seen_funcs.add(function.name)
|
51
|
+
|
52
|
+
if function.is_atomic:
|
53
|
+
return True
|
54
|
+
|
44
55
|
if function.name not in QMODULE.native_defs:
|
45
56
|
return False
|
46
57
|
|
@@ -56,9 +67,9 @@ class DeclarativeCallEmitter(
|
|
56
67
|
return False
|
57
68
|
|
58
69
|
dependencies = QMODULE.function_dependencies[function.name]
|
59
|
-
return self._are_identifiers_purely_declarative(dependencies)
|
70
|
+
return self._are_identifiers_purely_declarative(dependencies, seen_funcs)
|
60
71
|
|
61
|
-
def _are_args_purely_declarative(self, args: list[Evaluated]) -> bool:
|
72
|
+
def _are_args_purely_declarative(self, args: list[Evaluated], caller: str) -> bool:
|
62
73
|
values = [arg.value for arg in args]
|
63
74
|
function_inputs: list[FunctionClosure] = list(
|
64
75
|
chain.from_iterable(
|
@@ -78,10 +89,13 @@ class DeclarativeCallEmitter(
|
|
78
89
|
if any(func.is_lambda for func in function_inputs):
|
79
90
|
return False
|
80
91
|
dependencies = [func.name for func in function_inputs if not func.is_lambda]
|
81
|
-
return self._are_identifiers_purely_declarative(dependencies)
|
92
|
+
return self._are_identifiers_purely_declarative(dependencies, {caller})
|
82
93
|
|
83
|
-
def _are_identifiers_purely_declarative(
|
84
|
-
|
85
|
-
|
94
|
+
def _are_identifiers_purely_declarative(
|
95
|
+
self, dependencies: list[str], seen_funcs: set[str]
|
96
|
+
) -> bool:
|
97
|
+
return all(
|
98
|
+
self._is_function_purely_declarative(self._current_scope[dep].value)
|
86
99
|
for dep in dependencies
|
100
|
+
if seen_funcs is None or dep not in seen_funcs
|
87
101
|
)
|
@@ -13,9 +13,13 @@ import sympy
|
|
13
13
|
|
14
14
|
from classiq.interface.debug_info.debug_info import (
|
15
15
|
DebugInfoCollection,
|
16
|
-
new_function_debug_info_by_node,
|
17
16
|
)
|
18
17
|
from classiq.interface.exceptions import ClassiqInternalExpansionError
|
18
|
+
from classiq.interface.generator.expressions.atomic_expression_functions import (
|
19
|
+
CLASSICAL_ATTRIBUTES,
|
20
|
+
SUPPORTED_CLASSIQ_BUILTIN_FUNCTIONS,
|
21
|
+
SUPPORTED_PYTHON_BUILTIN_FUNCTIONS,
|
22
|
+
)
|
19
23
|
from classiq.interface.generator.expressions.evaluated_expression import (
|
20
24
|
EvaluatedExpression,
|
21
25
|
)
|
@@ -31,7 +35,11 @@ from classiq.interface.generator.functions.port_declaration import (
|
|
31
35
|
PortDeclarationDirection,
|
32
36
|
)
|
33
37
|
from classiq.interface.helpers.pydantic_model_helpers import nameables_to_dict
|
34
|
-
from classiq.interface.model.handle_binding import
|
38
|
+
from classiq.interface.model.handle_binding import (
|
39
|
+
FieldHandleBinding,
|
40
|
+
HandleBinding,
|
41
|
+
NestedHandleBinding,
|
42
|
+
)
|
35
43
|
from classiq.interface.model.native_function_definition import NativeFunctionDefinition
|
36
44
|
from classiq.interface.model.quantum_function_declaration import (
|
37
45
|
NamedParamsQuantumFunctionDeclaration,
|
@@ -161,10 +169,7 @@ class Emitter(Generic[QuantumStatementT], ABC):
|
|
161
169
|
self._update_captured_classical_vars(statement)
|
162
170
|
if isinstance(statement, QuantumOperation):
|
163
171
|
self._update_captured_vars(statement)
|
164
|
-
|
165
|
-
self._interpreter._model.debug_info[statement.uuid] = (
|
166
|
-
new_function_debug_info_by_node(statement) # type:ignore[arg-type]
|
167
|
-
)
|
172
|
+
self._interpreter.add_to_debug_info(statement)
|
168
173
|
self._builder.emit_statement(statement)
|
169
174
|
|
170
175
|
def _update_captured_classical_vars(self, stmt: QuantumStatement) -> None:
|
@@ -224,9 +229,17 @@ class Emitter(Generic[QuantumStatementT], ABC):
|
|
224
229
|
handles = dict.fromkeys(
|
225
230
|
handle
|
226
231
|
for handle in vrc.var_handles
|
227
|
-
if
|
232
|
+
if handle.name
|
233
|
+
not in SUPPORTED_PYTHON_BUILTIN_FUNCTIONS
|
234
|
+
| SUPPORTED_CLASSIQ_BUILTIN_FUNCTIONS
|
235
|
+
and isinstance(self._current_scope[handle.name].value, QuantumSymbol)
|
228
236
|
)
|
229
|
-
return [
|
237
|
+
return [
|
238
|
+
self._interpreter.evaluate(handle).value
|
239
|
+
for handle in handles
|
240
|
+
if not isinstance(handle, FieldHandleBinding)
|
241
|
+
or handle.field not in CLASSICAL_ATTRIBUTES
|
242
|
+
]
|
230
243
|
|
231
244
|
def _get_classical_vars_in_expression(
|
232
245
|
self, expr: Expression
|
@@ -49,13 +49,14 @@ class QuantumFunctionCallEmitter(CallEmitter[QuantumFunctionCall]):
|
|
49
49
|
raise ClassiqInternalExpansionError(
|
50
50
|
f"Unexpected lambda list type {type(funcs).__name__!r}"
|
51
51
|
)
|
52
|
-
self.
|
52
|
+
for stmt in self._create_recursive_if(call, index, len(funcs)):
|
53
|
+
self._interpreter.emit(stmt)
|
53
54
|
return True
|
54
55
|
|
55
56
|
@staticmethod
|
56
57
|
def _create_recursive_if(
|
57
58
|
call: QuantumFunctionCall, index: ClassicalScalarProxy, num_funcs: int
|
58
|
-
) -> QuantumStatement:
|
59
|
+
) -> list[QuantumStatement]:
|
59
60
|
if TYPE_CHECKING:
|
60
61
|
assert isinstance(call.function, OperandIdentifier)
|
61
62
|
stmt: list[QuantumStatement] = []
|
@@ -74,7 +75,7 @@ class QuantumFunctionCallEmitter(CallEmitter[QuantumFunctionCall]):
|
|
74
75
|
else_=stmt,
|
75
76
|
)
|
76
77
|
]
|
77
|
-
return stmt
|
78
|
+
return stmt
|
78
79
|
|
79
80
|
|
80
81
|
class DeclarativeQuantumFunctionCallEmitter(
|