classiq 0.54.0__py3-none-any.whl → 0.55.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/interface/_version.py +1 -1
- classiq/interface/generator/functions/builtins/internal_operators.py +9 -1
- classiq/interface/generator/generated_circuit_data.py +0 -1
- classiq/interface/generator/model/preferences/preferences.py +4 -0
- classiq/interface/generator/types/compilation_metadata.py +5 -0
- classiq/interface/ide/visual_model.py +3 -1
- classiq/interface/model/control.py +22 -1
- classiq/interface/model/model.py +4 -0
- classiq/interface/model/native_function_definition.py +1 -1
- classiq/interface/model/quantum_expressions/arithmetic_operation.py +2 -26
- classiq/interface/model/quantum_statement.py +3 -0
- classiq/model_expansions/closure.py +74 -11
- classiq/model_expansions/function_builder.py +0 -6
- classiq/model_expansions/generative_functions.py +2 -2
- classiq/model_expansions/interpreter.py +22 -25
- classiq/model_expansions/quantum_operations/control.py +79 -20
- classiq/model_expansions/quantum_operations/emitter.py +24 -8
- classiq/model_expansions/quantum_operations/expression_operation.py +25 -1
- classiq/model_expansions/quantum_operations/quantum_assignment_operation.py +3 -26
- classiq/model_expansions/quantum_operations/quantum_function_call.py +2 -32
- classiq/model_expansions/quantum_operations/within_apply.py +0 -16
- classiq/model_expansions/scope_initialization.py +0 -1
- classiq/qmod/builtins/operations.py +113 -17
- classiq/qmod/create_model_function.py +10 -12
- classiq/qmod/model_state_container.py +5 -1
- classiq/qmod/native/pretty_printer.py +6 -1
- classiq/qmod/pretty_print/pretty_printer.py +25 -11
- classiq/qmod/quantum_function.py +25 -19
- classiq/qmod/synthesize_separately.py +1 -2
- {classiq-0.54.0.dist-info → classiq-0.55.0.dist-info}/METADATA +1 -1
- {classiq-0.54.0.dist-info → classiq-0.55.0.dist-info}/RECORD +32 -32
- classiq/model_expansions/call_to_model_converter.py +0 -190
- {classiq-0.54.0.dist-info → classiq-0.55.0.dist-info}/WHEEL +0 -0
@@ -70,7 +70,12 @@ class Emitter(Generic[QuantumStatementT]):
|
|
70
70
|
|
71
71
|
self._scope_guard = self._interpreter._scope_guard
|
72
72
|
self._machine_precision = self._interpreter._model.preferences.machine_precision
|
73
|
-
|
73
|
+
self._expanded_functions_compilation_metadata = (
|
74
|
+
self._interpreter._expanded_functions_compilation_metadata
|
75
|
+
)
|
76
|
+
self._functions_compilation_metadata = (
|
77
|
+
self._interpreter._model.functions_compilation_metadata
|
78
|
+
)
|
74
79
|
self._generative_contexts: dict[str, OperationContext] = {}
|
75
80
|
|
76
81
|
@abstractmethod
|
@@ -100,7 +105,7 @@ class Emitter(Generic[QuantumStatementT]):
|
|
100
105
|
return self._interpreter._current_scope
|
101
106
|
|
102
107
|
@property
|
103
|
-
def _expanded_functions(self) ->
|
108
|
+
def _expanded_functions(self) -> dict[str, NativeFunctionDefinition]:
|
104
109
|
return self._interpreter._expanded_functions
|
105
110
|
|
106
111
|
@property
|
@@ -137,23 +142,34 @@ class Emitter(Generic[QuantumStatementT]):
|
|
137
142
|
)
|
138
143
|
new_positional_arg_decls = new_declaration.positional_arg_declarations
|
139
144
|
is_atomic = function.is_atomic
|
140
|
-
|
145
|
+
new_function_name = function.name
|
146
|
+
if not is_atomic: # perform monomorphization per interpreted parameters set
|
141
147
|
self._add_params_to_scope(
|
142
148
|
new_positional_arg_decls, evaluated_args, function
|
143
149
|
)
|
144
150
|
context = self._expand_operation(
|
145
151
|
function.with_new_declaration(new_declaration)
|
146
152
|
)
|
147
|
-
|
148
|
-
|
153
|
+
function_context = cast(FunctionContext, context)
|
154
|
+
closure_id = function_context.closure.closure_id
|
155
|
+
function_def = self._expanded_functions.get(closure_id)
|
156
|
+
if function_def is None:
|
157
|
+
function_def = self._builder.create_definition(function_context)
|
158
|
+
self._expanded_functions[closure_id] = function_def
|
159
|
+
new_function_name = function_def.name
|
160
|
+
compilation_metadata = self._functions_compilation_metadata.get(
|
161
|
+
function.name
|
149
162
|
)
|
163
|
+
if compilation_metadata is not None:
|
164
|
+
self._expanded_functions_compilation_metadata[new_function_name] = (
|
165
|
+
compilation_metadata
|
166
|
+
)
|
167
|
+
|
150
168
|
new_positional_args = self._get_new_positional_args(
|
151
169
|
evaluated_args, is_atomic, new_positional_arg_decls
|
152
170
|
)
|
153
171
|
new_call = QuantumFunctionCall(
|
154
|
-
function=
|
155
|
-
new_declaration.name if is_atomic else self._expanded_functions[-1].name
|
156
|
-
),
|
172
|
+
function=new_function_name,
|
157
173
|
positional_args=new_positional_args,
|
158
174
|
)
|
159
175
|
is_allocate_or_free = (
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import ast
|
2
2
|
from abc import abstractmethod
|
3
3
|
from itertools import chain
|
4
|
-
from typing import TYPE_CHECKING, TypeVar
|
4
|
+
from typing import TYPE_CHECKING, TypeVar, Union
|
5
5
|
|
6
6
|
from classiq.interface.exceptions import (
|
7
7
|
ClassiqExpansionError,
|
@@ -13,6 +13,7 @@ from classiq.interface.generator.expressions.evaluated_expression import (
|
|
13
13
|
from classiq.interface.generator.expressions.expression import Expression
|
14
14
|
from classiq.interface.generator.functions.type_name import TypeName
|
15
15
|
from classiq.interface.model.bind_operation import BindOperation
|
16
|
+
from classiq.interface.model.control import Control
|
16
17
|
from classiq.interface.model.handle_binding import (
|
17
18
|
FieldHandleBinding,
|
18
19
|
HandleBinding,
|
@@ -20,6 +21,7 @@ from classiq.interface.model.handle_binding import (
|
|
20
21
|
SubscriptHandleBinding,
|
21
22
|
)
|
22
23
|
from classiq.interface.model.quantum_expressions.quantum_expression import (
|
24
|
+
QuantumAssignmentOperation,
|
23
25
|
QuantumExpressionOperation,
|
24
26
|
)
|
25
27
|
from classiq.interface.model.quantum_type import (
|
@@ -214,3 +216,25 @@ class ExpressionOperationEmitter(Emitter[ExpressionOperationT]):
|
|
214
216
|
self._machine_precision,
|
215
217
|
)
|
216
218
|
return op_with_evaluated_types
|
219
|
+
|
220
|
+
@staticmethod
|
221
|
+
def _all_vars_boolean(op: QuantumExpressionOperation) -> bool:
|
222
|
+
if not all(
|
223
|
+
var_type.has_size_in_bits and var_type.size_in_bits == 1
|
224
|
+
for var_type in op.var_types.values()
|
225
|
+
):
|
226
|
+
return False
|
227
|
+
return not any(
|
228
|
+
isinstance(var_type, QuantumNumeric)
|
229
|
+
and (var_type.sign_value or var_type.fraction_digits_value > 0)
|
230
|
+
for var_type in op.var_types.values()
|
231
|
+
)
|
232
|
+
|
233
|
+
@staticmethod
|
234
|
+
def _is_res_boolean(op: Union[QuantumAssignmentOperation, Control]) -> bool:
|
235
|
+
if not (op.result_type.has_size_in_bits and op.result_type.size_in_bits == 1):
|
236
|
+
return False
|
237
|
+
return not (
|
238
|
+
isinstance(op.result_type, QuantumNumeric)
|
239
|
+
and (op.result_type.sign_value or op.result_type.fraction_digits_value > 0)
|
240
|
+
)
|
@@ -101,7 +101,7 @@ class QuantumAssignmentOperationEmitter(
|
|
101
101
|
if (
|
102
102
|
op.operation_kind != ArithmeticOperationKind.InplaceXor
|
103
103
|
or op.result_type.size_in_bits > 1
|
104
|
-
or not _is_res_boolean(op)
|
104
|
+
or not self._is_res_boolean(op)
|
105
105
|
or target.quantum_type.size_in_bits > 1
|
106
106
|
or _is_constant(expression.expr)
|
107
107
|
):
|
@@ -141,7 +141,8 @@ class QuantumAssignmentOperationEmitter(
|
|
141
141
|
ArithmeticOperationKind.Assignment,
|
142
142
|
ArithmeticOperationKind.InplaceXor,
|
143
143
|
)
|
144
|
-
or not _all_vars_boolean(op)
|
144
|
+
or not self._all_vars_boolean(op)
|
145
|
+
or not self._is_res_boolean(op)
|
145
146
|
):
|
146
147
|
return op, expression, False
|
147
148
|
optimizer = BooleanExpressionOptimizer()
|
@@ -250,27 +251,3 @@ def _validate_naive_inplace_handles(qe: ArithmeticOperation) -> None:
|
|
250
251
|
raise ClassiqExpansionError(
|
251
252
|
HANDLE_ERROR_MESSAGE.format(handle_str=str(qe.result_var))
|
252
253
|
)
|
253
|
-
|
254
|
-
|
255
|
-
def _all_vars_boolean(op: ArithmeticOperation) -> bool:
|
256
|
-
if not all(
|
257
|
-
var_type.has_size_in_bits and var_type.size_in_bits == 1
|
258
|
-
for var_type in op.var_types.values()
|
259
|
-
):
|
260
|
-
return False
|
261
|
-
if any(
|
262
|
-
isinstance(var_type, QuantumNumeric)
|
263
|
-
and (var_type.sign_value or var_type.fraction_digits_value > 0)
|
264
|
-
for var_type in op.var_types.values()
|
265
|
-
):
|
266
|
-
return False
|
267
|
-
return _is_res_boolean(op)
|
268
|
-
|
269
|
-
|
270
|
-
def _is_res_boolean(op: ArithmeticOperation) -> bool:
|
271
|
-
if not (op.result_type.has_size_in_bits and op.result_type.size_in_bits == 1):
|
272
|
-
return False
|
273
|
-
return not (
|
274
|
-
isinstance(op.result_type, QuantumNumeric)
|
275
|
-
and (op.result_type.sign_value or op.result_type.fraction_digits_value > 0)
|
276
|
-
)
|
@@ -2,10 +2,6 @@ from typing import TYPE_CHECKING
|
|
2
2
|
|
3
3
|
from classiq.interface.model.quantum_function_call import QuantumFunctionCall
|
4
4
|
|
5
|
-
from classiq.model_expansions.call_to_model_converter import (
|
6
|
-
BlockFunctionInfo,
|
7
|
-
CallToModelConverter,
|
8
|
-
)
|
9
5
|
from classiq.model_expansions.closure import FunctionClosure
|
10
6
|
from classiq.model_expansions.quantum_operations.emitter import Emitter
|
11
7
|
from classiq.qmod.semantics.error_manager import ErrorManager
|
@@ -18,37 +14,11 @@ class QuantumFunctionCallEmitter(Emitter[QuantumFunctionCall]):
|
|
18
14
|
def __init__(self, interpreter: "Interpreter") -> None:
|
19
15
|
super().__init__(interpreter)
|
20
16
|
self._model = self._interpreter._model
|
21
|
-
self._synthesized_separately_blocks = (
|
22
|
-
self._interpreter._synthesized_separately_blocks
|
23
|
-
)
|
24
17
|
|
25
18
|
def emit(self, call: QuantumFunctionCall, /) -> None:
|
26
|
-
function
|
27
|
-
FunctionClosure
|
28
|
-
)
|
19
|
+
function = self._interpreter.evaluate(call.function).as_type(FunctionClosure)
|
29
20
|
args = call.positional_args
|
30
21
|
with ErrorManager().call(
|
31
22
|
function.name
|
32
23
|
), function.scope.freeze(), self._propagated_var_stack.capture_variables(call):
|
33
|
-
|
34
|
-
if function.synthesis_data.should_synthesize_separately:
|
35
|
-
interpreted_call_converter = CallToModelConverter(
|
36
|
-
call,
|
37
|
-
function.positional_arg_declarations,
|
38
|
-
function.scope.data,
|
39
|
-
self._model,
|
40
|
-
)
|
41
|
-
self._update_synthesized_separately_models(
|
42
|
-
interpreted_call_converter, new_call.func_name
|
43
|
-
)
|
44
|
-
|
45
|
-
def _update_synthesized_separately_models(
|
46
|
-
self, call_converter: CallToModelConverter, call_name: str
|
47
|
-
) -> None:
|
48
|
-
synthesized_separately_blocks = self._synthesized_separately_blocks
|
49
|
-
block_id = call_converter.block_id
|
50
|
-
block_function = synthesized_separately_blocks.get(block_id)
|
51
|
-
if block_function is None:
|
52
|
-
block_function = BlockFunctionInfo.from_call_converter(call_converter)
|
53
|
-
synthesized_separately_blocks[block_id] = block_function
|
54
|
-
block_function.calls.add(call_name)
|
24
|
+
self._emit_quantum_function_call(function, args)
|
@@ -1,5 +1,4 @@
|
|
1
1
|
from classiq.interface.generator.functions.builtins.internal_operators import (
|
2
|
-
COMPUTE_OPERATOR_NAME,
|
3
2
|
WITHIN_APPLY_NAME,
|
4
3
|
)
|
5
4
|
from classiq.interface.model.within_apply_operation import WithinApply
|
@@ -26,10 +25,6 @@ class WithinApplyEmitter(Emitter[WithinApply]):
|
|
26
25
|
}
|
27
26
|
)
|
28
27
|
|
29
|
-
if self._should_wrap(within_apply.compute):
|
30
|
-
self._emit_wrapped(within_apply)
|
31
|
-
return
|
32
|
-
|
33
28
|
self._emit_as_operation(within_apply)
|
34
29
|
|
35
30
|
def _emit_as_operation(self, within_apply: WithinApply) -> None:
|
@@ -46,14 +41,3 @@ class WithinApplyEmitter(Emitter[WithinApply]):
|
|
46
41
|
source_ref=within_apply.source_ref,
|
47
42
|
)
|
48
43
|
)
|
49
|
-
|
50
|
-
def _emit_wrapped(self, within_apply: WithinApply) -> None:
|
51
|
-
wrapped_compute = self._create_expanded_wrapping_function(
|
52
|
-
COMPUTE_OPERATOR_NAME, within_apply.compute
|
53
|
-
)
|
54
|
-
wrapped_within_apply = WithinApply(
|
55
|
-
compute=[wrapped_compute],
|
56
|
-
action=within_apply.action,
|
57
|
-
source_ref=within_apply.source_ref,
|
58
|
-
)
|
59
|
-
self._builder.emit_statement(wrapped_within_apply)
|
@@ -1,12 +1,16 @@
|
|
1
1
|
import inspect
|
2
2
|
import sys
|
3
|
+
import warnings
|
3
4
|
from collections.abc import Mapping
|
4
5
|
from types import FrameType
|
5
6
|
from typing import (
|
7
|
+
TYPE_CHECKING,
|
6
8
|
Any,
|
7
9
|
Callable,
|
8
10
|
Final,
|
11
|
+
Optional,
|
9
12
|
Union,
|
13
|
+
overload,
|
10
14
|
)
|
11
15
|
|
12
16
|
from classiq.interface.exceptions import ClassiqValueError
|
@@ -21,13 +25,16 @@ from classiq.interface.model.classical_parameter_declaration import (
|
|
21
25
|
ClassicalParameterDeclaration,
|
22
26
|
)
|
23
27
|
from classiq.interface.model.control import Control
|
24
|
-
from classiq.interface.model.inplace_binary_operation import (
|
25
|
-
BinaryOperation,
|
26
|
-
InplaceBinaryOperation,
|
27
|
-
)
|
28
28
|
from classiq.interface.model.invert import Invert
|
29
29
|
from classiq.interface.model.phase_operation import PhaseOperation
|
30
30
|
from classiq.interface.model.power import Power
|
31
|
+
from classiq.interface.model.quantum_expressions.amplitude_loading_operation import (
|
32
|
+
AmplitudeLoadingOperation,
|
33
|
+
)
|
34
|
+
from classiq.interface.model.quantum_expressions.arithmetic_operation import (
|
35
|
+
ArithmeticOperation,
|
36
|
+
ArithmeticOperationKind,
|
37
|
+
)
|
31
38
|
from classiq.interface.model.quantum_function_call import QuantumFunctionCall
|
32
39
|
from classiq.interface.model.quantum_function_declaration import (
|
33
40
|
QuantumOperandDeclaration,
|
@@ -37,7 +44,7 @@ from classiq.interface.model.repeat import Repeat
|
|
37
44
|
from classiq.interface.model.statement_block import StatementBlock
|
38
45
|
from classiq.interface.model.within_apply_operation import WithinApply
|
39
46
|
|
40
|
-
from classiq.qmod.qmod_variable import Input, Output, QArray, QBit, QNum, QVar
|
47
|
+
from classiq.qmod.qmod_variable import Input, Output, QArray, QBit, QNum, QScalar, QVar
|
41
48
|
from classiq.qmod.quantum_callable import QCallable
|
42
49
|
from classiq.qmod.quantum_expandable import prepare_arg
|
43
50
|
from classiq.qmod.symbolic_expr import SymbolicExpr
|
@@ -91,6 +98,7 @@ def if_(
|
|
91
98
|
def control(
|
92
99
|
ctrl: Union[SymbolicExpr, QBit, QArray[QBit]],
|
93
100
|
stmt_block: Union[QCallable, Callable[[], None]],
|
101
|
+
else_block: Union[QCallable, Callable[[], None], None] = None,
|
94
102
|
) -> None:
|
95
103
|
_validate_operand(stmt_block)
|
96
104
|
assert QCallable.CURRENT_EXPANDABLE is not None
|
@@ -98,39 +106,125 @@ def control(
|
|
98
106
|
control_stmt = Control(
|
99
107
|
expression=Expression(expr=str(ctrl)),
|
100
108
|
body=_operand_to_body(stmt_block, "stmt_block"),
|
109
|
+
else_block=_operand_to_body(else_block, "else_block") if else_block else None,
|
101
110
|
source_ref=source_ref,
|
102
111
|
)
|
103
112
|
control_stmt.set_generative_block("body", stmt_block)
|
113
|
+
if else_block is not None:
|
114
|
+
control_stmt.set_generative_block("else_block", else_block)
|
104
115
|
QCallable.CURRENT_EXPANDABLE.append_statement_to_body(control_stmt)
|
105
116
|
|
106
117
|
|
118
|
+
def assign(expression: SymbolicExpr, target_var: QScalar) -> None:
|
119
|
+
assert QCallable.CURRENT_EXPANDABLE is not None
|
120
|
+
source_ref = get_source_ref(sys._getframe(1))
|
121
|
+
QCallable.CURRENT_EXPANDABLE.append_statement_to_body(
|
122
|
+
ArithmeticOperation(
|
123
|
+
expression=Expression(expr=str(expression)),
|
124
|
+
result_var=target_var.get_handle_binding(),
|
125
|
+
operation_kind=ArithmeticOperationKind.Assignment,
|
126
|
+
source_ref=source_ref,
|
127
|
+
)
|
128
|
+
)
|
129
|
+
|
130
|
+
|
131
|
+
def assign_amplitude(expression: SymbolicExpr, target_var: QScalar) -> None:
|
132
|
+
assert QCallable.CURRENT_EXPANDABLE is not None
|
133
|
+
source_ref = get_source_ref(sys._getframe(1))
|
134
|
+
QCallable.CURRENT_EXPANDABLE.append_statement_to_body(
|
135
|
+
AmplitudeLoadingOperation(
|
136
|
+
expression=Expression(expr=str(expression)),
|
137
|
+
result_var=target_var.get_handle_binding(),
|
138
|
+
source_ref=source_ref,
|
139
|
+
)
|
140
|
+
)
|
141
|
+
|
142
|
+
|
143
|
+
@overload
|
144
|
+
def inplace_add(expression: SymbolicExpr, target_var: QScalar) -> None:
|
145
|
+
pass
|
146
|
+
|
147
|
+
|
148
|
+
@overload
|
149
|
+
def inplace_add(*, value: QNum, target: QNum) -> None:
|
150
|
+
pass
|
151
|
+
|
152
|
+
|
107
153
|
def inplace_add(
|
108
|
-
|
109
|
-
|
154
|
+
expression: Optional[SymbolicExpr] = None,
|
155
|
+
target_var: Optional[QScalar] = None,
|
156
|
+
value: Optional[QNum] = None,
|
157
|
+
target: Optional[QNum] = None,
|
110
158
|
) -> None:
|
111
159
|
assert QCallable.CURRENT_EXPANDABLE is not None
|
160
|
+
if value is not None or target is not None:
|
161
|
+
warnings.warn(
|
162
|
+
"Parameters 'value' and 'target of function 'inplace_add' have "
|
163
|
+
"been renamed to 'expression' and 'target_var' respectively. Parameters "
|
164
|
+
"'value' and 'target' will no longer be supported starting on 02/12/24 at "
|
165
|
+
"the earliest.\nHint: Change `inplace_add(value=..., target=...)` to "
|
166
|
+
"`inplace_add(expression=..., target_var=...)` or `inplace_add(..., ...)`.",
|
167
|
+
category=DeprecationWarning,
|
168
|
+
stacklevel=2,
|
169
|
+
)
|
170
|
+
if value is not None:
|
171
|
+
expression = value
|
172
|
+
if target is not None:
|
173
|
+
target_var = target
|
174
|
+
if TYPE_CHECKING:
|
175
|
+
assert expression is not None
|
176
|
+
assert target_var is not None
|
112
177
|
source_ref = get_source_ref(sys._getframe(1))
|
113
178
|
QCallable.CURRENT_EXPANDABLE.append_statement_to_body(
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
179
|
+
ArithmeticOperation(
|
180
|
+
expression=Expression(expr=str(expression)),
|
181
|
+
result_var=target_var.get_handle_binding(),
|
182
|
+
operation_kind=ArithmeticOperationKind.InplaceAdd,
|
118
183
|
source_ref=source_ref,
|
119
184
|
)
|
120
185
|
)
|
121
186
|
|
122
187
|
|
188
|
+
@overload
|
189
|
+
def inplace_xor(expression: SymbolicExpr, target_var: QScalar) -> None:
|
190
|
+
pass
|
191
|
+
|
192
|
+
|
193
|
+
@overload
|
194
|
+
def inplace_xor(*, value: QNum, target: QNum) -> None:
|
195
|
+
pass
|
196
|
+
|
197
|
+
|
123
198
|
def inplace_xor(
|
124
|
-
|
125
|
-
|
199
|
+
expression: Optional[SymbolicExpr] = None,
|
200
|
+
target_var: Optional[QScalar] = None,
|
201
|
+
value: Optional[QNum] = None,
|
202
|
+
target: Optional[QNum] = None,
|
126
203
|
) -> None:
|
127
204
|
assert QCallable.CURRENT_EXPANDABLE is not None
|
205
|
+
if value is not None or target is not None:
|
206
|
+
warnings.warn(
|
207
|
+
"Parameters 'value' and 'target of function 'inplace_xor' have "
|
208
|
+
"been renamed to 'expression' and 'target_var' respectively. Parameters "
|
209
|
+
"'value' and 'target' will no longer be supported starting on 02/12/24 at "
|
210
|
+
"the earliest.\nHint: Change `inplace_xor(value=..., target=...)` to "
|
211
|
+
"`inplace_xor(expression=..., target_var=...)` or `inplace_xor(..., ...)`.",
|
212
|
+
category=DeprecationWarning,
|
213
|
+
stacklevel=2,
|
214
|
+
)
|
215
|
+
if value is not None:
|
216
|
+
expression = value
|
217
|
+
if target is not None:
|
218
|
+
target_var = target
|
219
|
+
if TYPE_CHECKING:
|
220
|
+
assert expression is not None
|
221
|
+
assert target_var is not None
|
128
222
|
source_ref = get_source_ref(sys._getframe(1))
|
129
223
|
QCallable.CURRENT_EXPANDABLE.append_statement_to_body(
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
224
|
+
ArithmeticOperation(
|
225
|
+
expression=Expression(expr=str(expression)),
|
226
|
+
result_var=target_var.get_handle_binding(),
|
227
|
+
operation_kind=ArithmeticOperationKind.InplaceXor,
|
134
228
|
source_ref=source_ref,
|
135
229
|
)
|
136
230
|
)
|
@@ -297,6 +391,8 @@ def _operand_to_body(
|
|
297
391
|
|
298
392
|
|
299
393
|
__all__ = [
|
394
|
+
"assign",
|
395
|
+
"assign_amplitude",
|
300
396
|
"bind",
|
301
397
|
"control",
|
302
398
|
"invert",
|
@@ -60,6 +60,7 @@ def create_model(
|
|
60
60
|
user_gen_functions = {
|
61
61
|
gen_func._py_callable.__name__ for gen_func in GEN_QFUNCS
|
62
62
|
} - set(BUILTIN_FUNCTION_DECLARATIONS.keys())
|
63
|
+
|
63
64
|
if len(user_gen_functions) > 0 and is_generative_expansion_enabled():
|
64
65
|
model = _expand_generative_model(
|
65
66
|
(
|
@@ -100,11 +101,17 @@ def _expand_generative_model(
|
|
100
101
|
def _dummy() -> None:
|
101
102
|
pass
|
102
103
|
|
104
|
+
functions_compilation_metadata = {
|
105
|
+
dec_func._py_callable.__name__: dec_func.compilation_metadata
|
106
|
+
for dec_func in DEC_QFUNCS
|
107
|
+
if dec_func.compilation_metadata is not None
|
108
|
+
}
|
103
109
|
model = _dummy.create_model(
|
104
110
|
constraints,
|
105
111
|
execution_preferences,
|
106
112
|
preferences,
|
107
113
|
classical_execution_function,
|
114
|
+
functions_compilation_metadata,
|
108
115
|
)
|
109
116
|
generative_functions = _get_generative_functions(gen_main, preferences)
|
110
117
|
model.functions = generative_functions
|
@@ -157,7 +164,8 @@ def _get_wrapper_main(gen_main: QFunc, preferences: Optional[Preferences]) -> Mo
|
|
157
164
|
def _get_all_model_functions_as_generative_functions() -> list[GenerativeQFunc]:
|
158
165
|
|
159
166
|
gen_functions = list(GEN_QFUNCS) + [
|
160
|
-
|
167
|
+
GenerativeQFunc(dec_func._py_callable, dec_func.func_decl)
|
168
|
+
for dec_func in DEC_QFUNCS
|
161
169
|
]
|
162
170
|
return [
|
163
171
|
(
|
@@ -173,16 +181,6 @@ def _get_all_model_functions_as_generative_functions() -> list[GenerativeQFunc]:
|
|
173
181
|
]
|
174
182
|
|
175
183
|
|
176
|
-
def _get_gen_from_dec(dec_func: QFunc) -> GenerativeQFunc:
|
177
|
-
synthesis_data = dec_func.synthesis_data
|
178
|
-
if synthesis_data is not None and synthesis_data.should_synthesize_separately:
|
179
|
-
raise ClassiqError(
|
180
|
-
"""The model contains generative functions,
|
181
|
-
which cannot coexist with functions marked for separate synthesis"""
|
182
|
-
)
|
183
|
-
return GenerativeQFunc(dec_func._py_callable, dec_func.func_decl)
|
184
|
-
|
185
|
-
|
186
184
|
def _interpret_generative_model(
|
187
185
|
gen_model: Model, gen_functions: list[GenerativeQFunc]
|
188
186
|
) -> dict[str, NativeFunctionDefinition]:
|
@@ -192,7 +190,7 @@ def _interpret_generative_model(
|
|
192
190
|
)
|
193
191
|
interpreter = Interpreter(gen_model, gen_functions, is_frontend=True)
|
194
192
|
set_frontend_interpreter(interpreter)
|
195
|
-
expand_model
|
193
|
+
expand_model = interpreter.expand()
|
196
194
|
functions_dict = nameables_to_dict(expand_model.functions)
|
197
195
|
|
198
196
|
# Inline _gen_main call in main
|
@@ -1,8 +1,11 @@
|
|
1
1
|
from classiq.interface.generator.constant import Constant
|
2
|
+
from classiq.interface.generator.types.compilation_metadata import CompilationMetadata
|
2
3
|
from classiq.interface.generator.types.enum_declaration import EnumDeclaration
|
3
4
|
from classiq.interface.generator.types.qstruct_declaration import QStructDeclaration
|
4
5
|
from classiq.interface.generator.types.struct_declaration import StructDeclaration
|
5
|
-
from classiq.interface.model.native_function_definition import
|
6
|
+
from classiq.interface.model.native_function_definition import (
|
7
|
+
NativeFunctionDefinition,
|
8
|
+
)
|
6
9
|
|
7
10
|
|
8
11
|
class ModelStateContainer:
|
@@ -11,6 +14,7 @@ class ModelStateContainer:
|
|
11
14
|
qstruct_decls: dict[str, QStructDeclaration]
|
12
15
|
native_defs: dict[str, NativeFunctionDefinition]
|
13
16
|
constants: dict[str, Constant]
|
17
|
+
functions_compilation_metadata: dict[str, CompilationMetadata]
|
14
18
|
|
15
19
|
|
16
20
|
QMODULE = ModelStateContainer()
|
@@ -253,7 +253,12 @@ class DSLPrettyPrinter(Visitor):
|
|
253
253
|
def visit_Control(self, op: Control) -> str:
|
254
254
|
control = f"{self._indent}control ({self.visit(op.expression)}) {{\n"
|
255
255
|
control += self._visit_body(op.body)
|
256
|
-
control += f"{self._indent}}}
|
256
|
+
control += f"{self._indent}}}"
|
257
|
+
if op.else_block is not None:
|
258
|
+
control += " else {\n"
|
259
|
+
control += self._visit_body(op.else_block)
|
260
|
+
control += f"{self._indent}}}"
|
261
|
+
control += "\n"
|
257
262
|
return control
|
258
263
|
|
259
264
|
def visit_PhaseOperation(self, op: PhaseOperation) -> str:
|
@@ -50,9 +50,6 @@ from classiq.interface.model.quantum_expressions.arithmetic_operation import (
|
|
50
50
|
ArithmeticOperation,
|
51
51
|
ArithmeticOperationKind,
|
52
52
|
)
|
53
|
-
from classiq.interface.model.quantum_expressions.quantum_expression import (
|
54
|
-
QuantumAssignmentOperation,
|
55
|
-
)
|
56
53
|
from classiq.interface.model.quantum_function_call import (
|
57
54
|
OperandIdentifier,
|
58
55
|
QuantumFunctionCall,
|
@@ -391,7 +388,10 @@ class PythonPrettyPrinter(Visitor):
|
|
391
388
|
|
392
389
|
def visit_Control(self, op: Control) -> str:
|
393
390
|
self._imports["control"] = 1
|
394
|
-
|
391
|
+
control_else = (
|
392
|
+
f", {self._visit_body(op.else_block)}" if op.else_block is not None else ""
|
393
|
+
)
|
394
|
+
return f"{self._indent}control({self.visit(op.expression)}, {self._visit_body(op.body)}{control_else})\n"
|
395
395
|
|
396
396
|
def visit_PhaseOperation(self, op: PhaseOperation) -> str:
|
397
397
|
self._imports["phase"] = 1
|
@@ -429,13 +429,16 @@ class PythonPrettyPrinter(Visitor):
|
|
429
429
|
code = f"lambda{argument_string}: {'[' if len(body) > 1 else ''}\n"
|
430
430
|
self._level += 1
|
431
431
|
for i, statement in enumerate(body):
|
432
|
-
if isinstance(
|
433
|
-
statement, (QuantumAssignmentOperation, VariableDeclarationStatement)
|
434
|
-
):
|
432
|
+
if isinstance(statement, VariableDeclarationStatement):
|
435
433
|
raise AssertionError(
|
436
|
-
"pretty printing
|
434
|
+
"pretty printing variable declaration statements in quantum lambda function is unsupported."
|
437
435
|
)
|
438
|
-
|
436
|
+
if isinstance(statement, AmplitudeLoadingOperation):
|
437
|
+
code += self.visit_AmplitudeLoadingOperation(statement, in_lambda=True)
|
438
|
+
elif isinstance(statement, ArithmeticOperation):
|
439
|
+
code += self.visit_ArithmeticOperation(statement, in_lambda=True)
|
440
|
+
else:
|
441
|
+
code += self.visit(statement)
|
439
442
|
if i < len(body) - 1:
|
440
443
|
code += ","
|
441
444
|
self._level -= 1
|
@@ -469,18 +472,29 @@ class PythonPrettyPrinter(Visitor):
|
|
469
472
|
def visit_FieldHandleBinding(self, var_ref: FieldHandleBinding) -> str:
|
470
473
|
return f"{self.visit(var_ref.base_handle)}.{self.visit(var_ref.field)}"
|
471
474
|
|
472
|
-
def visit_ArithmeticOperation(
|
475
|
+
def visit_ArithmeticOperation(
|
476
|
+
self, arith_op: ArithmeticOperation, in_lambda: bool = False
|
477
|
+
) -> str:
|
473
478
|
if arith_op.operation_kind == ArithmeticOperationKind.Assignment:
|
474
479
|
op = "|="
|
480
|
+
func = "assign"
|
475
481
|
elif arith_op.operation_kind == ArithmeticOperationKind.InplaceXor:
|
476
482
|
op = "^="
|
483
|
+
func = "inplace_xor"
|
477
484
|
else:
|
478
485
|
op = "+="
|
486
|
+
func = "inplace_add"
|
487
|
+
if in_lambda:
|
488
|
+
self._imports[func] = 1
|
489
|
+
return f"{func}({self.visit(arith_op.expression)}, {self._indent}{self.visit(arith_op.result_var)})\n"
|
479
490
|
return f"{self._indent}{self.visit(arith_op.result_var)} {op} {self.visit(arith_op.expression)}\n"
|
480
491
|
|
481
492
|
def visit_AmplitudeLoadingOperation(
|
482
|
-
self, amplitude_loading_op: AmplitudeLoadingOperation
|
493
|
+
self, amplitude_loading_op: AmplitudeLoadingOperation, in_lambda: bool = False
|
483
494
|
) -> str:
|
495
|
+
if in_lambda:
|
496
|
+
self._imports["assign_amplitude"] = 1
|
497
|
+
return f"assign_amplitude({self.visit(amplitude_loading_op.expression)}, {self._indent}{self.visit(amplitude_loading_op.result_var)})\n"
|
484
498
|
return f"{self._indent}{self.visit(amplitude_loading_op.result_var)} *= {self.visit(amplitude_loading_op.expression)}\n"
|
485
499
|
|
486
500
|
def _print_bind_handles(self, handles: list[HandleBinding]) -> str:
|