classiq 0.45.1__py3-none-any.whl → 0.46.1__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/__init__.py +0 -1
- classiq/_internals/__init__.py +20 -0
- classiq/_internals/authentication/authentication.py +11 -0
- classiq/analyzer/analyzer.py +12 -10
- classiq/applications/combinatorial_helpers/combinatorial_problem_utils.py +1 -1
- classiq/applications/combinatorial_helpers/pauli_helpers/pauli_utils.py +1 -1
- classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +1 -1
- classiq/applications/libraries/qmci_library.py +4 -9
- classiq/execution/execution_session.py +68 -7
- classiq/executor.py +14 -2
- classiq/interface/_version.py +1 -1
- classiq/interface/backend/backend_preferences.py +189 -0
- classiq/interface/backend/quantum_backend_providers.py +38 -0
- classiq/interface/debug_info/debug_info.py +22 -2
- classiq/interface/exceptions.py +16 -1
- classiq/interface/executor/execution_preferences.py +18 -0
- classiq/interface/generator/application_apis/chemistry_declarations.py +1 -177
- classiq/interface/generator/application_apis/combinatorial_optimization_declarations.py +0 -12
- classiq/interface/generator/application_apis/finance_declarations.py +8 -43
- classiq/interface/generator/application_apis/qsvm_declarations.py +0 -78
- classiq/interface/generator/builtin_api_builder.py +0 -3
- classiq/interface/generator/functions/__init__.py +0 -2
- classiq/interface/generator/functions/builtins/__init__.py +0 -15
- classiq/interface/generator/generated_circuit_data.py +2 -0
- classiq/interface/generator/hardware/hardware_data.py +37 -0
- classiq/interface/generator/model/constraints.py +18 -1
- classiq/interface/generator/model/preferences/preferences.py +53 -1
- classiq/interface/generator/model/quantum_register.py +1 -1
- classiq/interface/generator/quantum_program.py +10 -2
- classiq/interface/generator/transpiler_basis_gates.py +4 -0
- classiq/interface/generator/types/builtin_enum_declarations.py +136 -21
- classiq/interface/generator/types/enum_declaration.py +1 -3
- classiq/interface/generator/types/struct_declaration.py +1 -3
- classiq/interface/hardware.py +5 -0
- classiq/interface/ide/visual_model.py +1 -1
- classiq/interface/model/classical_parameter_declaration.py +6 -0
- classiq/interface/model/inplace_binary_operation.py +0 -14
- classiq/interface/model/model.py +1 -18
- classiq/interface/model/port_declaration.py +4 -2
- classiq/interface/model/quantum_function_declaration.py +19 -6
- classiq/interface/model/quantum_lambda_function.py +11 -1
- classiq/interface/model/quantum_variable_declaration.py +1 -1
- classiq/model_expansions/__init__.py +0 -0
- classiq/model_expansions/atomic_expression_functions_defs.py +250 -0
- classiq/model_expansions/capturing/__init__.py +0 -0
- classiq/model_expansions/capturing/captured_var_manager.py +50 -0
- classiq/model_expansions/capturing/mangling_utils.py +17 -0
- classiq/model_expansions/capturing/propagated_var_stack.py +180 -0
- classiq/model_expansions/closure.py +160 -0
- classiq/model_expansions/debug_flag.py +3 -0
- classiq/model_expansions/evaluators/__init__.py +0 -0
- classiq/model_expansions/evaluators/arg_type_match.py +160 -0
- classiq/model_expansions/evaluators/argument_types.py +42 -0
- classiq/model_expansions/evaluators/classical_expression.py +36 -0
- classiq/model_expansions/evaluators/control.py +144 -0
- classiq/model_expansions/evaluators/parameter_types.py +227 -0
- classiq/model_expansions/evaluators/quantum_type_utils.py +235 -0
- classiq/model_expansions/evaluators/type_type_match.py +90 -0
- classiq/model_expansions/expression_evaluator.py +125 -0
- classiq/model_expansions/expression_renamer.py +76 -0
- classiq/model_expansions/function_builder.py +192 -0
- classiq/model_expansions/generative_functions.py +101 -0
- classiq/model_expansions/interpreter.py +365 -0
- classiq/model_expansions/model_tables.py +105 -0
- classiq/model_expansions/quantum_operations/__init__.py +19 -0
- classiq/model_expansions/quantum_operations/bind.py +64 -0
- classiq/model_expansions/quantum_operations/classicalif.py +39 -0
- classiq/model_expansions/quantum_operations/control.py +235 -0
- classiq/model_expansions/quantum_operations/emitter.py +215 -0
- classiq/model_expansions/quantum_operations/expression_operation.py +218 -0
- classiq/model_expansions/quantum_operations/inplace_binary_operation.py +250 -0
- classiq/model_expansions/quantum_operations/invert.py +38 -0
- classiq/model_expansions/quantum_operations/power.py +74 -0
- classiq/model_expansions/quantum_operations/quantum_assignment_operation.py +174 -0
- classiq/model_expansions/quantum_operations/quantum_function_call.py +15 -0
- classiq/model_expansions/quantum_operations/repeat.py +33 -0
- classiq/model_expansions/quantum_operations/variable_decleration.py +28 -0
- classiq/model_expansions/quantum_operations/within_apply.py +46 -0
- classiq/model_expansions/scope.py +226 -0
- classiq/model_expansions/scope_initialization.py +136 -0
- classiq/model_expansions/sympy_conversion/__init__.py +0 -0
- classiq/model_expansions/sympy_conversion/arithmetics.py +49 -0
- classiq/model_expansions/sympy_conversion/expression_to_sympy.py +150 -0
- classiq/model_expansions/sympy_conversion/sympy_to_python.py +113 -0
- classiq/model_expansions/utils/__init__.py +0 -0
- classiq/model_expansions/utils/counted_name_allocator.py +11 -0
- classiq/model_expansions/visitors/__init__.py +0 -0
- classiq/model_expansions/visitors/boolean_expression_transformers.py +214 -0
- classiq/model_expansions/visitors/variable_references.py +115 -0
- classiq/qmod/__init__.py +1 -3
- classiq/qmod/builtins/enums.py +33 -2
- classiq/qmod/builtins/functions/__init__.py +251 -0
- classiq/qmod/builtins/functions/amplitude_estimation.py +27 -0
- classiq/qmod/builtins/functions/arithmetic.py +68 -0
- classiq/qmod/builtins/functions/benchmarking.py +8 -0
- classiq/qmod/builtins/functions/chemistry.py +91 -0
- classiq/qmod/builtins/functions/discrete_sine_cosine_transform.py +105 -0
- classiq/qmod/builtins/functions/exponentiation.py +111 -0
- classiq/qmod/builtins/functions/finance.py +34 -0
- classiq/qmod/builtins/functions/grover.py +178 -0
- classiq/qmod/builtins/functions/hea.py +59 -0
- classiq/qmod/builtins/functions/linear_pauli_rotation.py +65 -0
- classiq/qmod/builtins/functions/modular_exponentiation.py +137 -0
- classiq/qmod/builtins/functions/operators.py +22 -0
- classiq/qmod/builtins/functions/qaoa_penalty.py +116 -0
- classiq/qmod/builtins/functions/qft.py +23 -0
- classiq/qmod/builtins/functions/qpe.py +39 -0
- classiq/qmod/builtins/functions/qsvm.py +24 -0
- classiq/qmod/builtins/functions/qsvt.py +136 -0
- classiq/qmod/builtins/functions/standard_gates.py +739 -0
- classiq/qmod/builtins/functions/state_preparation.py +357 -0
- classiq/qmod/builtins/functions/swap_test.py +25 -0
- classiq/qmod/builtins/structs.py +50 -28
- classiq/qmod/cparam.py +64 -0
- classiq/qmod/create_model_function.py +190 -0
- classiq/qmod/declaration_inferrer.py +52 -81
- classiq/qmod/expression_query.py +16 -0
- classiq/qmod/generative.py +48 -0
- classiq/qmod/model_state_container.py +1 -2
- classiq/qmod/native/pretty_printer.py +7 -11
- classiq/qmod/pretty_print/pretty_printer.py +7 -11
- classiq/qmod/python_classical_type.py +67 -0
- classiq/qmod/qfunc.py +19 -4
- classiq/qmod/qmod_parameter.py +15 -64
- classiq/qmod/qmod_variable.py +27 -45
- classiq/qmod/quantum_callable.py +1 -1
- classiq/qmod/quantum_expandable.py +10 -4
- classiq/qmod/quantum_function.py +22 -40
- classiq/qmod/semantics/error_manager.py +22 -10
- classiq/qmod/semantics/static_semantics_visitor.py +10 -12
- classiq/qmod/semantics/validation/types_validation.py +6 -7
- classiq/qmod/utilities.py +2 -2
- classiq/qmod/write_qmod.py +14 -0
- classiq/show.py +10 -0
- classiq/synthesis.py +46 -2
- {classiq-0.45.1.dist-info → classiq-0.46.1.dist-info}/METADATA +1 -1
- {classiq-0.45.1.dist-info → classiq-0.46.1.dist-info}/RECORD +138 -74
- classiq/interface/generator/functions/builtins/core_library/__init__.py +0 -16
- classiq/interface/generator/functions/builtins/core_library/atomic_quantum_functions.py +0 -710
- classiq/interface/generator/functions/builtins/core_library/exponentiation_functions.py +0 -105
- classiq/interface/generator/functions/builtins/open_lib_functions.py +0 -2489
- classiq/interface/generator/functions/builtins/quantum_operators.py +0 -24
- classiq/interface/generator/types/builtin_struct_declarations/__init__.py +0 -1
- classiq/interface/generator/types/builtin_struct_declarations/pauli_struct_declarations.py +0 -21
- classiq/qmod/builtins/functions.py +0 -1029
- {classiq-0.45.1.dist-info → classiq-0.46.1.dist-info}/WHEEL +0 -0
@@ -0,0 +1,174 @@
|
|
1
|
+
import ast
|
2
|
+
from typing import Tuple
|
3
|
+
|
4
|
+
from classiq.interface.exceptions import ClassiqExpansionError
|
5
|
+
from classiq.interface.generator.compiler_keywords import INPLACE_ARITH_AUX_VAR_PREFIX
|
6
|
+
from classiq.interface.generator.expressions.expression import Expression
|
7
|
+
from classiq.interface.model.handle_binding import HandleBinding
|
8
|
+
from classiq.interface.model.inplace_binary_operation import (
|
9
|
+
BinaryOperation,
|
10
|
+
InplaceBinaryOperation,
|
11
|
+
)
|
12
|
+
from classiq.interface.model.quantum_expressions.arithmetic_operation import (
|
13
|
+
ArithmeticOperation,
|
14
|
+
)
|
15
|
+
from classiq.interface.model.quantum_expressions.quantum_expression import (
|
16
|
+
QuantumAssignmentOperation,
|
17
|
+
)
|
18
|
+
from classiq.interface.model.quantum_function_call import QuantumFunctionCall
|
19
|
+
from classiq.interface.model.quantum_type import (
|
20
|
+
QuantumNumeric,
|
21
|
+
)
|
22
|
+
from classiq.interface.model.variable_declaration_statement import (
|
23
|
+
VariableDeclarationStatement,
|
24
|
+
)
|
25
|
+
from classiq.interface.model.within_apply_operation import WithinApply
|
26
|
+
|
27
|
+
from classiq.model_expansions.evaluators.quantum_type_utils import copy_type_information
|
28
|
+
from classiq.model_expansions.quantum_operations.expression_operation import (
|
29
|
+
ExpressionOperationEmitter,
|
30
|
+
)
|
31
|
+
from classiq.model_expansions.scope import QuantumSymbol
|
32
|
+
from classiq.model_expansions.visitors.boolean_expression_transformers import (
|
33
|
+
BooleanExpressionFuncLibAdapter,
|
34
|
+
BooleanExpressionOptimizer,
|
35
|
+
)
|
36
|
+
from classiq.qmod import builtins
|
37
|
+
from classiq.qmod.builtins.functions import X
|
38
|
+
|
39
|
+
|
40
|
+
class QuantumAssignmentOperationEmitter(
|
41
|
+
ExpressionOperationEmitter[QuantumAssignmentOperation]
|
42
|
+
):
|
43
|
+
def emit(self, op: QuantumAssignmentOperation, /) -> None:
|
44
|
+
new_expression = self._evaluate_op_expression(op)
|
45
|
+
arrays_with_subscript = self._get_symbols_to_split(new_expression)
|
46
|
+
if len(arrays_with_subscript) > 0:
|
47
|
+
self._emit_with_split(op, new_expression, arrays_with_subscript)
|
48
|
+
return
|
49
|
+
|
50
|
+
self._emit_op(new_expression, op)
|
51
|
+
|
52
|
+
def _emit_op(self, expression: Expression, op: QuantumAssignmentOperation) -> None:
|
53
|
+
op = self._evaluate_types_in_expression(op, expression)
|
54
|
+
with self._propagated_var_stack.capture_variables(op):
|
55
|
+
pass # This propagates the expression variables
|
56
|
+
if isinstance(op, ArithmeticOperation):
|
57
|
+
self._emit_arithmetic_op(op, expression)
|
58
|
+
return
|
59
|
+
self._emit_general_assignment_operation(op)
|
60
|
+
|
61
|
+
def _emit_arithmetic_op(
|
62
|
+
self, op: ArithmeticOperation, expression: Expression
|
63
|
+
) -> None:
|
64
|
+
op, expression, is_bool_opt = self._optimize_boolean_expression(op, expression)
|
65
|
+
if op.inplace_result:
|
66
|
+
self._emit_inplace_arithmetic_op(op, expression, is_bool_opt)
|
67
|
+
else:
|
68
|
+
self._emit_general_assignment_operation(op)
|
69
|
+
|
70
|
+
def _emit_inplace_arithmetic_op(
|
71
|
+
self, op: ArithmeticOperation, expression: Expression, is_bool_opt: bool
|
72
|
+
) -> None:
|
73
|
+
if op.result_type.size_in_bits > 1 or not _is_res_boolean(op):
|
74
|
+
_validate_naive_inplace_handles(op)
|
75
|
+
self._build_naive_inplace(op, expression)
|
76
|
+
return
|
77
|
+
|
78
|
+
op, expression, to_invert = self._adapt_boolean_inplace(
|
79
|
+
op, expression, is_bool_opt
|
80
|
+
)
|
81
|
+
self._emit_general_assignment_operation(op)
|
82
|
+
if not to_invert:
|
83
|
+
return
|
84
|
+
|
85
|
+
call = QuantumFunctionCall(
|
86
|
+
function=builtins.functions.X.__name__, # type:ignore[attr-defined]
|
87
|
+
positional_args=[op.result_var],
|
88
|
+
source_ref=op.source_ref,
|
89
|
+
)
|
90
|
+
call.set_func_decl(X.func_decl)
|
91
|
+
self._interpreter.emit_statement(call)
|
92
|
+
|
93
|
+
def _emit_general_assignment_operation(
|
94
|
+
self, op: QuantumAssignmentOperation
|
95
|
+
) -> None:
|
96
|
+
result = self._interpreter.evaluate(op.result_var).as_type(QuantumSymbol)
|
97
|
+
copy_type_information(op.result_type, result.quantum_type, str(result.handle))
|
98
|
+
self._builder.emit_statement(op)
|
99
|
+
|
100
|
+
def _optimize_boolean_expression(
|
101
|
+
self, op: ArithmeticOperation, expression: Expression
|
102
|
+
) -> Tuple[ArithmeticOperation, Expression, bool]:
|
103
|
+
if not _all_vars_boolean(op):
|
104
|
+
return op, expression, False
|
105
|
+
optimizer = BooleanExpressionOptimizer()
|
106
|
+
optimized_expression = Expression(
|
107
|
+
expr=ast.unparse(optimizer.visit(ast.parse(expression.expr)))
|
108
|
+
)
|
109
|
+
optimized_expression = self._evaluate_expression(optimized_expression)
|
110
|
+
optimized_op = op.copy(update=dict(expression=optimized_expression))
|
111
|
+
return optimized_op, optimized_expression, optimizer.is_convertible
|
112
|
+
|
113
|
+
def _adapt_boolean_inplace(
|
114
|
+
self, op: ArithmeticOperation, expression: Expression, is_bool_opt: bool
|
115
|
+
) -> Tuple[ArithmeticOperation, Expression, bool]:
|
116
|
+
adapter = BooleanExpressionFuncLibAdapter(is_bool_opt)
|
117
|
+
adapted_expression = self._evaluate_expression(
|
118
|
+
Expression(expr=ast.unparse(adapter.visit(ast.parse(expression.expr))))
|
119
|
+
)
|
120
|
+
adapted_op = op.copy(update=dict(expression=adapted_expression))
|
121
|
+
return adapted_op, adapted_expression, adapter.to_invert
|
122
|
+
|
123
|
+
def _build_naive_inplace(
|
124
|
+
self, qe: ArithmeticOperation, new_expression: Expression
|
125
|
+
) -> None:
|
126
|
+
aux_var = self._counted_name_allocator.allocate(INPLACE_ARITH_AUX_VAR_PREFIX)
|
127
|
+
self._interpreter.emit_statement(
|
128
|
+
VariableDeclarationStatement(name=aux_var, quantum_type=QuantumNumeric())
|
129
|
+
)
|
130
|
+
arith_expression = ArithmeticOperation(
|
131
|
+
result_var=HandleBinding(name=aux_var),
|
132
|
+
expression=new_expression,
|
133
|
+
inplace_result=False,
|
134
|
+
)
|
135
|
+
inplace_store = InplaceBinaryOperation(
|
136
|
+
operation=BinaryOperation.Xor,
|
137
|
+
target=qe.result_var,
|
138
|
+
value=HandleBinding(name=aux_var),
|
139
|
+
)
|
140
|
+
self._interpreter.emit_statement(
|
141
|
+
WithinApply(compute=[arith_expression], action=[inplace_store])
|
142
|
+
)
|
143
|
+
|
144
|
+
|
145
|
+
def _validate_naive_inplace_handles(qe: ArithmeticOperation) -> None:
|
146
|
+
if qe.result_var in qe.var_handles:
|
147
|
+
raise ClassiqExpansionError(
|
148
|
+
f"Quantum variable {qe.result_var.name} cannot appear on both sides of "
|
149
|
+
f"the ^= assignment"
|
150
|
+
)
|
151
|
+
|
152
|
+
|
153
|
+
def _all_vars_boolean(op: ArithmeticOperation) -> bool:
|
154
|
+
if not all(
|
155
|
+
var_type.has_size_in_bits and var_type.size_in_bits == 1
|
156
|
+
for var_type in op.var_types.values()
|
157
|
+
):
|
158
|
+
return False
|
159
|
+
if any(
|
160
|
+
isinstance(var_type, QuantumNumeric)
|
161
|
+
and (var_type.sign_value or var_type.fraction_digits_value > 0)
|
162
|
+
for var_type in op.var_types.values()
|
163
|
+
):
|
164
|
+
return False
|
165
|
+
return _is_res_boolean(op)
|
166
|
+
|
167
|
+
|
168
|
+
def _is_res_boolean(op: ArithmeticOperation) -> bool:
|
169
|
+
if not (op.result_type.has_size_in_bits and op.result_type.size_in_bits == 1):
|
170
|
+
return False
|
171
|
+
return not (
|
172
|
+
isinstance(op.result_type, QuantumNumeric)
|
173
|
+
and (op.result_type.sign_value or op.result_type.fraction_digits_value > 0)
|
174
|
+
)
|
@@ -0,0 +1,15 @@
|
|
1
|
+
from classiq.interface.model.quantum_function_call import QuantumFunctionCall
|
2
|
+
|
3
|
+
from classiq.model_expansions.closure import FunctionClosure
|
4
|
+
from classiq.model_expansions.quantum_operations.emitter import Emitter
|
5
|
+
from classiq.qmod.semantics.error_manager import ErrorManager
|
6
|
+
|
7
|
+
|
8
|
+
class QuantumFunctionCallEmitter(Emitter[QuantumFunctionCall]):
|
9
|
+
def emit(self, call: QuantumFunctionCall, /) -> None:
|
10
|
+
function = self._interpreter.evaluate(call.function).as_type(FunctionClosure)
|
11
|
+
args = call.positional_args
|
12
|
+
with ErrorManager().call(
|
13
|
+
function.name
|
14
|
+
), self._propagated_var_stack.capture_variables(call):
|
15
|
+
self._emit_quantum_function_call(function, args)
|
@@ -0,0 +1,33 @@
|
|
1
|
+
from classiq.interface.generator.expressions.expression import Expression
|
2
|
+
from classiq.interface.generator.functions.builtins.internal_operators import (
|
3
|
+
REPEAT_OPERATOR_NAME,
|
4
|
+
)
|
5
|
+
from classiq.interface.generator.functions.classical_type import Integer
|
6
|
+
from classiq.interface.model.classical_parameter_declaration import (
|
7
|
+
ClassicalParameterDeclaration,
|
8
|
+
)
|
9
|
+
from classiq.interface.model.repeat import Repeat
|
10
|
+
|
11
|
+
from classiq.model_expansions.closure import FunctionClosure
|
12
|
+
from classiq.model_expansions.quantum_operations.emitter import Emitter
|
13
|
+
from classiq.model_expansions.scope import Scope
|
14
|
+
|
15
|
+
|
16
|
+
class RepeatEmitter(Emitter[Repeat]):
|
17
|
+
def emit(self, repeat: Repeat, /) -> None:
|
18
|
+
count = self._interpreter.evaluate(repeat.count).as_type(int)
|
19
|
+
for i in range(count):
|
20
|
+
with self._propagated_var_stack.capture_variables(repeat):
|
21
|
+
iteration_function = FunctionClosure.create(
|
22
|
+
name=REPEAT_OPERATOR_NAME,
|
23
|
+
positional_arg_declarations=[
|
24
|
+
ClassicalParameterDeclaration(
|
25
|
+
name=repeat.iter_var, classical_type=Integer()
|
26
|
+
)
|
27
|
+
],
|
28
|
+
body=repeat.body,
|
29
|
+
scope=Scope(parent=self._current_scope),
|
30
|
+
)
|
31
|
+
self._emit_quantum_function_call(
|
32
|
+
iteration_function, [Expression(expr=str(i))]
|
33
|
+
)
|
@@ -0,0 +1,28 @@
|
|
1
|
+
from classiq.interface.model.handle_binding import HandleBinding
|
2
|
+
from classiq.interface.model.variable_declaration_statement import (
|
3
|
+
VariableDeclarationStatement,
|
4
|
+
)
|
5
|
+
|
6
|
+
from classiq.model_expansions.evaluators.parameter_types import (
|
7
|
+
evaluate_type_in_quantum_symbol,
|
8
|
+
)
|
9
|
+
from classiq.model_expansions.quantum_operations.emitter import Emitter
|
10
|
+
from classiq.model_expansions.scope import Evaluated, QuantumSymbol
|
11
|
+
|
12
|
+
|
13
|
+
class VariableDeclarationStatementEmitter(Emitter[VariableDeclarationStatement]):
|
14
|
+
def emit(self, variable_declaration: VariableDeclarationStatement, /) -> None:
|
15
|
+
var_decl = variable_declaration.copy()
|
16
|
+
var_decl.quantum_type = variable_declaration.quantum_type.copy()
|
17
|
+
self._current_scope[variable_declaration.name] = Evaluated(
|
18
|
+
value=QuantumSymbol(
|
19
|
+
handle=HandleBinding(name=var_decl.name),
|
20
|
+
quantum_type=evaluate_type_in_quantum_symbol(
|
21
|
+
var_decl.quantum_type,
|
22
|
+
self._current_scope,
|
23
|
+
var_decl.name,
|
24
|
+
),
|
25
|
+
),
|
26
|
+
defining_function=self._builder.current_function,
|
27
|
+
)
|
28
|
+
self._builder.emit_statement(var_decl)
|
@@ -0,0 +1,46 @@
|
|
1
|
+
from classiq.interface.generator.functions.builtins.internal_operators import (
|
2
|
+
COMPUTE_OPERATOR_NAME,
|
3
|
+
WITHIN_APPLY_NAME,
|
4
|
+
)
|
5
|
+
from classiq.interface.model.within_apply_operation import WithinApply
|
6
|
+
|
7
|
+
from classiq.model_expansions.closure import Closure
|
8
|
+
from classiq.model_expansions.quantum_operations.emitter import Emitter
|
9
|
+
from classiq.model_expansions.scope import Scope
|
10
|
+
|
11
|
+
|
12
|
+
class WithinApplyEmitter(Emitter[WithinApply]):
|
13
|
+
def emit(self, within_apply: WithinApply, /) -> None:
|
14
|
+
if self._should_wrap(within_apply.compute):
|
15
|
+
self._emit_wrapped(within_apply)
|
16
|
+
return
|
17
|
+
|
18
|
+
self._emit_as_operation(within_apply)
|
19
|
+
|
20
|
+
def _emit_as_operation(self, within_apply: WithinApply) -> None:
|
21
|
+
within_apply_operation = Closure(
|
22
|
+
name=WITHIN_APPLY_NAME,
|
23
|
+
blocks=dict(within=within_apply.compute, apply=within_apply.action),
|
24
|
+
scope=Scope(parent=self._current_scope),
|
25
|
+
)
|
26
|
+
with self._propagated_var_stack.capture_variables(within_apply):
|
27
|
+
context = self._expand_operation(within_apply_operation)
|
28
|
+
self._builder.emit_statement(
|
29
|
+
WithinApply(
|
30
|
+
compute=context.statements("within"),
|
31
|
+
action=context.statements("apply"),
|
32
|
+
source_ref=within_apply.source_ref,
|
33
|
+
)
|
34
|
+
)
|
35
|
+
|
36
|
+
def _emit_wrapped(self, within_apply: WithinApply) -> None:
|
37
|
+
with self._propagated_var_stack.capture_variables(within_apply):
|
38
|
+
wrapped_compute = self._create_expanded_wrapping_function(
|
39
|
+
COMPUTE_OPERATOR_NAME, within_apply.compute
|
40
|
+
)
|
41
|
+
wrapped_within_apply = WithinApply(
|
42
|
+
compute=[wrapped_compute],
|
43
|
+
action=within_apply.action,
|
44
|
+
source_ref=within_apply.source_ref,
|
45
|
+
)
|
46
|
+
self._builder.emit_statement(wrapped_within_apply)
|
@@ -0,0 +1,226 @@
|
|
1
|
+
import itertools
|
2
|
+
from collections import UserDict
|
3
|
+
from dataclasses import dataclass
|
4
|
+
from functools import singledispatch
|
5
|
+
from typing import TYPE_CHECKING, Any, Dict, Iterator, Optional, Type, TypeVar, Union
|
6
|
+
|
7
|
+
from typing_extensions import Self
|
8
|
+
|
9
|
+
from classiq.interface.exceptions import (
|
10
|
+
ClassiqExpansionError,
|
11
|
+
ClassiqInternalExpansionError,
|
12
|
+
)
|
13
|
+
from classiq.interface.generator.expressions.evaluated_expression import (
|
14
|
+
EvaluatedExpression,
|
15
|
+
)
|
16
|
+
from classiq.interface.generator.expressions.expression import Expression
|
17
|
+
from classiq.interface.generator.expressions.qmod_qarray_proxy import (
|
18
|
+
ILLEGAL_SLICE_BOUNDS_MSG,
|
19
|
+
)
|
20
|
+
from classiq.interface.generator.expressions.qmod_struct_instance import (
|
21
|
+
QmodStructInstance,
|
22
|
+
)
|
23
|
+
from classiq.interface.generator.functions.type_name import TypeName
|
24
|
+
from classiq.interface.model.handle_binding import (
|
25
|
+
FieldHandleBinding,
|
26
|
+
HandleBinding,
|
27
|
+
SlicedHandleBinding,
|
28
|
+
SubscriptHandleBinding,
|
29
|
+
)
|
30
|
+
from classiq.interface.model.quantum_function_call import ArgValue
|
31
|
+
from classiq.interface.model.quantum_type import (
|
32
|
+
QuantumBitvector,
|
33
|
+
QuantumType,
|
34
|
+
)
|
35
|
+
|
36
|
+
if TYPE_CHECKING:
|
37
|
+
from classiq.model_expansions.closure import Closure
|
38
|
+
|
39
|
+
T = TypeVar("T")
|
40
|
+
|
41
|
+
|
42
|
+
@dataclass(frozen=True)
|
43
|
+
class QuantumSymbol:
|
44
|
+
handle: HandleBinding
|
45
|
+
quantum_type: QuantumType
|
46
|
+
|
47
|
+
@property
|
48
|
+
def is_subscript(self) -> bool:
|
49
|
+
return isinstance(self.handle, (SubscriptHandleBinding, SlicedHandleBinding))
|
50
|
+
|
51
|
+
def emit(self) -> HandleBinding:
|
52
|
+
return self.handle
|
53
|
+
|
54
|
+
def __getitem__(self, item: Union[slice, int]) -> "QuantumSymbol":
|
55
|
+
if isinstance(item, int):
|
56
|
+
return self._subscript(item)
|
57
|
+
|
58
|
+
return self._slice(item.start, item.stop)
|
59
|
+
|
60
|
+
def _slice(self, start: int, end: int) -> "QuantumSymbol":
|
61
|
+
if not isinstance(self.quantum_type, QuantumBitvector):
|
62
|
+
raise ClassiqExpansionError(
|
63
|
+
f"{self.quantum_type.type_name} is not subscriptable."
|
64
|
+
)
|
65
|
+
if start >= end:
|
66
|
+
raise ClassiqExpansionError(ILLEGAL_SLICE_BOUNDS_MSG.format(start, end))
|
67
|
+
if (
|
68
|
+
self.quantum_type.has_length
|
69
|
+
and end - start > self.quantum_type.length_value
|
70
|
+
):
|
71
|
+
raise ClassiqExpansionError(
|
72
|
+
f"Slice [{start}:{end}] is out of bounds for {self.handle}."
|
73
|
+
)
|
74
|
+
return QuantumSymbol(
|
75
|
+
handle=SlicedHandleBinding(
|
76
|
+
base_handle=self.handle,
|
77
|
+
start=Expression(expr=str(start)),
|
78
|
+
end=Expression(expr=str(end)),
|
79
|
+
),
|
80
|
+
quantum_type=QuantumBitvector(
|
81
|
+
element_type=self.quantum_type.element_type,
|
82
|
+
length=Expression(expr=str(end - start)),
|
83
|
+
),
|
84
|
+
)
|
85
|
+
|
86
|
+
def _subscript(self, index: int) -> "QuantumSymbol":
|
87
|
+
if not isinstance(self.quantum_type, QuantumBitvector):
|
88
|
+
raise ClassiqExpansionError(
|
89
|
+
f"{self.quantum_type.type_name} is not subscriptable."
|
90
|
+
)
|
91
|
+
if (
|
92
|
+
index < 0
|
93
|
+
or self.quantum_type.has_length
|
94
|
+
and index > self.quantum_type.length_value
|
95
|
+
):
|
96
|
+
raise ClassiqExpansionError(
|
97
|
+
f"Subscript [{index}] is out of bounds for {self.handle}."
|
98
|
+
)
|
99
|
+
return QuantumSymbol(
|
100
|
+
handle=SubscriptHandleBinding(
|
101
|
+
base_handle=self.handle,
|
102
|
+
index=Expression(expr=str(index)),
|
103
|
+
),
|
104
|
+
quantum_type=self.quantum_type.element_type,
|
105
|
+
)
|
106
|
+
|
107
|
+
@property
|
108
|
+
def fields(self) -> Dict[str, "QuantumSymbol"]:
|
109
|
+
quantum_type = self.quantum_type
|
110
|
+
if not isinstance(quantum_type, TypeName):
|
111
|
+
raise ClassiqExpansionError(
|
112
|
+
f"{self.quantum_type.type_name} is not a struct"
|
113
|
+
)
|
114
|
+
return {
|
115
|
+
field_name: QuantumSymbol(
|
116
|
+
handle=FieldHandleBinding(base_handle=self.handle, field=field_name),
|
117
|
+
quantum_type=field_type,
|
118
|
+
)
|
119
|
+
for field_name, field_type in quantum_type.fields.items()
|
120
|
+
}
|
121
|
+
|
122
|
+
|
123
|
+
@singledispatch
|
124
|
+
def _evaluated_to_str(value: Any) -> str:
|
125
|
+
return str(value)
|
126
|
+
|
127
|
+
|
128
|
+
@_evaluated_to_str.register
|
129
|
+
def _evaluated_to_str_list(value: list) -> str:
|
130
|
+
return f"[{', '.join(_evaluated_to_str(x) for x in value)}]"
|
131
|
+
|
132
|
+
|
133
|
+
@_evaluated_to_str.register
|
134
|
+
def _evaluated_to_str_struct_literal(value: QmodStructInstance) -> str:
|
135
|
+
return f"struct_literal({value.struct_declaration.name}, {', '.join(f'{k}={_evaluated_to_str(v)}' for k, v in value.fields.items())})"
|
136
|
+
|
137
|
+
|
138
|
+
@dataclass(frozen=True)
|
139
|
+
class Evaluated: # FIXME: Merge with EvaluatedExpression if possible
|
140
|
+
value: Any
|
141
|
+
defining_function: Optional["Closure"] = None
|
142
|
+
|
143
|
+
def as_type(self, t: Type[T]) -> T:
|
144
|
+
if t is int:
|
145
|
+
return self._as_int() # type: ignore[return-value]
|
146
|
+
|
147
|
+
if not isinstance(self.value, t):
|
148
|
+
raise ClassiqExpansionError(
|
149
|
+
f"Invalid access to expression {self.value!r} as {t}"
|
150
|
+
)
|
151
|
+
|
152
|
+
return self.value
|
153
|
+
|
154
|
+
def _as_int(self) -> int:
|
155
|
+
if not isinstance(self.value, (int, float)):
|
156
|
+
raise ClassiqExpansionError(
|
157
|
+
f"Invalid access to expression {self.value!r} as {int}"
|
158
|
+
)
|
159
|
+
|
160
|
+
return int(self.value)
|
161
|
+
|
162
|
+
def emit(self) -> ArgValue:
|
163
|
+
if isinstance(self.value, QuantumSymbol):
|
164
|
+
return self.value.emit()
|
165
|
+
|
166
|
+
ret = Expression(expr=_evaluated_to_str(self.value))
|
167
|
+
ret._evaluated_expr = EvaluatedExpression(value=self.value)
|
168
|
+
return ret
|
169
|
+
|
170
|
+
|
171
|
+
if TYPE_CHECKING:
|
172
|
+
EvaluatedUserDict = UserDict[str, Evaluated]
|
173
|
+
else:
|
174
|
+
EvaluatedUserDict = UserDict
|
175
|
+
|
176
|
+
|
177
|
+
class Scope(EvaluatedUserDict):
|
178
|
+
def __init__(
|
179
|
+
self,
|
180
|
+
data: Optional[Dict[str, Evaluated]] = None,
|
181
|
+
/,
|
182
|
+
*,
|
183
|
+
parent: Optional[Self] = None,
|
184
|
+
) -> None:
|
185
|
+
super().__init__(data or {})
|
186
|
+
self._parent: Optional[Self] = parent
|
187
|
+
|
188
|
+
@property
|
189
|
+
def parent(self) -> Optional[Self]:
|
190
|
+
return self._parent
|
191
|
+
|
192
|
+
def __getitem__(self, name: str) -> Evaluated:
|
193
|
+
if name in self.data:
|
194
|
+
return self.data[name]
|
195
|
+
if self._parent is not None:
|
196
|
+
return self._parent[name]
|
197
|
+
raise ClassiqExpansionError(f"Variable {name!r} is undefined")
|
198
|
+
|
199
|
+
def __contains__(self, item: Any) -> bool:
|
200
|
+
return item in self.data or (self._parent is not None and item in self._parent)
|
201
|
+
|
202
|
+
def __iter__(self) -> Iterator[str]:
|
203
|
+
if self._parent is None:
|
204
|
+
return iter(self.data)
|
205
|
+
return iter(itertools.chain(self.data, self._parent))
|
206
|
+
|
207
|
+
def iter_without_top_level(self) -> Iterator[str]:
|
208
|
+
if self.parent is None:
|
209
|
+
return iter(tuple())
|
210
|
+
return iter(itertools.chain(self.data, self.parent.iter_without_top_level()))
|
211
|
+
|
212
|
+
def __or__(self, other: Any) -> "Scope": # type: ignore[override]
|
213
|
+
if not (isinstance(other, Scope) and isinstance(self, Scope)):
|
214
|
+
raise ClassiqInternalExpansionError
|
215
|
+
|
216
|
+
if self.parent is None:
|
217
|
+
parent = other.parent
|
218
|
+
elif other.parent is None:
|
219
|
+
parent = self.parent
|
220
|
+
else:
|
221
|
+
parent = self.parent | other.parent
|
222
|
+
|
223
|
+
return Scope(
|
224
|
+
(self.data or {}) | (other.data or {}),
|
225
|
+
parent=parent,
|
226
|
+
)
|
@@ -0,0 +1,136 @@
|
|
1
|
+
from typing import List, Sequence
|
2
|
+
|
3
|
+
from classiq.interface.generator.constant import Constant
|
4
|
+
from classiq.interface.generator.expressions.expression_constants import (
|
5
|
+
CPARAM_EXECUTION_SUFFIX,
|
6
|
+
)
|
7
|
+
from classiq.interface.model.classical_parameter_declaration import (
|
8
|
+
ClassicalParameterDeclaration,
|
9
|
+
)
|
10
|
+
from classiq.interface.model.handle_binding import HandleBinding
|
11
|
+
from classiq.interface.model.model import MAIN_FUNCTION_NAME, Model
|
12
|
+
from classiq.interface.model.native_function_definition import NativeFunctionDefinition
|
13
|
+
from classiq.interface.model.port_declaration import PortDeclaration
|
14
|
+
from classiq.interface.model.quantum_function_declaration import PositionalArg
|
15
|
+
|
16
|
+
from classiq.model_expansions.closure import FunctionClosure, GenerativeFunctionClosure
|
17
|
+
from classiq.model_expansions.evaluators.classical_expression import (
|
18
|
+
evaluate_classical_expression,
|
19
|
+
)
|
20
|
+
from classiq.model_expansions.evaluators.parameter_types import (
|
21
|
+
evaluate_type_in_quantum_symbol,
|
22
|
+
)
|
23
|
+
from classiq.model_expansions.expression_renamer import ExpressionRenamer
|
24
|
+
from classiq.model_expansions.scope import Evaluated, QuantumSymbol, Scope
|
25
|
+
from classiq.qmod.builtins.functions import (
|
26
|
+
CORE_LIB_DECLS,
|
27
|
+
OPEN_LIB_DECLS,
|
28
|
+
STD_QMOD_OPERATORS,
|
29
|
+
)
|
30
|
+
from classiq.qmod.quantum_function import GenerativeQFunc
|
31
|
+
|
32
|
+
|
33
|
+
def get_main_renamer(
|
34
|
+
functions: Sequence[NativeFunctionDefinition],
|
35
|
+
) -> ExpressionRenamer:
|
36
|
+
for func in functions:
|
37
|
+
if func.name == MAIN_FUNCTION_NAME:
|
38
|
+
return ExpressionRenamer.from_positional_arg_declarations(
|
39
|
+
func.positional_arg_declarations, CPARAM_EXECUTION_SUFFIX
|
40
|
+
)
|
41
|
+
return ExpressionRenamer(var_mapping={})
|
42
|
+
|
43
|
+
|
44
|
+
def _add_constants_to_scope(constants: List[Constant], scope: Scope) -> None:
|
45
|
+
for constant in constants:
|
46
|
+
scope[constant.name] = Evaluated(
|
47
|
+
value=evaluate_classical_expression(constant.value, scope).value
|
48
|
+
)
|
49
|
+
|
50
|
+
|
51
|
+
def _add_functions_to_scope(
|
52
|
+
functions: List[NativeFunctionDefinition], scope: Scope
|
53
|
+
) -> None:
|
54
|
+
for function in functions:
|
55
|
+
scope[function.name] = Evaluated(
|
56
|
+
value=FunctionClosure.create(
|
57
|
+
name=function.name,
|
58
|
+
positional_arg_declarations=function.positional_arg_declarations,
|
59
|
+
body=function.body,
|
60
|
+
scope=Scope(parent=scope),
|
61
|
+
)
|
62
|
+
)
|
63
|
+
|
64
|
+
|
65
|
+
def _add_generative_functions_to_scope(
|
66
|
+
functions: List[GenerativeQFunc], scope: Scope
|
67
|
+
) -> None:
|
68
|
+
for function in functions:
|
69
|
+
scope[function.func_decl.name] = Evaluated(
|
70
|
+
value=GenerativeFunctionClosure.create(
|
71
|
+
name=function.func_decl.name,
|
72
|
+
positional_arg_declarations=function.func_decl.positional_arg_declarations,
|
73
|
+
scope=Scope(parent=scope),
|
74
|
+
generative_function=function,
|
75
|
+
)
|
76
|
+
)
|
77
|
+
|
78
|
+
|
79
|
+
def _init_builtins_scope(scope: Scope) -> None:
|
80
|
+
for builtin_function in CORE_LIB_DECLS:
|
81
|
+
scope[builtin_function.name] = Evaluated(
|
82
|
+
value=FunctionClosure.create(
|
83
|
+
name=builtin_function.name,
|
84
|
+
positional_arg_declarations=builtin_function.positional_arg_declarations,
|
85
|
+
scope=Scope(parent=scope),
|
86
|
+
is_atomic=True,
|
87
|
+
)
|
88
|
+
)
|
89
|
+
for builtin_function in STD_QMOD_OPERATORS:
|
90
|
+
scope[builtin_function.name] = Evaluated(
|
91
|
+
value=FunctionClosure.create(
|
92
|
+
name=builtin_function.name,
|
93
|
+
positional_arg_declarations=builtin_function.positional_arg_declarations,
|
94
|
+
scope=Scope(parent=scope),
|
95
|
+
)
|
96
|
+
)
|
97
|
+
for func_decl in OPEN_LIB_DECLS:
|
98
|
+
if func_decl.name not in scope:
|
99
|
+
scope[func_decl.name] = Evaluated(
|
100
|
+
value=FunctionClosure.create(
|
101
|
+
name=func_decl.name,
|
102
|
+
positional_arg_declarations=func_decl.positional_arg_declarations,
|
103
|
+
scope=Scope(parent=scope),
|
104
|
+
is_atomic=True,
|
105
|
+
)
|
106
|
+
)
|
107
|
+
|
108
|
+
|
109
|
+
def add_entry_point_params_to_scope(
|
110
|
+
parameters: Sequence[PositionalArg], main_closure: FunctionClosure
|
111
|
+
) -> None:
|
112
|
+
for parameter in parameters:
|
113
|
+
if isinstance(parameter, PortDeclaration):
|
114
|
+
main_closure.scope[parameter.name] = Evaluated(
|
115
|
+
value=QuantumSymbol(
|
116
|
+
handle=HandleBinding(name=parameter.name),
|
117
|
+
quantum_type=evaluate_type_in_quantum_symbol(
|
118
|
+
parameter.quantum_type, main_closure.scope, parameter.name
|
119
|
+
),
|
120
|
+
),
|
121
|
+
defining_function=main_closure,
|
122
|
+
)
|
123
|
+
elif isinstance(parameter, ClassicalParameterDeclaration):
|
124
|
+
main_closure.scope[parameter.name] = Evaluated(
|
125
|
+
value=parameter.classical_type.as_symbolic(parameter.name),
|
126
|
+
defining_function=main_closure,
|
127
|
+
)
|
128
|
+
|
129
|
+
|
130
|
+
def init_top_level_scope(
|
131
|
+
model: Model, generative_functions: List[GenerativeQFunc], scope: Scope
|
132
|
+
) -> None:
|
133
|
+
_add_functions_to_scope(model.functions, scope)
|
134
|
+
_add_generative_functions_to_scope(generative_functions, scope)
|
135
|
+
_add_constants_to_scope(model.constants, scope)
|
136
|
+
_init_builtins_scope(scope)
|
File without changes
|