classiq 0.75.0__py3-none-any.whl → 0.77.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- classiq/_internals/api_wrapper.py +36 -0
- classiq/analyzer/show_interactive_hack.py +58 -2
- classiq/applications/chemistry/chemistry_model_constructor.py +15 -7
- classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +11 -1
- classiq/applications/combinatorial_optimization/combinatorial_problem.py +8 -7
- classiq/applications/qnn/gradients/quantum_gradient.py +3 -5
- classiq/applications/qnn/gradients/simple_quantum_gradient.py +2 -2
- classiq/applications/qnn/qlayer.py +14 -19
- classiq/applications/qnn/types.py +1 -4
- classiq/execution/__init__.py +3 -0
- classiq/execution/execution_session.py +3 -16
- classiq/execution/qnn.py +2 -2
- classiq/execution/user_budgets.py +38 -0
- classiq/executor.py +7 -19
- classiq/interface/_version.py +1 -1
- classiq/interface/debug_info/debug_info.py +16 -2
- classiq/interface/executor/user_budget.py +56 -0
- classiq/interface/generator/application_apis/finance_declarations.py +3 -0
- classiq/interface/generator/expressions/atomic_expression_functions.py +3 -0
- classiq/interface/generator/expressions/proxies/classical/any_classical_value.py +30 -124
- classiq/interface/generator/expressions/proxies/classical/classical_array_proxy.py +45 -21
- classiq/interface/generator/expressions/proxies/classical/qmod_struct_instance.py +7 -0
- classiq/interface/generator/expressions/proxies/classical/utils.py +12 -11
- classiq/interface/generator/expressions/proxies/quantum/qmod_qarray_proxy.py +6 -15
- classiq/interface/generator/expressions/proxies/quantum/qmod_qscalar_proxy.py +22 -6
- classiq/interface/generator/expressions/proxies/quantum/qmod_sized_proxy.py +9 -4
- classiq/interface/generator/expressions/sympy_supported_expressions.py +1 -0
- classiq/interface/generator/functions/classical_type.py +6 -1
- classiq/interface/generator/functions/type_name.py +7 -2
- classiq/interface/generator/functions/type_qualifier.py +15 -0
- classiq/interface/generator/model/preferences/preferences.py +7 -0
- classiq/interface/generator/quantum_program.py +5 -19
- classiq/interface/helpers/backward_compatibility.py +9 -0
- classiq/interface/helpers/datastructures.py +6 -0
- classiq/interface/model/handle_binding.py +8 -0
- classiq/interface/model/model.py +3 -6
- classiq/interface/model/port_declaration.py +1 -2
- classiq/interface/model/quantum_function_call.py +31 -1
- classiq/interface/model/quantum_lambda_function.py +2 -1
- classiq/interface/model/quantum_statement.py +14 -1
- classiq/interface/server/routes.py +6 -0
- classiq/interface/source_reference.py +7 -2
- classiq/model_expansions/atomic_expression_functions_defs.py +62 -19
- classiq/model_expansions/capturing/captured_vars.py +18 -6
- classiq/model_expansions/closure.py +5 -0
- classiq/model_expansions/evaluators/arg_type_match.py +2 -2
- classiq/model_expansions/evaluators/argument_types.py +3 -3
- classiq/model_expansions/evaluators/classical_expression.py +9 -9
- classiq/model_expansions/evaluators/classical_type_inference.py +17 -6
- classiq/model_expansions/evaluators/parameter_types.py +45 -24
- classiq/model_expansions/expression_evaluator.py +21 -12
- classiq/model_expansions/function_builder.py +45 -0
- classiq/model_expansions/generative_functions.py +62 -35
- classiq/model_expansions/interpreters/base_interpreter.py +32 -7
- classiq/model_expansions/interpreters/frontend_generative_interpreter.py +9 -3
- classiq/model_expansions/interpreters/generative_interpreter.py +17 -5
- classiq/model_expansions/quantum_operations/allocate.py +8 -3
- classiq/model_expansions/quantum_operations/assignment_result_processor.py +221 -20
- classiq/model_expansions/quantum_operations/bind.py +54 -30
- classiq/model_expansions/quantum_operations/block_evaluator.py +42 -0
- classiq/model_expansions/quantum_operations/call_emitter.py +35 -18
- classiq/model_expansions/quantum_operations/composite_emitter.py +1 -1
- classiq/model_expansions/quantum_operations/declarative_call_emitter.py +23 -9
- classiq/model_expansions/quantum_operations/emitter.py +21 -9
- classiq/model_expansions/quantum_operations/quantum_function_call.py +4 -3
- classiq/model_expansions/scope.py +63 -10
- classiq/model_expansions/sympy_conversion/arithmetics.py +18 -0
- classiq/model_expansions/sympy_conversion/expression_to_sympy.py +2 -0
- classiq/model_expansions/sympy_conversion/sympy_to_python.py +10 -1
- classiq/model_expansions/transformers/model_renamer.py +45 -7
- classiq/model_expansions/utils/handles_collector.py +1 -1
- classiq/model_expansions/visitors/symbolic_param_inference.py +3 -3
- classiq/model_expansions/visitors/variable_references.py +45 -9
- classiq/open_library/functions/lookup_table.py +1 -1
- classiq/open_library/functions/state_preparation.py +1 -1
- classiq/qmod/builtins/functions/allocation.py +2 -2
- classiq/qmod/builtins/functions/arithmetic.py +14 -12
- classiq/qmod/builtins/functions/standard_gates.py +23 -23
- classiq/qmod/create_model_function.py +21 -3
- classiq/qmod/declaration_inferrer.py +19 -7
- classiq/qmod/generative.py +9 -1
- classiq/qmod/global_declarative_switch.py +19 -0
- classiq/qmod/native/expression_to_qmod.py +4 -0
- classiq/qmod/native/pretty_printer.py +12 -3
- classiq/qmod/pretty_print/pretty_printer.py +5 -1
- classiq/qmod/python_classical_type.py +4 -5
- classiq/qmod/qfunc.py +31 -23
- classiq/qmod/qmod_constant.py +15 -7
- classiq/qmod/qmod_variable.py +7 -1
- classiq/qmod/quantum_expandable.py +29 -1
- classiq/qmod/quantum_function.py +45 -25
- classiq/qmod/semantics/lambdas.py +6 -2
- classiq/qmod/semantics/validation/main_validation.py +17 -4
- classiq/qmod/symbolic.py +8 -19
- classiq/qmod/symbolic_expr.py +26 -0
- classiq/qmod/write_qmod.py +36 -10
- classiq/synthesis.py +24 -37
- classiq/visualization.py +35 -0
- {classiq-0.75.0.dist-info → classiq-0.77.0.dist-info}/METADATA +1 -1
- {classiq-0.75.0.dist-info → classiq-0.77.0.dist-info}/RECORD +101 -96
- {classiq-0.75.0.dist-info → classiq-0.77.0.dist-info}/WHEEL +0 -0
@@ -1,5 +1,5 @@
|
|
1
1
|
from functools import singledispatchmethod
|
2
|
-
from typing import Any
|
2
|
+
from typing import Any, Optional
|
3
3
|
|
4
4
|
import numpy as np
|
5
5
|
from numpy.random import permutation
|
@@ -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
|
)
|
@@ -100,9 +104,9 @@ class GenerativeInterpreter(BaseInterpreter):
|
|
100
104
|
def infer_symbolic_parameters(
|
101
105
|
self,
|
102
106
|
functions: list[NativeFunctionDefinition],
|
103
|
-
additional_signatures:
|
104
|
-
list[NamedParamsQuantumFunctionDeclaration]
|
105
|
-
|
107
|
+
additional_signatures: Optional[
|
108
|
+
list[NamedParamsQuantumFunctionDeclaration]
|
109
|
+
] = None,
|
106
110
|
) -> None:
|
107
111
|
pass
|
108
112
|
|
@@ -152,10 +156,16 @@ class GenerativeInterpreter(BaseInterpreter):
|
|
152
156
|
QuantumFunctionCallEmitter(self).emit(call)
|
153
157
|
|
154
158
|
@emit.register
|
159
|
+
def _emit_allocate(self, allocate: Allocate) -> None:
|
160
|
+
return self.emit_allocate(allocate)
|
161
|
+
|
155
162
|
def emit_allocate(self, allocate: Allocate) -> None:
|
156
163
|
AllocateEmitter(self).emit(allocate)
|
157
164
|
|
158
165
|
@emit.register
|
166
|
+
def _emit_bind(self, bind: BindOperation) -> None:
|
167
|
+
self.emit_bind(bind)
|
168
|
+
|
159
169
|
def emit_bind(self, bind: BindOperation) -> None:
|
160
170
|
BindEmitter(self).emit(bind)
|
161
171
|
|
@@ -210,6 +220,7 @@ class GenerativeInterpreter(BaseInterpreter):
|
|
210
220
|
self,
|
211
221
|
[
|
212
222
|
ExpressionEvaluator(self, "condition"),
|
223
|
+
IfElimination(self),
|
213
224
|
BlockEvaluator(
|
214
225
|
self,
|
215
226
|
CLASSICAL_IF_OPERATOR_NAME,
|
@@ -243,6 +254,7 @@ class GenerativeInterpreter(BaseInterpreter):
|
|
243
254
|
self,
|
244
255
|
[
|
245
256
|
ExpressionEvaluator(self, "count"),
|
257
|
+
RepeatElimination(self),
|
246
258
|
RepeatBlockEvaluator(self, REPEAT_OPERATOR_NAME, "body"),
|
247
259
|
],
|
248
260
|
).emit(repeat)
|
@@ -1,10 +1,13 @@
|
|
1
|
-
from typing import TYPE_CHECKING
|
1
|
+
from typing import TYPE_CHECKING, Optional
|
2
2
|
|
3
3
|
import sympy
|
4
4
|
|
5
5
|
from classiq.interface.debug_info.debug_info import FunctionDebugInfo
|
6
6
|
from classiq.interface.exceptions import ClassiqValueError
|
7
7
|
from classiq.interface.generator.expressions.expression import Expression
|
8
|
+
from classiq.interface.generator.expressions.proxies.classical.any_classical_value import (
|
9
|
+
AnyClassicalValue,
|
10
|
+
)
|
8
11
|
from classiq.interface.model.allocate import Allocate
|
9
12
|
from classiq.interface.model.handle_binding import NestedHandleBinding
|
10
13
|
from classiq.interface.model.quantum_type import QuantumBitvector
|
@@ -46,7 +49,7 @@ class AllocateEmitter(Emitter[Allocate]):
|
|
46
49
|
self.emit_statement(allocate)
|
47
50
|
return True
|
48
51
|
|
49
|
-
def _get_var_size(self, target: QuantumSymbol, size: Expression
|
52
|
+
def _get_var_size(self, target: QuantumSymbol, size: Optional[Expression]) -> str:
|
50
53
|
if size is None:
|
51
54
|
if not target.quantum_type.is_evaluated:
|
52
55
|
raise ClassiqValueError(
|
@@ -55,7 +58,9 @@ class AllocateEmitter(Emitter[Allocate]):
|
|
55
58
|
return str(target.quantum_type.size_in_bits)
|
56
59
|
|
57
60
|
size_value = self._interpreter.evaluate(size).value
|
58
|
-
if self._allow_symbolic_size and isinstance(
|
61
|
+
if self._allow_symbolic_size and isinstance(
|
62
|
+
size_value, (sympy.Basic, AnyClassicalValue)
|
63
|
+
):
|
59
64
|
return str(size_value)
|
60
65
|
if not isinstance(size_value, (int, float)):
|
61
66
|
raise ClassiqValueError(
|
@@ -1,6 +1,14 @@
|
|
1
|
+
from typing import Optional
|
2
|
+
|
3
|
+
from classiq.interface.exceptions import ClassiqExpansionError
|
1
4
|
from classiq.interface.generator.arith.arithmetic import compute_arithmetic_result_type
|
2
|
-
from classiq.interface.generator.
|
3
|
-
|
5
|
+
from classiq.interface.generator.expressions.expression import Expression
|
6
|
+
from classiq.interface.model.allocate import Allocate
|
7
|
+
from classiq.interface.model.bind_operation import BindOperation
|
8
|
+
from classiq.interface.model.handle_binding import (
|
9
|
+
ConcreteHandleBinding,
|
10
|
+
HandleBinding,
|
11
|
+
SubscriptHandleBinding,
|
4
12
|
)
|
5
13
|
from classiq.interface.model.quantum_expressions.arithmetic_operation import (
|
6
14
|
ArithmeticOperation,
|
@@ -9,6 +17,12 @@ from classiq.interface.model.quantum_expressions.arithmetic_operation import (
|
|
9
17
|
from classiq.interface.model.quantum_expressions.quantum_expression import (
|
10
18
|
QuantumAssignmentOperation,
|
11
19
|
)
|
20
|
+
from classiq.interface.model.quantum_function_call import QuantumFunctionCall
|
21
|
+
from classiq.interface.model.quantum_type import QuantumBitvector, QuantumNumeric
|
22
|
+
from classiq.interface.model.variable_declaration_statement import (
|
23
|
+
VariableDeclarationStatement,
|
24
|
+
)
|
25
|
+
from classiq.interface.model.within_apply_operation import WithinApply
|
12
26
|
|
13
27
|
from classiq.model_expansions.evaluators.quantum_type_utils import copy_type_information
|
14
28
|
from classiq.model_expansions.quantum_operations.arithmetic.explicit_boolean_expressions import (
|
@@ -18,26 +32,49 @@ from classiq.model_expansions.quantum_operations.arithmetic.explicit_boolean_exp
|
|
18
32
|
from classiq.model_expansions.quantum_operations.emitter import Emitter
|
19
33
|
from classiq.model_expansions.scope import QuantumSymbol
|
20
34
|
from classiq.model_expansions.transformers.ast_renamer import rename_variables
|
35
|
+
from classiq.qmod.builtins.functions.standard_gates import CX
|
21
36
|
|
22
37
|
|
23
38
|
class AssignmentResultProcessor(Emitter[QuantumAssignmentOperation]):
|
24
39
|
def emit(self, op: QuantumAssignmentOperation, /) -> bool:
|
25
|
-
if (
|
40
|
+
if not (
|
26
41
|
isinstance(op, ArithmeticOperation)
|
27
42
|
and op.operation_kind == ArithmeticOperationKind.Assignment
|
28
43
|
):
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
44
|
+
return False
|
45
|
+
|
46
|
+
result_symbol = self._interpreter.evaluate(op.result_var).as_type(QuantumSymbol)
|
47
|
+
result_type = result_symbol.quantum_type
|
48
|
+
|
49
|
+
validate_assignment_bool_expression(
|
50
|
+
result_symbol, op.expression.expr, op.operation_kind
|
51
|
+
)
|
52
|
+
convert_assignment_bool_expression(op)
|
53
|
+
|
54
|
+
inferred_result_type = self._infer_result_type(op)
|
55
|
+
if inferred_result_type is None:
|
56
|
+
return False
|
57
|
+
|
58
|
+
if not isinstance(result_type, QuantumNumeric):
|
59
|
+
copy_type_information(
|
60
|
+
inferred_result_type, result_symbol.quantum_type, str(op.result_var)
|
61
|
+
)
|
62
|
+
return False
|
63
|
+
|
64
|
+
self._copy_numeric_attributes(result_type, inferred_result_type)
|
65
|
+
if self._same_numeric_attributes(result_type, inferred_result_type):
|
66
|
+
return False
|
67
|
+
|
68
|
+
self._validate_declared_attributes(
|
69
|
+
result_type, inferred_result_type, str(op.result_var)
|
70
|
+
)
|
71
|
+
self._assign_to_inferred_var_and_bind(op, result_type, inferred_result_type)
|
72
|
+
return True
|
73
|
+
|
74
|
+
def _infer_result_type(self, op: ArithmeticOperation) -> Optional[QuantumNumeric]:
|
38
75
|
expr = self._evaluate_expression(op.expression)
|
39
76
|
if len(self._get_classical_vars_in_expression(expr)):
|
40
|
-
return
|
77
|
+
return None
|
41
78
|
symbols = self._get_symbols_in_expression(expr)
|
42
79
|
expr_str = rename_variables(
|
43
80
|
expr.expr,
|
@@ -48,16 +85,180 @@ class AssignmentResultProcessor(Emitter[QuantumAssignmentOperation]):
|
|
48
85
|
expr_str = expr_str.replace(
|
49
86
|
symbol.handle.qmod_expr, symbol.handle.identifier
|
50
87
|
)
|
51
|
-
|
88
|
+
return compute_arithmetic_result_type(
|
52
89
|
expr_str,
|
53
90
|
{symbol.handle.identifier: symbol.quantum_type for symbol in symbols},
|
54
91
|
self._machine_precision,
|
55
92
|
)
|
56
|
-
result_symbol = self._interpreter.evaluate(op.result_var).as_type(QuantumSymbol)
|
57
93
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
94
|
+
@staticmethod
|
95
|
+
def _copy_numeric_attributes(
|
96
|
+
result_type: QuantumNumeric, inferred_result_type: QuantumNumeric
|
97
|
+
) -> None:
|
98
|
+
if not result_type.has_size_in_bits:
|
99
|
+
result_type.size = Expression(expr=str(inferred_result_type.size_in_bits))
|
100
|
+
if not result_type.has_sign:
|
101
|
+
result_type.is_signed = Expression(
|
102
|
+
expr=str(inferred_result_type.sign_value)
|
103
|
+
)
|
104
|
+
if not result_type.has_fraction_digits:
|
105
|
+
result_type.fraction_digits = Expression(
|
106
|
+
expr=str(inferred_result_type.fraction_digits_value)
|
107
|
+
)
|
108
|
+
|
109
|
+
@staticmethod
|
110
|
+
def _same_numeric_attributes(
|
111
|
+
result_type: QuantumNumeric, inferred_result_type: QuantumNumeric
|
112
|
+
) -> bool:
|
113
|
+
return (
|
114
|
+
result_type.size_in_bits == inferred_result_type.size_in_bits
|
115
|
+
and result_type.sign_value == inferred_result_type.sign_value
|
116
|
+
and result_type.fraction_digits_value
|
117
|
+
== inferred_result_type.fraction_digits_value
|
118
|
+
)
|
119
|
+
|
120
|
+
@classmethod
|
121
|
+
def _validate_declared_attributes(
|
122
|
+
cls, result_type: QuantumNumeric, inferred_result_type: QuantumNumeric, var: str
|
123
|
+
) -> None:
|
124
|
+
result_size, result_sign, result_fractions = (
|
125
|
+
result_type.size_in_bits,
|
126
|
+
result_type.sign_value,
|
127
|
+
result_type.fraction_digits_value,
|
128
|
+
)
|
129
|
+
inferred_size, inferred_sign, inferred_fractions = (
|
130
|
+
inferred_result_type.size_in_bits,
|
131
|
+
inferred_result_type.sign_value,
|
132
|
+
inferred_result_type.fraction_digits_value,
|
133
|
+
)
|
134
|
+
result_integers = result_size - result_fractions
|
135
|
+
inferred_integers = inferred_size - inferred_fractions
|
136
|
+
|
137
|
+
if (
|
138
|
+
(result_integers < inferred_integers)
|
139
|
+
or (result_fractions < inferred_fractions)
|
140
|
+
or (not result_sign and inferred_sign)
|
141
|
+
or (
|
142
|
+
result_sign
|
143
|
+
and not inferred_sign
|
144
|
+
and result_integers == inferred_integers
|
145
|
+
)
|
146
|
+
):
|
147
|
+
if (
|
148
|
+
not result_sign
|
149
|
+
and result_fractions == 0
|
150
|
+
and not inferred_sign
|
151
|
+
and inferred_fractions == 0
|
152
|
+
):
|
153
|
+
result_size_str = f"size {result_size}"
|
154
|
+
inferred_size_str = f"size {inferred_size}"
|
155
|
+
hint = f"Hint: increase the size in the declaration of {var!r} or omit it to enable automatic inference."
|
156
|
+
else:
|
157
|
+
result_size_str = f"size {result_size}, {'signed' if result_sign else 'unsigned'}, and {result_fractions} fraction digits"
|
158
|
+
inferred_size_str = f"size {inferred_size}, {'signed' if inferred_sign else 'unsigned'}, and {inferred_fractions} fraction digits"
|
159
|
+
hint = f"Hint: omit the numeric attributes from the declaration of {var!r} to enable automatic inference."
|
160
|
+
raise ClassiqExpansionError(
|
161
|
+
f"Cannot assign an expression with inferred {inferred_size_str} to variable {var!r} with declared {result_size_str}. {hint}"
|
162
|
+
)
|
163
|
+
|
164
|
+
@staticmethod
|
165
|
+
def _craft_size_string(size: int, is_signed: bool, fraction_digits: int) -> str:
|
166
|
+
extra = (
|
167
|
+
f", with {fraction_digits} fraction digits" if fraction_digits > 0 else ""
|
168
|
+
)
|
169
|
+
return f"{size} ({'signed' if is_signed else 'unsigned'}{extra})"
|
170
|
+
|
171
|
+
def _assign_to_inferred_var_and_bind(
|
172
|
+
self,
|
173
|
+
op: ArithmeticOperation,
|
174
|
+
result_type: QuantumNumeric,
|
175
|
+
inferred_result_type: QuantumNumeric,
|
176
|
+
) -> None:
|
177
|
+
handles: list[HandleBinding] = []
|
178
|
+
|
179
|
+
extra_fraction_digits = (
|
180
|
+
result_type.fraction_digits_value
|
181
|
+
- inferred_result_type.fraction_digits_value
|
182
|
+
)
|
183
|
+
if extra_fraction_digits > 0:
|
184
|
+
handles.append(
|
185
|
+
self._declare_qarray("extra_fraction_digits", extra_fraction_digits)
|
186
|
+
)
|
187
|
+
|
188
|
+
inferred_result_name = self._counted_name_allocator.allocate("inferred_result")
|
189
|
+
inferred_result_handle = HandleBinding(name=inferred_result_name)
|
190
|
+
self._interpreter.emit(
|
191
|
+
VariableDeclarationStatement(
|
192
|
+
name=inferred_result_name, quantum_type=inferred_result_type
|
193
|
+
)
|
194
|
+
)
|
195
|
+
handles.append(inferred_result_handle)
|
196
|
+
modified_op = op.model_copy(update={"result_var": inferred_result_handle})
|
197
|
+
self._interpreter.add_to_debug_info(modified_op)
|
198
|
+
self._interpreter.emit(modified_op)
|
199
|
+
|
200
|
+
result_integer_size = (
|
201
|
+
result_type.size_in_bits - result_type.fraction_digits_value
|
202
|
+
)
|
203
|
+
inferred_result_integer_size = (
|
204
|
+
inferred_result_type.size_in_bits
|
205
|
+
- inferred_result_type.fraction_digits_value
|
206
|
+
)
|
207
|
+
extra_integers = result_integer_size - inferred_result_integer_size
|
208
|
+
if extra_integers > 0:
|
209
|
+
handles.append(self._declare_qarray("extra_integers", extra_integers))
|
210
|
+
|
211
|
+
self._interpreter.emit(
|
212
|
+
BindOperation(in_handles=handles, out_handles=[op.result_var])
|
213
|
+
)
|
214
|
+
|
215
|
+
if (
|
216
|
+
result_type.sign_value
|
217
|
+
and inferred_result_type.sign_value
|
218
|
+
and extra_integers > 0
|
219
|
+
):
|
220
|
+
sign_idx = result_type.size_in_bits - extra_integers - 1
|
221
|
+
self._sign_extension(op.result_var, sign_idx, result_type.size_in_bits)
|
222
|
+
|
223
|
+
def _declare_qarray(
|
224
|
+
self,
|
225
|
+
prefix: str,
|
226
|
+
size: int,
|
227
|
+
allocate: bool = True,
|
228
|
+
) -> HandleBinding:
|
229
|
+
name = self._counted_name_allocator.allocate(prefix)
|
230
|
+
handle = HandleBinding(name=name)
|
231
|
+
quantum_type = QuantumBitvector(length=Expression(expr=str(size)))
|
232
|
+
self._interpreter.emit(
|
233
|
+
VariableDeclarationStatement(name=name, quantum_type=quantum_type)
|
234
|
+
)
|
235
|
+
if allocate:
|
236
|
+
self._interpreter.emit(Allocate(target=handle))
|
237
|
+
return handle
|
238
|
+
|
239
|
+
def _sign_extension(
|
240
|
+
self,
|
241
|
+
result_var: ConcreteHandleBinding,
|
242
|
+
sign_idx: int,
|
243
|
+
size: int,
|
244
|
+
) -> None:
|
245
|
+
aux = self._declare_qarray("inferred_result_aux", size, allocate=False)
|
246
|
+
self._interpreter.emit(
|
247
|
+
WithinApply(
|
248
|
+
compute=[BindOperation(in_handles=[result_var], out_handles=[aux])],
|
249
|
+
action=[
|
250
|
+
QuantumFunctionCall(
|
251
|
+
function=CX.func_decl.name,
|
252
|
+
positional_args=[
|
253
|
+
SubscriptHandleBinding(
|
254
|
+
base_handle=aux, index=Expression(expr=str(sign_idx))
|
255
|
+
),
|
256
|
+
SubscriptHandleBinding(
|
257
|
+
base_handle=aux, index=Expression(expr=str(idx))
|
258
|
+
),
|
259
|
+
],
|
260
|
+
)
|
261
|
+
for idx in range(sign_idx + 1, size)
|
262
|
+
],
|
263
|
+
)
|
63
264
|
)
|
@@ -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
|
@@ -4,13 +4,17 @@ from typing import (
|
|
4
4
|
TYPE_CHECKING,
|
5
5
|
Any,
|
6
6
|
Generic,
|
7
|
+
Optional,
|
7
8
|
cast,
|
8
9
|
)
|
9
10
|
from uuid import UUID
|
10
11
|
|
11
12
|
import sympy
|
12
13
|
|
13
|
-
from classiq.interface.debug_info.debug_info import
|
14
|
+
from classiq.interface.debug_info.debug_info import (
|
15
|
+
FunctionDebugInfo,
|
16
|
+
calculate_port_to_passed_variable_mapping,
|
17
|
+
)
|
14
18
|
from classiq.interface.exceptions import ClassiqExpansionError
|
15
19
|
from classiq.interface.generator.expressions.proxies.classical.any_classical_value import (
|
16
20
|
AnyClassicalValue,
|
@@ -59,7 +63,13 @@ from classiq.model_expansions.quantum_operations.emitter import (
|
|
59
63
|
Emitter,
|
60
64
|
QuantumStatementT,
|
61
65
|
)
|
62
|
-
from classiq.model_expansions.scope import
|
66
|
+
from classiq.model_expansions.scope import (
|
67
|
+
Evaluated,
|
68
|
+
QuantumSymbol,
|
69
|
+
QuantumSymbolList,
|
70
|
+
QuantumVariable,
|
71
|
+
Scope,
|
72
|
+
)
|
63
73
|
from classiq.model_expansions.transformers.var_splitter import VarSplitter
|
64
74
|
from classiq.model_expansions.utils.text_utils import are, readable_list, s
|
65
75
|
from classiq.qmod.semantics.validation.signature_validation import (
|
@@ -71,11 +81,14 @@ if TYPE_CHECKING:
|
|
71
81
|
|
72
82
|
|
73
83
|
def _validate_cloning(evaluated_args: list[Evaluated]) -> None:
|
74
|
-
handles =
|
75
|
-
|
84
|
+
handles = chain.from_iterable(
|
85
|
+
(
|
86
|
+
[arg.value.handle]
|
87
|
+
if isinstance(arg.value, QuantumSymbol)
|
88
|
+
else arg.value.handles if isinstance(arg.value, QuantumSymbolList) else []
|
89
|
+
)
|
76
90
|
for arg in evaluated_args
|
77
|
-
|
78
|
-
]
|
91
|
+
)
|
79
92
|
for handle, other_handle in combinations(handles, 2):
|
80
93
|
if handle.overlaps(other_handle):
|
81
94
|
if handle == other_handle:
|
@@ -90,13 +103,15 @@ def _validate_cloning(evaluated_args: list[Evaluated]) -> None:
|
|
90
103
|
|
91
104
|
|
92
105
|
def _is_symbolic(arg: Any) -> bool:
|
106
|
+
if isinstance(arg, (ClassicalProxy, AnyClassicalValue)):
|
107
|
+
return True
|
93
108
|
if isinstance(arg, list):
|
94
109
|
return any(_is_symbolic(item) for item in arg)
|
95
110
|
if isinstance(arg, QmodStructInstance):
|
96
111
|
return any(_is_symbolic(item) for item in arg.fields.values())
|
97
112
|
if isinstance(arg, sympy.Basic):
|
98
113
|
return len(arg.free_symbols) > 0
|
99
|
-
return
|
114
|
+
return False
|
100
115
|
|
101
116
|
|
102
117
|
class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSplitter):
|
@@ -125,7 +140,7 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
|
|
125
140
|
self,
|
126
141
|
function: FunctionClosure,
|
127
142
|
args: list[ArgValue],
|
128
|
-
propagated_debug_info: FunctionDebugInfo
|
143
|
+
propagated_debug_info: Optional[FunctionDebugInfo],
|
129
144
|
) -> QuantumFunctionCall:
|
130
145
|
call = self._create_quantum_function_call(
|
131
146
|
function, args, propagated_debug_info=propagated_debug_info
|
@@ -135,8 +150,8 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
|
|
135
150
|
|
136
151
|
@staticmethod
|
137
152
|
def _get_back_ref(
|
138
|
-
propagated_debug_info: FunctionDebugInfo
|
139
|
-
) -> UUID
|
153
|
+
propagated_debug_info: Optional[FunctionDebugInfo],
|
154
|
+
) -> Optional[UUID]:
|
140
155
|
if propagated_debug_info is None:
|
141
156
|
return None
|
142
157
|
if propagated_debug_info.node is None:
|
@@ -147,7 +162,7 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
|
|
147
162
|
self,
|
148
163
|
function: FunctionClosure,
|
149
164
|
args: list[ArgValue],
|
150
|
-
propagated_debug_info: FunctionDebugInfo
|
165
|
+
propagated_debug_info: Optional[FunctionDebugInfo],
|
151
166
|
) -> QuantumFunctionCall:
|
152
167
|
function = function.clone()
|
153
168
|
function = function.set_depth(self._builder.current_function.depth + 1)
|
@@ -187,11 +202,13 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
|
|
187
202
|
back_ref=self._get_back_ref(propagated_debug_info),
|
188
203
|
)
|
189
204
|
|
190
|
-
port_to_passed_variable_map =
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
205
|
+
port_to_passed_variable_map = calculate_port_to_passed_variable_mapping(
|
206
|
+
new_positional_arg_decls,
|
207
|
+
[
|
208
|
+
arg.value.handle if isinstance(arg.value, QuantumSymbol) else None
|
209
|
+
for arg in evaluated_args
|
210
|
+
],
|
211
|
+
)
|
195
212
|
self._debug_info[new_call.uuid] = FunctionDebugInfo(
|
196
213
|
name=new_call.func_name,
|
197
214
|
port_to_passed_variable_map=port_to_passed_variable_map,
|
@@ -274,7 +291,7 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
|
|
274
291
|
) -> None:
|
275
292
|
for parameter, argument in zip(parameters, arguments):
|
276
293
|
param_handle = HandleBinding(name=parameter.name)
|
277
|
-
if isinstance(argument.value,
|
294
|
+
if isinstance(argument.value, QuantumVariable):
|
278
295
|
assert isinstance(parameter, PortDeclaration)
|
279
296
|
closure.scope[parameter.name] = Evaluated(
|
280
297
|
QuantumSymbol(
|
@@ -307,7 +324,7 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
|
|
307
324
|
positional_args = [
|
308
325
|
arg.emit()
|
309
326
|
for arg in evaluated_args
|
310
|
-
if isinstance(arg.value,
|
327
|
+
if isinstance(arg.value, QuantumVariable) or _is_symbolic(arg.value)
|
311
328
|
]
|
312
329
|
|
313
330
|
return positional_args
|