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.
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 +27 -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 +111 -0
  99. classiq/qmod/builtins/functions/finance.py +34 -0
  100. classiq/qmod/builtins/functions/grover.py +178 -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 +357 -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 +27 -45
  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.1.dist-info → classiq-0.46.1.dist-info}/METADATA +1 -1
  137. {classiq-0.45.1.dist-info → classiq-0.46.1.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.1.dist-info → classiq-0.46.1.dist-info}/WHEEL +0 -0
@@ -0,0 +1,218 @@
1
+ import ast
2
+ from abc import abstractmethod
3
+ from itertools import chain
4
+ from typing import TYPE_CHECKING, Dict, List, Tuple, TypeVar
5
+
6
+ import sympy
7
+
8
+ from classiq.interface.exceptions import (
9
+ ClassiqExpansionError,
10
+ ClassiqInternalExpansionError,
11
+ )
12
+ from classiq.interface.generator.expressions.evaluated_expression import (
13
+ EvaluatedExpression,
14
+ )
15
+ from classiq.interface.generator.expressions.expression import Expression
16
+ from classiq.interface.generator.functions.type_name import TypeName
17
+ from classiq.interface.model.bind_operation import BindOperation
18
+ from classiq.interface.model.handle_binding import (
19
+ FieldHandleBinding,
20
+ HandleBinding,
21
+ SlicedHandleBinding,
22
+ SubscriptHandleBinding,
23
+ )
24
+ from classiq.interface.model.quantum_expressions.quantum_expression import (
25
+ QuantumExpressionOperation,
26
+ )
27
+ from classiq.interface.model.quantum_type import (
28
+ QuantumBit,
29
+ QuantumBitvector,
30
+ QuantumNumeric,
31
+ )
32
+ from classiq.interface.model.variable_declaration_statement import (
33
+ VariableDeclarationStatement,
34
+ )
35
+ from classiq.interface.model.within_apply_operation import WithinApply
36
+
37
+ from classiq.model_expansions.quantum_operations.emitter import Emitter
38
+ from classiq.model_expansions.scope import QuantumSymbol
39
+ from classiq.model_expansions.sympy_conversion.sympy_to_python import (
40
+ translate_sympy_quantum_expression,
41
+ )
42
+ from classiq.model_expansions.visitors.variable_references import VarRefCollector
43
+
44
+ ExpressionOperationT = TypeVar("ExpressionOperationT", bound=QuantumExpressionOperation)
45
+
46
+
47
+ class ExpressionOperationEmitter(Emitter[ExpressionOperationT]):
48
+ @abstractmethod
49
+ def emit(self, op: ExpressionOperationT, /) -> None:
50
+ pass
51
+
52
+ def _emit_with_split(
53
+ self,
54
+ op: ExpressionOperationT,
55
+ expression: Expression,
56
+ symbols_to_split: List[QuantumSymbol],
57
+ ) -> None:
58
+ symbols_parts, bind_ops = self._get_bind_ops(symbols_to_split)
59
+
60
+ for symbol_parts in symbols_parts:
61
+ for symbol in symbol_parts:
62
+ if symbol.handle.identifier not in self._current_scope:
63
+ self._interpreter.emit_statement(
64
+ VariableDeclarationStatement(
65
+ name=symbol.handle.identifier,
66
+ quantum_type=symbol.quantum_type,
67
+ )
68
+ )
69
+
70
+ new_expression = self._update_op_expression(
71
+ {symbol.handle: symbol for symbol in chain.from_iterable(symbols_parts)},
72
+ expression,
73
+ )
74
+ new_op = op.copy(update=dict(expression=new_expression))
75
+
76
+ self._interpreter.emit_statement(
77
+ WithinApply(
78
+ compute=bind_ops,
79
+ action=[new_op],
80
+ source_ref=op.source_ref,
81
+ )
82
+ )
83
+
84
+ def _update_op_expression(
85
+ self,
86
+ symbol_parts: Dict[HandleBinding, QuantumSymbol],
87
+ expression: Expression,
88
+ ) -> Expression:
89
+ vrc = VarRefCollector(ignore_duplicated_handles=True)
90
+ vrc.visit(ast.parse(expression.expr))
91
+
92
+ new_expr_str = expression.expr
93
+ for handle in vrc.var_handles:
94
+ collapsed_handle = handle.collapse()
95
+ if collapsed_handle in symbol_parts:
96
+ new_expr_str = new_expr_str.replace(
97
+ str(handle), symbol_parts[collapsed_handle].handle.identifier
98
+ )
99
+ self._check_all_handles_were_replaced(new_expr_str)
100
+
101
+ new_expr = Expression(expr=new_expr_str)
102
+ new_expr._evaluated_expr = EvaluatedExpression(
103
+ value=self._interpreter.evaluate(new_expr).value
104
+ )
105
+ return new_expr
106
+
107
+ @staticmethod
108
+ def _check_all_handles_were_replaced(new_expr_str: str) -> None:
109
+ vrc = VarRefCollector(ignore_duplicated_handles=True)
110
+ vrc.visit(ast.parse(new_expr_str))
111
+ for handle in vrc.var_handles:
112
+ if isinstance(
113
+ handle,
114
+ (SubscriptHandleBinding, SlicedHandleBinding, FieldHandleBinding),
115
+ ):
116
+ raise ClassiqInternalExpansionError(f"Did not replace handle {handle}")
117
+
118
+ @staticmethod
119
+ def _get_bind_ops(
120
+ symbols_to_split: List[QuantumSymbol],
121
+ ) -> Tuple[List[List[QuantumSymbol]], List[BindOperation]]:
122
+ bind_ops = []
123
+ symbols_parts = []
124
+ for symbol in symbols_to_split:
125
+ symbol_parts = ExpressionOperationEmitter._get_symbol_parts(symbol)
126
+ symbols_parts.append(symbol_parts)
127
+ bind_ops.append(
128
+ BindOperation(
129
+ in_handles=[symbol.handle],
130
+ out_handles=[
131
+ HandleBinding(name=symbol_part.handle.identifier)
132
+ for symbol_part in symbol_parts
133
+ ],
134
+ )
135
+ )
136
+ return symbols_parts, bind_ops
137
+
138
+ @staticmethod
139
+ def _get_symbol_parts(symbol: QuantumSymbol) -> List[QuantumSymbol]:
140
+ quantum_type = symbol.quantum_type
141
+
142
+ if isinstance(quantum_type, (QuantumBit, QuantumNumeric)):
143
+ return [symbol]
144
+
145
+ if isinstance(quantum_type, QuantumBitvector):
146
+ if not quantum_type.has_length:
147
+ raise ClassiqExpansionError(
148
+ f"Could not determine the length of quantum array "
149
+ f"{symbol.handle}."
150
+ )
151
+ return list(
152
+ chain.from_iterable(
153
+ ExpressionOperationEmitter._get_symbol_parts(symbol[idx])
154
+ for idx in range(quantum_type.length_value)
155
+ )
156
+ )
157
+
158
+ if TYPE_CHECKING:
159
+ assert isinstance(quantum_type, TypeName)
160
+
161
+ return list(
162
+ chain.from_iterable(
163
+ ExpressionOperationEmitter._get_symbol_parts(field_symbol)
164
+ for field_symbol in symbol.fields.values()
165
+ )
166
+ )
167
+
168
+ def _get_symbols_to_split(self, expression: Expression) -> List[QuantumSymbol]:
169
+ vrc = VarRefCollector(ignore_duplicated_handles=True)
170
+ vrc.visit(ast.parse(expression.expr))
171
+ symbol_names_to_split = dict.fromkeys(
172
+ handle.name
173
+ for handle in vrc.var_handles
174
+ if isinstance(handle, (SubscriptHandleBinding, FieldHandleBinding))
175
+ )
176
+ return [
177
+ symbol
178
+ for symbol_name in symbol_names_to_split
179
+ if isinstance(
180
+ symbol := self._current_scope[symbol_name].value,
181
+ QuantumSymbol,
182
+ )
183
+ ]
184
+
185
+ def _evaluate_op_expression(self, op: ExpressionOperationT) -> Expression:
186
+ return self._evaluate_expression(op.expression)
187
+
188
+ def _evaluate_expression(self, expression: Expression) -> Expression:
189
+ evaluated_expression = self._interpreter.evaluate(expression)
190
+ if isinstance(evaluated_expression.value, sympy.Basic):
191
+ new_expression = Expression(
192
+ expr=translate_sympy_quantum_expression(evaluated_expression.value)
193
+ )
194
+ else:
195
+ new_expression = Expression(expr=str(evaluated_expression.value))
196
+ new_expression._evaluated_expr = EvaluatedExpression(
197
+ value=evaluated_expression.value
198
+ )
199
+ return new_expression
200
+
201
+ def _evaluate_types_in_expression(
202
+ self, op: ExpressionOperationT, expression: Expression
203
+ ) -> ExpressionOperationT:
204
+ op_with_evaluated_types = op.copy(update={"expression": expression})
205
+ vrc = VarRefCollector()
206
+ vrc.visit(ast.parse(op_with_evaluated_types.expression.expr))
207
+ handles = list(vrc.var_handles)
208
+ op_with_evaluated_types.set_var_handles(handles)
209
+ op_with_evaluated_types.initialize_var_types(
210
+ {
211
+ handle.name: self._interpreter.evaluate(handle)
212
+ .as_type(QuantumSymbol)
213
+ .quantum_type
214
+ for handle in handles
215
+ },
216
+ self._machine_precision,
217
+ )
218
+ return op_with_evaluated_types
@@ -0,0 +1,250 @@
1
+ from typing import TYPE_CHECKING, List, Optional, Sequence, Tuple
2
+
3
+ from classiq.interface.exceptions import ClassiqInternalExpansionError
4
+ from classiq.interface.generator.expressions.expression import Expression
5
+ from classiq.interface.generator.functions.port_declaration import (
6
+ PortDeclarationDirection,
7
+ )
8
+ from classiq.interface.model.bind_operation import BindOperation
9
+ from classiq.interface.model.handle_binding import HandleBinding
10
+ from classiq.interface.model.inplace_binary_operation import (
11
+ BinaryOperation,
12
+ InplaceBinaryOperation,
13
+ )
14
+ from classiq.interface.model.port_declaration import PortDeclaration
15
+ from classiq.interface.model.quantum_function_call import QuantumFunctionCall
16
+ from classiq.interface.model.quantum_function_declaration import (
17
+ NamedParamsQuantumFunctionDeclaration,
18
+ )
19
+ from classiq.interface.model.quantum_statement import QuantumStatement
20
+ from classiq.interface.model.quantum_type import (
21
+ QuantumBit,
22
+ QuantumBitvector,
23
+ QuantumNumeric,
24
+ )
25
+ from classiq.interface.model.variable_declaration_statement import (
26
+ VariableDeclarationStatement,
27
+ )
28
+
29
+ from classiq.model_expansions.closure import FunctionClosure
30
+ from classiq.model_expansions.evaluators.parameter_types import (
31
+ evaluate_types_in_quantum_symbols,
32
+ )
33
+ from classiq.model_expansions.evaluators.quantum_type_utils import (
34
+ validate_inplace_binary_op_vars,
35
+ )
36
+ from classiq.model_expansions.quantum_operations.emitter import Emitter
37
+ from classiq.model_expansions.scope import QuantumSymbol, Scope
38
+ from classiq.qmod.builtins.functions import integer_xor, modular_add
39
+
40
+
41
+ def _binary_function_declaration(
42
+ op: BinaryOperation,
43
+ ) -> NamedParamsQuantumFunctionDeclaration:
44
+ return {
45
+ BinaryOperation.Addition: modular_add.func_decl,
46
+ BinaryOperation.Xor: integer_xor.func_decl,
47
+ }[op]
48
+
49
+
50
+ class InplaceBinaryOperationEmitter(Emitter[InplaceBinaryOperation]):
51
+ def emit(self, op: InplaceBinaryOperation, /) -> None:
52
+ value_var = self._interpreter.evaluate(op.value).as_type(QuantumSymbol)
53
+ target_var = self._interpreter.evaluate(op.target).as_type(QuantumSymbol)
54
+ value_var, target_var = evaluate_types_in_quantum_symbols(
55
+ [value_var, target_var], self._current_scope
56
+ )
57
+ validate_inplace_binary_op_vars(value_var, target_var, op.operation.value)
58
+ if TYPE_CHECKING:
59
+ assert isinstance(value_var.quantum_type, QuantumNumeric)
60
+ assert isinstance(target_var.quantum_type, QuantumNumeric)
61
+
62
+ sign_diff = int(value_var.quantum_type.sign_value) - int(
63
+ target_var.quantum_type.sign_value
64
+ )
65
+ frac_digits_diff = (
66
+ value_var.quantum_type.fraction_digits_value
67
+ - target_var.quantum_type.fraction_digits_value
68
+ )
69
+ if (
70
+ sign_diff + frac_digits_diff == value_var.quantum_type.size_in_bits
71
+ or -sign_diff - frac_digits_diff == target_var.quantum_type.size_in_bits
72
+ ):
73
+ with self._propagated_var_stack.capture_variables(op):
74
+ return
75
+
76
+ value_var = QuantumSymbol(
77
+ handle=HandleBinding(name="value"), quantum_type=value_var.quantum_type
78
+ )
79
+ target_var = QuantumSymbol(
80
+ handle=HandleBinding(name="target"),
81
+ quantum_type=target_var.quantum_type,
82
+ )
83
+ inplace_binary_op_function = FunctionClosure.create(
84
+ name=op.operation.value,
85
+ positional_arg_declarations=[
86
+ PortDeclaration(
87
+ name=value_var.handle.name,
88
+ quantum_type=value_var.quantum_type,
89
+ direction=PortDeclarationDirection.Inout,
90
+ ),
91
+ PortDeclaration(
92
+ name=target_var.handle.name,
93
+ quantum_type=target_var.quantum_type,
94
+ direction=PortDeclarationDirection.Inout,
95
+ ),
96
+ ],
97
+ body=_build_inplace_binary_operation(
98
+ value_var=value_var,
99
+ target_var=target_var,
100
+ frac_digits_diff=frac_digits_diff,
101
+ internal_function_declaration=_binary_function_declaration(
102
+ op.operation
103
+ ),
104
+ ),
105
+ scope=Scope(parent=self._current_scope),
106
+ )
107
+ with self._propagated_var_stack.capture_variables(op):
108
+ self._emit_quantum_function_call(
109
+ inplace_binary_op_function, [op.value, op.target]
110
+ )
111
+
112
+
113
+ def _build_inplace_binary_operation(
114
+ value_var: QuantumSymbol,
115
+ target_var: QuantumSymbol,
116
+ frac_digits_diff: int,
117
+ internal_function_declaration: NamedParamsQuantumFunctionDeclaration,
118
+ ) -> List[QuantumStatement]:
119
+ if TYPE_CHECKING:
120
+ assert isinstance(value_var.quantum_type, QuantumNumeric)
121
+ assert isinstance(target_var.quantum_type, QuantumNumeric)
122
+
123
+ value_overlap_var, value_sign_var, value_bind_targets = _get_inplace_bind_targets(
124
+ "value", value_var, frac_digits_diff
125
+ )
126
+ target_overlap_var, target_sign_var, target_bind_targets = (
127
+ _get_inplace_bind_targets("target", target_var, -frac_digits_diff)
128
+ )
129
+
130
+ value_pre_ops, value_post_ops = _get_inplace_pre_post_ops(
131
+ value_var, value_bind_targets
132
+ )
133
+ target_pre_ops, target_post_ops = _get_inplace_pre_post_ops(
134
+ target_var, target_bind_targets
135
+ )
136
+
137
+ binary_ops = []
138
+ if value_overlap_var is not None and target_overlap_var is not None:
139
+ binary_ops.append(
140
+ _internal_inplace_binary_operation_function_call(
141
+ internal_function_declaration,
142
+ value_overlap_var.handle,
143
+ target_overlap_var.handle,
144
+ )
145
+ )
146
+ if value_sign_var is not None and target_sign_var is not None:
147
+ binary_ops.append(
148
+ _internal_inplace_binary_operation_function_call(
149
+ internal_function_declaration,
150
+ value_sign_var.handle,
151
+ target_sign_var.handle,
152
+ )
153
+ )
154
+ if len(binary_ops) == 0:
155
+ raise ClassiqInternalExpansionError("Bug in unrolling inplace operation")
156
+
157
+ return [
158
+ *value_pre_ops,
159
+ *target_pre_ops,
160
+ *binary_ops,
161
+ *target_post_ops,
162
+ *value_post_ops,
163
+ ]
164
+
165
+
166
+ def _internal_inplace_binary_operation_function_call(
167
+ internal_function_declaration: NamedParamsQuantumFunctionDeclaration,
168
+ value_var: HandleBinding,
169
+ target_var: HandleBinding,
170
+ ) -> QuantumFunctionCall:
171
+ internal_function_call = QuantumFunctionCall(
172
+ function=internal_function_declaration.name,
173
+ positional_args=[value_var, target_var],
174
+ )
175
+ internal_function_call.set_func_decl(internal_function_declaration)
176
+ return internal_function_call
177
+
178
+
179
+ def _get_inplace_bind_targets(
180
+ kind: str, var: QuantumSymbol, frac_digits_diff: int
181
+ ) -> Tuple[Optional[QuantumSymbol], Optional[QuantumSymbol], List[QuantumSymbol]]:
182
+ quantum_type = var.quantum_type
183
+ if TYPE_CHECKING:
184
+ assert isinstance(quantum_type, QuantumNumeric)
185
+
186
+ if not quantum_type.sign_value and frac_digits_diff <= 0:
187
+ return var, None, []
188
+
189
+ significand_overlap = (
190
+ quantum_type.size_in_bits
191
+ - quantum_type.fraction_digits_value
192
+ - int(quantum_type.sign_value)
193
+ )
194
+ fraction_overlap = quantum_type.fraction_digits_value - max(0, frac_digits_diff)
195
+ if significand_overlap + fraction_overlap == 0 and quantum_type.size_in_bits == 1:
196
+ assert quantum_type.sign_value
197
+ return None, var, []
198
+
199
+ bind_targets = []
200
+
201
+ if frac_digits_diff > 0:
202
+ bind_targets.append(
203
+ QuantumSymbol(
204
+ handle=HandleBinding(name=f"trimmed_{kind}_fraction_digits"),
205
+ quantum_type=QuantumBitvector(
206
+ length=Expression(expr=str(frac_digits_diff)),
207
+ ),
208
+ )
209
+ )
210
+
211
+ overlap_var = None
212
+ if significand_overlap + fraction_overlap > 0:
213
+ overlap_var = QuantumSymbol(
214
+ handle=HandleBinding(name=f"{kind}_overlap"),
215
+ quantum_type=QuantumNumeric(
216
+ size=Expression(expr=str(significand_overlap + fraction_overlap)),
217
+ is_signed=Expression(expr="False"),
218
+ fraction_digits=Expression(expr=str(fraction_overlap)),
219
+ ),
220
+ )
221
+ bind_targets.append(overlap_var)
222
+
223
+ sign_var = None
224
+ if quantum_type.sign_value:
225
+ sign_var = QuantumSymbol(
226
+ handle=HandleBinding(name=f"trimmed_{kind}_sign"),
227
+ quantum_type=QuantumBit(),
228
+ )
229
+ bind_targets.append(sign_var)
230
+
231
+ return overlap_var, sign_var, bind_targets
232
+
233
+
234
+ def _get_inplace_pre_post_ops(
235
+ var: QuantumSymbol, bind_targets: List[QuantumSymbol]
236
+ ) -> Tuple[Sequence[QuantumStatement], Sequence[QuantumStatement]]:
237
+ if len(bind_targets) == 0:
238
+ return [], []
239
+
240
+ value_bind_op = BindOperation(
241
+ in_handles=[var.handle],
242
+ out_handles=[var.handle for var in bind_targets],
243
+ )
244
+ return [
245
+ VariableDeclarationStatement(
246
+ name=var.handle.name,
247
+ quantum_type=var.quantum_type,
248
+ )
249
+ for var in bind_targets
250
+ ] + [value_bind_op], [value_bind_op.reversed()]
@@ -0,0 +1,38 @@
1
+ from classiq.interface.generator.functions.builtins.internal_operators import (
2
+ INVERT_OPERATOR_NAME,
3
+ )
4
+ from classiq.interface.model.invert import Invert
5
+
6
+ from classiq.model_expansions.closure import Closure
7
+ from classiq.model_expansions.quantum_operations.emitter import Emitter
8
+ from classiq.model_expansions.scope import Scope
9
+
10
+
11
+ class InvertEmitter(Emitter[Invert]):
12
+ def emit(self, invert: Invert, /) -> None:
13
+ if self._should_wrap(invert.body):
14
+ self._emit_wrapped(invert)
15
+ return
16
+
17
+ self._emit_as_operation(invert)
18
+
19
+ def _emit_as_operation(self, invert: Invert) -> None:
20
+ invert_operation = Closure(
21
+ name=INVERT_OPERATOR_NAME,
22
+ blocks={"body": invert.body},
23
+ scope=Scope(parent=self._current_scope),
24
+ )
25
+ with self._propagated_var_stack.capture_variables(invert):
26
+ context = self._expand_operation(invert_operation)
27
+ self._builder.emit_statement(
28
+ Invert(body=context.statements("body"), source_ref=invert.source_ref)
29
+ )
30
+
31
+ def _emit_wrapped(self, invert: Invert) -> None:
32
+ with self._propagated_var_stack.capture_variables(invert):
33
+ wrapping_function = self._create_expanded_wrapping_function(
34
+ INVERT_OPERATOR_NAME, invert.body
35
+ )
36
+ self._builder.emit_statement(
37
+ Invert(body=[wrapping_function], source_ref=invert.source_ref)
38
+ )
@@ -0,0 +1,74 @@
1
+ from typing import Union
2
+
3
+ import sympy
4
+
5
+ from classiq.interface.exceptions import ClassiqExpansionError
6
+ from classiq.interface.generator.expressions.evaluated_expression import (
7
+ EvaluatedExpression,
8
+ )
9
+ from classiq.interface.generator.expressions.expression import Expression
10
+ from classiq.interface.generator.functions.builtins.internal_operators import (
11
+ POWER_OPERATOR_NAME,
12
+ )
13
+ from classiq.interface.model.power import Power
14
+
15
+ from classiq.model_expansions.closure import Closure
16
+ from classiq.model_expansions.quantum_operations.emitter import Emitter
17
+ from classiq.model_expansions.scope import Scope
18
+
19
+
20
+ class PowerEmitter(Emitter[Power]):
21
+ _power_value: Union[int, sympy.Basic]
22
+ _power_expr: Expression
23
+
24
+ def emit(self, power: Power, /) -> None:
25
+ self._power = power
26
+ self._power_value = self._get_power_value()
27
+ self._power_expr = Expression(
28
+ expr=str(self._power_value), source_ref=power.power.source_ref
29
+ )
30
+ self._power_expr._evaluated_expr = EvaluatedExpression(value=self._power_value)
31
+
32
+ if len(power.body) > 1 and isinstance(self._power_value, sympy.Basic):
33
+ self._emit_wrapped()
34
+ return
35
+
36
+ self._emit_as_operation(power)
37
+
38
+ def _emit_as_operation(self, power: Power) -> None:
39
+ power_operation = Closure(
40
+ name=POWER_OPERATOR_NAME,
41
+ blocks=dict(body=self._power.body),
42
+ scope=Scope(parent=self._current_scope),
43
+ )
44
+ with self._propagated_var_stack.capture_variables(self._power):
45
+ context = self._expand_operation(power_operation)
46
+ self._builder.emit_statement(
47
+ Power(
48
+ body=context.statements("body"),
49
+ power=self._power_expr,
50
+ source_ref=power.source_ref,
51
+ )
52
+ )
53
+
54
+ def _emit_wrapped(self) -> None:
55
+ with self._propagated_var_stack.capture_variables(self._power):
56
+ wrapping_function = self._create_expanded_wrapping_function(
57
+ POWER_OPERATOR_NAME, self._power.body
58
+ )
59
+ self._builder.emit_statement(
60
+ Power(
61
+ body=[wrapping_function],
62
+ power=self._power_expr,
63
+ source_ref=self._power.source_ref,
64
+ )
65
+ )
66
+
67
+ def _get_power_value(self) -> Union[int, sympy.Basic]:
68
+ power_value = self._interpreter.evaluate(self._power.power).value
69
+ if not (isinstance(power_value, int) or power_value.is_symbol):
70
+ raise ClassiqExpansionError(
71
+ f"`power`'s argument should be an integer or identifier. Complex "
72
+ f"expressions are not supported. Got {str(power_value)!r}"
73
+ )
74
+ return power_value