classiq 0.45.0__py3-none-any.whl → 0.46.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.
Files changed (146) hide show
  1. classiq/__init__.py +0 -1
  2. classiq/_internals/__init__.py +20 -0
  3. classiq/_internals/authentication/authentication.py +11 -0
  4. classiq/analyzer/analyzer.py +12 -10
  5. classiq/applications/combinatorial_helpers/combinatorial_problem_utils.py +1 -1
  6. classiq/applications/combinatorial_helpers/pauli_helpers/pauli_utils.py +1 -1
  7. classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +1 -1
  8. classiq/applications/libraries/qmci_library.py +4 -9
  9. classiq/execution/execution_session.py +68 -7
  10. classiq/executor.py +14 -2
  11. classiq/interface/_version.py +1 -1
  12. classiq/interface/backend/backend_preferences.py +189 -0
  13. classiq/interface/backend/quantum_backend_providers.py +38 -0
  14. classiq/interface/debug_info/debug_info.py +22 -2
  15. classiq/interface/exceptions.py +16 -1
  16. classiq/interface/executor/execution_preferences.py +18 -0
  17. classiq/interface/generator/application_apis/chemistry_declarations.py +1 -177
  18. classiq/interface/generator/application_apis/combinatorial_optimization_declarations.py +0 -12
  19. classiq/interface/generator/application_apis/finance_declarations.py +8 -43
  20. classiq/interface/generator/application_apis/qsvm_declarations.py +0 -78
  21. classiq/interface/generator/builtin_api_builder.py +0 -3
  22. classiq/interface/generator/functions/__init__.py +0 -2
  23. classiq/interface/generator/functions/builtins/__init__.py +0 -15
  24. classiq/interface/generator/generated_circuit_data.py +2 -0
  25. classiq/interface/generator/hardware/hardware_data.py +37 -0
  26. classiq/interface/generator/model/constraints.py +18 -1
  27. classiq/interface/generator/model/preferences/preferences.py +53 -1
  28. classiq/interface/generator/model/quantum_register.py +1 -1
  29. classiq/interface/generator/quantum_program.py +10 -2
  30. classiq/interface/generator/transpiler_basis_gates.py +4 -0
  31. classiq/interface/generator/types/builtin_enum_declarations.py +136 -21
  32. classiq/interface/generator/types/enum_declaration.py +1 -3
  33. classiq/interface/generator/types/struct_declaration.py +1 -3
  34. classiq/interface/hardware.py +5 -0
  35. classiq/interface/ide/visual_model.py +1 -1
  36. classiq/interface/model/classical_parameter_declaration.py +6 -0
  37. classiq/interface/model/inplace_binary_operation.py +0 -14
  38. classiq/interface/model/model.py +1 -18
  39. classiq/interface/model/port_declaration.py +4 -2
  40. classiq/interface/model/quantum_function_declaration.py +19 -6
  41. classiq/interface/model/quantum_lambda_function.py +11 -1
  42. classiq/interface/model/quantum_variable_declaration.py +1 -1
  43. classiq/model_expansions/__init__.py +0 -0
  44. classiq/model_expansions/atomic_expression_functions_defs.py +250 -0
  45. classiq/model_expansions/capturing/__init__.py +0 -0
  46. classiq/model_expansions/capturing/captured_var_manager.py +50 -0
  47. classiq/model_expansions/capturing/mangling_utils.py +17 -0
  48. classiq/model_expansions/capturing/propagated_var_stack.py +180 -0
  49. classiq/model_expansions/closure.py +160 -0
  50. classiq/model_expansions/debug_flag.py +3 -0
  51. classiq/model_expansions/evaluators/__init__.py +0 -0
  52. classiq/model_expansions/evaluators/arg_type_match.py +160 -0
  53. classiq/model_expansions/evaluators/argument_types.py +42 -0
  54. classiq/model_expansions/evaluators/classical_expression.py +36 -0
  55. classiq/model_expansions/evaluators/control.py +144 -0
  56. classiq/model_expansions/evaluators/parameter_types.py +227 -0
  57. classiq/model_expansions/evaluators/quantum_type_utils.py +235 -0
  58. classiq/model_expansions/evaluators/type_type_match.py +90 -0
  59. classiq/model_expansions/expression_evaluator.py +125 -0
  60. classiq/model_expansions/expression_renamer.py +76 -0
  61. classiq/model_expansions/function_builder.py +192 -0
  62. classiq/model_expansions/generative_functions.py +101 -0
  63. classiq/model_expansions/interpreter.py +365 -0
  64. classiq/model_expansions/model_tables.py +105 -0
  65. classiq/model_expansions/quantum_operations/__init__.py +19 -0
  66. classiq/model_expansions/quantum_operations/bind.py +64 -0
  67. classiq/model_expansions/quantum_operations/classicalif.py +39 -0
  68. classiq/model_expansions/quantum_operations/control.py +235 -0
  69. classiq/model_expansions/quantum_operations/emitter.py +215 -0
  70. classiq/model_expansions/quantum_operations/expression_operation.py +218 -0
  71. classiq/model_expansions/quantum_operations/inplace_binary_operation.py +250 -0
  72. classiq/model_expansions/quantum_operations/invert.py +38 -0
  73. classiq/model_expansions/quantum_operations/power.py +74 -0
  74. classiq/model_expansions/quantum_operations/quantum_assignment_operation.py +174 -0
  75. classiq/model_expansions/quantum_operations/quantum_function_call.py +15 -0
  76. classiq/model_expansions/quantum_operations/repeat.py +33 -0
  77. classiq/model_expansions/quantum_operations/variable_decleration.py +28 -0
  78. classiq/model_expansions/quantum_operations/within_apply.py +46 -0
  79. classiq/model_expansions/scope.py +226 -0
  80. classiq/model_expansions/scope_initialization.py +136 -0
  81. classiq/model_expansions/sympy_conversion/__init__.py +0 -0
  82. classiq/model_expansions/sympy_conversion/arithmetics.py +49 -0
  83. classiq/model_expansions/sympy_conversion/expression_to_sympy.py +150 -0
  84. classiq/model_expansions/sympy_conversion/sympy_to_python.py +113 -0
  85. classiq/model_expansions/utils/__init__.py +0 -0
  86. classiq/model_expansions/utils/counted_name_allocator.py +11 -0
  87. classiq/model_expansions/visitors/__init__.py +0 -0
  88. classiq/model_expansions/visitors/boolean_expression_transformers.py +214 -0
  89. classiq/model_expansions/visitors/variable_references.py +115 -0
  90. classiq/qmod/__init__.py +1 -3
  91. classiq/qmod/builtins/enums.py +33 -2
  92. classiq/qmod/builtins/functions/__init__.py +251 -0
  93. classiq/qmod/builtins/functions/amplitude_estimation.py +26 -0
  94. classiq/qmod/builtins/functions/arithmetic.py +68 -0
  95. classiq/qmod/builtins/functions/benchmarking.py +8 -0
  96. classiq/qmod/builtins/functions/chemistry.py +91 -0
  97. classiq/qmod/builtins/functions/discrete_sine_cosine_transform.py +105 -0
  98. classiq/qmod/builtins/functions/exponentiation.py +110 -0
  99. classiq/qmod/builtins/functions/finance.py +34 -0
  100. classiq/qmod/builtins/functions/grover.py +179 -0
  101. classiq/qmod/builtins/functions/hea.py +59 -0
  102. classiq/qmod/builtins/functions/linear_pauli_rotation.py +65 -0
  103. classiq/qmod/builtins/functions/modular_exponentiation.py +137 -0
  104. classiq/qmod/builtins/functions/operators.py +22 -0
  105. classiq/qmod/builtins/functions/qaoa_penalty.py +116 -0
  106. classiq/qmod/builtins/functions/qft.py +23 -0
  107. classiq/qmod/builtins/functions/qpe.py +39 -0
  108. classiq/qmod/builtins/functions/qsvm.py +24 -0
  109. classiq/qmod/builtins/functions/qsvt.py +136 -0
  110. classiq/qmod/builtins/functions/standard_gates.py +739 -0
  111. classiq/qmod/builtins/functions/state_preparation.py +356 -0
  112. classiq/qmod/builtins/functions/swap_test.py +25 -0
  113. classiq/qmod/builtins/structs.py +50 -28
  114. classiq/qmod/cparam.py +64 -0
  115. classiq/qmod/create_model_function.py +190 -0
  116. classiq/qmod/declaration_inferrer.py +52 -81
  117. classiq/qmod/expression_query.py +16 -0
  118. classiq/qmod/generative.py +48 -0
  119. classiq/qmod/model_state_container.py +1 -2
  120. classiq/qmod/native/pretty_printer.py +7 -11
  121. classiq/qmod/pretty_print/pretty_printer.py +7 -11
  122. classiq/qmod/python_classical_type.py +67 -0
  123. classiq/qmod/qfunc.py +19 -4
  124. classiq/qmod/qmod_parameter.py +15 -64
  125. classiq/qmod/qmod_variable.py +28 -46
  126. classiq/qmod/quantum_callable.py +1 -1
  127. classiq/qmod/quantum_expandable.py +10 -4
  128. classiq/qmod/quantum_function.py +22 -40
  129. classiq/qmod/semantics/error_manager.py +22 -10
  130. classiq/qmod/semantics/static_semantics_visitor.py +10 -12
  131. classiq/qmod/semantics/validation/types_validation.py +6 -7
  132. classiq/qmod/utilities.py +2 -2
  133. classiq/qmod/write_qmod.py +14 -0
  134. classiq/show.py +10 -0
  135. classiq/synthesis.py +46 -2
  136. {classiq-0.45.0.dist-info → classiq-0.46.0.dist-info}/METADATA +1 -1
  137. {classiq-0.45.0.dist-info → classiq-0.46.0.dist-info}/RECORD +138 -74
  138. classiq/interface/generator/functions/builtins/core_library/__init__.py +0 -16
  139. classiq/interface/generator/functions/builtins/core_library/atomic_quantum_functions.py +0 -710
  140. classiq/interface/generator/functions/builtins/core_library/exponentiation_functions.py +0 -105
  141. classiq/interface/generator/functions/builtins/open_lib_functions.py +0 -2489
  142. classiq/interface/generator/functions/builtins/quantum_operators.py +0 -24
  143. classiq/interface/generator/types/builtin_struct_declarations/__init__.py +0 -1
  144. classiq/interface/generator/types/builtin_struct_declarations/pauli_struct_declarations.py +0 -21
  145. classiq/qmod/builtins/functions.py +0 -1029
  146. {classiq-0.45.0.dist-info → classiq-0.46.0.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