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,235 @@
1
+ from typing import Mapping, Optional
2
+
3
+ from classiq.interface.exceptions import (
4
+ ClassiqExpansionError,
5
+ ClassiqInternalExpansionError,
6
+ )
7
+ from classiq.interface.generator.expressions.expression import Expression
8
+ from classiq.interface.generator.functions.concrete_types import ConcreteQuantumType
9
+ from classiq.interface.generator.functions.type_name import (
10
+ TypeName,
11
+ )
12
+ from classiq.interface.model.bind_operation import BindOperation
13
+ from classiq.interface.model.inplace_binary_operation import BinaryOperation
14
+ from classiq.interface.model.quantum_type import (
15
+ QuantumBit,
16
+ QuantumBitvector,
17
+ QuantumNumeric,
18
+ QuantumType,
19
+ )
20
+
21
+ from classiq.model_expansions.scope import QuantumSymbol, Scope
22
+
23
+
24
+ def copy_type_information(
25
+ from_type: QuantumType,
26
+ to_type: QuantumType,
27
+ to_param_name: str,
28
+ ) -> None:
29
+ if isinstance(to_type, QuantumBit):
30
+ set_size(to_type, from_type.size_in_bits, to_param_name)
31
+ elif isinstance(to_type, QuantumNumeric):
32
+ set_is_signed(to_type, getattr(from_type, "sign_value", None), to_param_name)
33
+ set_fraction_digits(
34
+ to_type,
35
+ getattr(from_type, "fraction_digits_value", None),
36
+ to_param_name,
37
+ )
38
+ set_size(to_type, from_type.size_in_bits, to_param_name)
39
+ elif isinstance(to_type, QuantumBitvector):
40
+ if isinstance(from_type, QuantumBitvector) and type( # noqa: E721
41
+ from_type.element_type
42
+ ) == type(to_type.element_type):
43
+ copy_type_information(
44
+ from_type.element_type, to_type.element_type, to_param_name
45
+ )
46
+ set_size(to_type, from_type.size_in_bits, to_param_name)
47
+ elif isinstance(to_type, TypeName):
48
+ if isinstance(from_type, TypeName) and from_type.name == to_type.name:
49
+ for field in from_type.fields:
50
+ copy_type_information(
51
+ from_type.fields[field], to_type.fields[field], to_param_name
52
+ )
53
+ set_size(to_type, from_type.size_in_bits, to_param_name)
54
+ else:
55
+ raise ClassiqInternalExpansionError
56
+
57
+
58
+ def set_size(quantum_type: QuantumType, size: int, param_name: str) -> None:
59
+ if size <= 0:
60
+ raise ClassiqExpansionError(
61
+ f"Size for {param_name!r} was deduced to be non-positive: {size!r}"
62
+ )
63
+
64
+ if quantum_type.has_size_in_bits and quantum_type.size_in_bits != size:
65
+ raise ClassiqExpansionError(
66
+ f"Size mismatch for port {param_name!r} between declared size {quantum_type.size_in_bits} and assigned size {size}"
67
+ )
68
+
69
+ if isinstance(quantum_type, QuantumNumeric):
70
+ quantum_type.size = Expression(expr=str(size))
71
+ if not quantum_type.has_sign or not quantum_type.has_fraction_digits:
72
+ set_is_signed(quantum_type, False, param_name)
73
+ set_fraction_digits(quantum_type, 0, param_name)
74
+ elif isinstance(quantum_type, QuantumBitvector):
75
+ if quantum_type.has_length:
76
+ if size % quantum_type.length_value != 0:
77
+ raise ClassiqExpansionError(
78
+ f"Size mismatch for port {param_name!r}. Cannot fit {size} "
79
+ f"qubits into an array of {quantum_type.length_value} elements."
80
+ )
81
+ set_size(
82
+ quantum_type.element_type,
83
+ size // quantum_type.length_value,
84
+ param_name,
85
+ )
86
+ set_length_by_size(quantum_type, size, param_name)
87
+ elif isinstance(quantum_type, TypeName):
88
+ fields_without_size = [
89
+ field_type
90
+ for field_type in quantum_type.fields.values()
91
+ if not field_type.has_size_in_bits
92
+ ]
93
+ if len(fields_without_size) > 1:
94
+ raise ClassiqInternalExpansionError(
95
+ f"QuantumStruct should have at most one field without "
96
+ f"predetermined size. Found {fields_without_size}."
97
+ )
98
+ if len(fields_without_size) == 1:
99
+ predetermined_size_part = sum(
100
+ field_type.size_in_bits if field_type.has_size_in_bits else 0
101
+ for field_type in quantum_type.fields.values()
102
+ )
103
+ set_size(fields_without_size[0], size - predetermined_size_part, param_name)
104
+
105
+
106
+ def set_fraction_digits(
107
+ quantum_numeric: QuantumNumeric, fraction_digits: Optional[int], param_name: str
108
+ ) -> None:
109
+ if fraction_digits is not None and fraction_digits < 0:
110
+ raise ClassiqExpansionError(
111
+ f"Number of fraction digits for {param_name!r} was deduced to be negative: "
112
+ f"{fraction_digits!r}"
113
+ )
114
+
115
+ if (
116
+ fraction_digits is not None
117
+ and quantum_numeric.fraction_digits is not None
118
+ and quantum_numeric.fraction_digits.is_evaluated()
119
+ and quantum_numeric.fraction_digits_value != fraction_digits
120
+ ):
121
+ raise ClassiqExpansionError(
122
+ f"Fraction digits mismatch for port {param_name!r} between declared "
123
+ f"fraction digits {quantum_numeric.fraction_digits_value!r} and assigned fraction "
124
+ f"digits {fraction_digits!r}"
125
+ )
126
+
127
+ if not (
128
+ quantum_numeric.fraction_digits is not None
129
+ and quantum_numeric.fraction_digits.is_evaluated()
130
+ ):
131
+ quantum_numeric.fraction_digits = Expression(expr=str(fraction_digits or 0))
132
+
133
+
134
+ def set_is_signed(
135
+ quantum_numeric: QuantumNumeric, is_signed: Optional[bool], param_name: str
136
+ ) -> None:
137
+ if (
138
+ is_signed is not None
139
+ and quantum_numeric.is_signed is not None
140
+ and quantum_numeric.is_signed.is_evaluated()
141
+ and quantum_numeric.sign_value != is_signed
142
+ ):
143
+ raise ClassiqExpansionError(
144
+ f"Sign mismatch for port {param_name!r} between declared sign {quantum_numeric.sign_value!r} and assigned sign {is_signed!r}"
145
+ )
146
+
147
+ if not (
148
+ quantum_numeric.is_signed is not None
149
+ and quantum_numeric.is_signed.is_evaluated()
150
+ ):
151
+ quantum_numeric.is_signed = Expression(expr=str(is_signed or False))
152
+
153
+
154
+ def set_element_type(
155
+ quantum_array: QuantumBitvector, element_type: ConcreteQuantumType
156
+ ) -> None:
157
+ quantum_array.element_type = element_type
158
+
159
+
160
+ def set_struct_fields(
161
+ quantum_struct: TypeName, fields: Mapping[str, ConcreteQuantumType]
162
+ ) -> None:
163
+ quantum_struct._set_fields(fields)
164
+
165
+
166
+ def set_length(quantum_array: QuantumBitvector, length: int) -> None:
167
+ quantum_array.length = Expression(expr=str(length))
168
+
169
+
170
+ def set_length_by_size(
171
+ quantum_array: QuantumBitvector, size: int, param_name: str
172
+ ) -> None:
173
+ if size <= 0:
174
+ raise ClassiqExpansionError(
175
+ f"Size for {param_name!r} was deduced to be non-positive: {size!r}"
176
+ )
177
+
178
+ if quantum_array.has_size_in_bits and quantum_array.size_in_bits != size:
179
+ raise ClassiqExpansionError(
180
+ f"Size mismatch for port {param_name!r} between declared size "
181
+ f"{quantum_array.size_in_bits} ({quantum_array.length_value} elements of "
182
+ f"size {quantum_array.element_type.size_in_bits}) and assigned size {size}."
183
+ )
184
+
185
+ if not quantum_array.element_type.has_size_in_bits:
186
+ raise ClassiqExpansionError(
187
+ f"Could not infer element size for array {param_name!r}."
188
+ )
189
+ element_size = quantum_array.element_type.size_in_bits
190
+
191
+ if size % element_size != 0:
192
+ raise ClassiqExpansionError(
193
+ f"Size mismatch for port {param_name!r}. Cannot fit elements of type "
194
+ f"{quantum_array.element_type.qmod_type_name} (size {element_size}) into "
195
+ f"{size} qubits."
196
+ )
197
+
198
+ quantum_array.length = Expression(expr=str(size // element_size))
199
+
200
+
201
+ def validate_bind_targets(bind: BindOperation, scope: Scope) -> None:
202
+ illegal_qnum_bind_targets = []
203
+ for out_handle in bind.out_handles:
204
+ out_var = scope[out_handle.name].as_type(QuantumSymbol)
205
+ out_var_type = out_var.quantum_type
206
+ if not isinstance(out_var_type, QuantumNumeric):
207
+ continue
208
+ if not out_var_type.has_size_in_bits:
209
+ illegal_qnum_bind_targets.append(str(out_var.handle))
210
+ elif not out_var_type.has_sign:
211
+ assert not out_var_type.has_fraction_digits
212
+ illegal_qnum_bind_targets.append(str(out_var.handle))
213
+ if len(illegal_qnum_bind_targets) > 0:
214
+ raise ClassiqExpansionError(
215
+ f"QNum bind targets {illegal_qnum_bind_targets!r} must be declared or initialized with size, sign, and fraction digits"
216
+ )
217
+
218
+
219
+ def validate_inplace_binary_op_vars(
220
+ value_var: QuantumSymbol, target_var: QuantumSymbol, operation_name: str
221
+ ) -> None:
222
+ if not isinstance(value_var.quantum_type, QuantumNumeric):
223
+ raise ClassiqExpansionError(
224
+ f"Cannot perform `{operation_name}` operation with non numeric value {value_var.handle}"
225
+ )
226
+ if not isinstance(target_var.quantum_type, QuantumNumeric):
227
+ raise ClassiqExpansionError(
228
+ f"Cannot perform `{operation_name}` operation with non numeric target {target_var.handle}"
229
+ )
230
+ if operation_name != BinaryOperation.Xor.value and (
231
+ value_var.quantum_type.sign_value or target_var.quantum_type.sign_value
232
+ ):
233
+ raise ClassiqExpansionError(
234
+ f"Cannot perform `{operation_name}` operation with signed variables"
235
+ )
@@ -0,0 +1,90 @@
1
+ from typing import Sequence
2
+
3
+ from classiq.interface.exceptions import (
4
+ ClassiqExpansionError,
5
+ ClassiqInternalExpansionError,
6
+ )
7
+ from classiq.interface.model.classical_parameter_declaration import (
8
+ AnonClassicalParameterDeclaration,
9
+ )
10
+ from classiq.interface.model.port_declaration import AnonPortDeclaration
11
+ from classiq.interface.model.quantum_function_declaration import (
12
+ AnonPositionalArg,
13
+ AnonQuantumOperandDeclaration,
14
+ )
15
+
16
+
17
+ def check_signature_match(
18
+ decl_params: Sequence[AnonPositionalArg],
19
+ op_params: Sequence[AnonPositionalArg],
20
+ location_str: str,
21
+ ) -> None:
22
+ if len(decl_params) != len(op_params):
23
+ raise ClassiqExpansionError(
24
+ f"{location_str} should have {len(decl_params)} parameters, "
25
+ f"not {len(op_params)}"
26
+ )
27
+ for idx, (decl_param, op_param) in enumerate(zip(decl_params, op_params)):
28
+ message_prefix = f"Argument {idx} in {location_str} has incompatible type; "
29
+ _check_type_match(op_param, decl_param, location_str, message_prefix)
30
+
31
+
32
+ def _check_type_match(
33
+ op_param: AnonPositionalArg,
34
+ decl_param: AnonPositionalArg,
35
+ location_str: str,
36
+ message_prefix: str,
37
+ ) -> None:
38
+ if isinstance(decl_param, AnonPortDeclaration):
39
+ error_message = message_prefix + "expected quantum parameter"
40
+ _check_qvar_type_match(op_param, error_message)
41
+ elif isinstance(decl_param, AnonQuantumOperandDeclaration):
42
+ if decl_param.is_list:
43
+ error_message = message_prefix + "expected operand list parameter"
44
+ else:
45
+ error_message = message_prefix + "expected operand parameter"
46
+ _check_operand_type_match(op_param, decl_param, location_str, error_message)
47
+ elif isinstance(decl_param, AnonClassicalParameterDeclaration):
48
+ error_message = (
49
+ message_prefix + f"expected classical {decl_param.classical_type} parameter"
50
+ )
51
+ _check_classical_type_match(op_param, decl_param, error_message)
52
+ else:
53
+ raise ClassiqInternalExpansionError(
54
+ f"unexpected parameter declaration type: {type(decl_param).__name__}"
55
+ )
56
+
57
+
58
+ def _check_qvar_type_match(op_param: AnonPositionalArg, error_message: str) -> None:
59
+ if not isinstance(op_param, AnonPortDeclaration):
60
+ raise ClassiqExpansionError(error_message)
61
+
62
+
63
+ def _check_operand_type_match(
64
+ op_param: AnonPositionalArg,
65
+ decl_param: AnonQuantumOperandDeclaration,
66
+ location_str: str,
67
+ error_message: str,
68
+ ) -> None:
69
+ if (
70
+ not isinstance(op_param, AnonQuantumOperandDeclaration)
71
+ or decl_param.is_list ^ op_param.is_list
72
+ ):
73
+ raise ClassiqExpansionError(error_message)
74
+ check_signature_match(
75
+ decl_param.positional_arg_declarations,
76
+ op_param.positional_arg_declarations,
77
+ f"operand {decl_param.name} in {location_str}",
78
+ )
79
+
80
+
81
+ def _check_classical_type_match(
82
+ op_param: AnonPositionalArg,
83
+ decl_param: AnonClassicalParameterDeclaration,
84
+ error_message: str,
85
+ ) -> None:
86
+ if (
87
+ not isinstance(op_param, AnonClassicalParameterDeclaration)
88
+ or decl_param.classical_type != op_param.classical_type
89
+ ):
90
+ raise ClassiqExpansionError(error_message)
@@ -0,0 +1,125 @@
1
+ import ast
2
+ from enum import EnumMeta
3
+ from typing import Any, Dict, List, Mapping, Optional, Set
4
+
5
+ from sympy import sympify
6
+
7
+ from classiq.interface.exceptions import ClassiqExpansionError
8
+ from classiq.interface.generator.constant import Constant
9
+ from classiq.interface.generator.expressions.evaluated_expression import (
10
+ EvaluatedExpression,
11
+ )
12
+ from classiq.interface.generator.expressions.expression import Expression
13
+ from classiq.interface.generator.expressions.expression_constants import (
14
+ CPARAM_EXECUTION_SUFFIX,
15
+ )
16
+ from classiq.interface.generator.expressions.expression_types import ExpressionValue
17
+ from classiq.interface.generator.expressions.sympy_supported_expressions import (
18
+ SYMPY_SUPPORTED_EXPRESSIONS,
19
+ )
20
+
21
+ from classiq.model_expansions.atomic_expression_functions_defs import (
22
+ ATOMIC_EXPRESSION_FUNCTIONS,
23
+ qmod_val_to_python,
24
+ )
25
+ from classiq.model_expansions.model_tables import SymbolTable
26
+ from classiq.model_expansions.sympy_conversion.expression_to_sympy import (
27
+ translate_to_sympy,
28
+ )
29
+ from classiq.model_expansions.sympy_conversion.sympy_to_python import sympy_to_python
30
+ from classiq.qmod import symbolic
31
+
32
+
33
+ def evaluate_constants(constants: List[Constant]) -> Dict[str, EvaluatedExpression]:
34
+ result: Dict[str, EvaluatedExpression] = {}
35
+ for constant in constants:
36
+ result[constant.name] = evaluate(constant.value, result)
37
+ return result
38
+
39
+
40
+ def evaluate_constants_as_python(constants: List[Constant]) -> Dict[str, Any]:
41
+ evaluated = evaluate_constants(constants)
42
+ return {
43
+ constant.name: qmod_val_to_python(
44
+ evaluated[constant.name].value, constant.const_type
45
+ )
46
+ for constant in constants
47
+ }
48
+
49
+
50
+ def evaluate(
51
+ expr: Expression,
52
+ locals_dict: Mapping[str, EvaluatedExpression],
53
+ uninitialized_locals: Optional[Set[str]] = None,
54
+ ) -> EvaluatedExpression:
55
+ model_locals: Dict[str, ExpressionValue] = {}
56
+ model_locals.update(ATOMIC_EXPRESSION_FUNCTIONS)
57
+ model_locals.update(SymbolTable.enum_table.enums)
58
+ # locals override builtin-functions
59
+ model_locals.update({name: expr.value for name, expr in locals_dict.items()})
60
+ uninitialized_locals = uninitialized_locals or set()
61
+
62
+ _validate_undefined_vars(expr.expr, model_locals, uninitialized_locals)
63
+
64
+ sympy_expr = translate_to_sympy(expr.expr)
65
+ try:
66
+ sympify_result = sympify(sympy_expr, locals=model_locals)
67
+ except (TypeError, IndexError) as e:
68
+ raise ClassiqExpansionError(str(e)) from None
69
+ except AttributeError as e:
70
+ if isinstance(e.obj, EnumMeta):
71
+ raise ClassiqExpansionError(
72
+ f"Enum {e.obj.__name__} has no member {e.name!r}. Available members: "
73
+ f"{', '.join(e.obj.__members__)}"
74
+ ) from e
75
+ raise
76
+
77
+ return EvaluatedExpression(
78
+ value=sympy_to_python(sympify_result, locals=model_locals)
79
+ )
80
+
81
+
82
+ def _validate_undefined_vars(
83
+ expr: str,
84
+ model_locals: Dict[str, ExpressionValue],
85
+ uninitialized_locals: Optional[Set[str]],
86
+ ) -> None:
87
+ uninitialized_locals = uninitialized_locals or set()
88
+ id_visitor = _VarsCollector()
89
+ id_visitor.visit(ast.parse(expr))
90
+ identifiers = id_visitor.vars
91
+ undefined_vars = (
92
+ identifiers
93
+ - model_locals.keys()
94
+ - set(SYMPY_SUPPORTED_EXPRESSIONS)
95
+ - set(symbolic.__all__)
96
+ - uninitialized_locals
97
+ )
98
+
99
+ # Ignore expanded execution parameters
100
+ undefined_vars = {
101
+ var for var in undefined_vars if not var.endswith(CPARAM_EXECUTION_SUFFIX)
102
+ }
103
+
104
+ if len(undefined_vars) == 1:
105
+ undefined_var = undefined_vars.__iter__().__next__()
106
+ raise ClassiqExpansionError(f"Variable {undefined_var!r} is undefined")
107
+ elif len(undefined_vars) > 1:
108
+ raise ClassiqExpansionError(f"Variables {list(undefined_vars)} are undefined")
109
+
110
+
111
+ class _VarsCollector(ast.NodeTransformer):
112
+ def __init__(self) -> None:
113
+ self.vars: Set[str] = set()
114
+
115
+ def visit_Name(self, node: ast.Name) -> None:
116
+ self.vars.add(node.id)
117
+
118
+ def visit_Call(self, node: ast.Call) -> None:
119
+ func = node.func
120
+ self.visit(func)
121
+ if not isinstance(func, ast.Name) or func.id != "struct_literal":
122
+ for arg in node.args:
123
+ self.visit(arg)
124
+ for kw in node.keywords:
125
+ self.visit(kw)
@@ -0,0 +1,76 @@
1
+ import ast
2
+ from typing import Dict, Sequence
3
+
4
+ from classiq.interface.generator.expressions.expression import Expression
5
+ from classiq.interface.generator.expressions.expression_constants import (
6
+ RESERVED_EXPRESSIONS,
7
+ )
8
+ from classiq.interface.generator.visitor import Transformer
9
+ from classiq.interface.model.quantum_function_call import QuantumFunctionCall
10
+ from classiq.interface.model.quantum_function_declaration import (
11
+ ClassicalParameterDeclaration,
12
+ PositionalArg,
13
+ )
14
+
15
+ from classiq.model_expansions.visitors.variable_references import VarRefTransformer
16
+
17
+
18
+ class ExpressionRenamer(Transformer):
19
+
20
+ def __init__(self, var_mapping: Dict[str, str]) -> None:
21
+ self.var_ref_transformer = VarRefTransformer(var_mapping)
22
+
23
+ @property
24
+ def var_mapping(self) -> Dict[str, str]:
25
+ return self.var_ref_transformer.var_mapping
26
+
27
+ def visit_QuantumFunctionCall(
28
+ self, node: QuantumFunctionCall
29
+ ) -> QuantumFunctionCall:
30
+ update_dict = dict(
31
+ positional_args=[self.visit(arg) for arg in node.positional_args],
32
+ )
33
+ return node.copy(update=update_dict)
34
+
35
+ def visit_Expression(self, expression: Expression) -> Expression:
36
+ parsed_expr = ast.parse(expression.expr)
37
+ renamed_expr = self.var_ref_transformer.visit(parsed_expr)
38
+ return Expression(expr=ast.unparse(renamed_expr))
39
+
40
+ def rename_string(self, name: str) -> str:
41
+ return self.var_mapping.get(name, name)
42
+
43
+ def rename_expression_dict(
44
+ self, expr_dict: Dict[str, Expression]
45
+ ) -> Dict[str, Expression]:
46
+ return {
47
+ self.rename_string(key): self.visit(val) for key, val in expr_dict.items()
48
+ }
49
+
50
+ @classmethod
51
+ def from_positional_arg_declarations(
52
+ cls, positional_arg_declarations: Sequence[PositionalArg], suffix: str
53
+ ) -> "ExpressionRenamer":
54
+ var_mapping: Dict[str, str] = dict()
55
+ for arg in positional_arg_declarations:
56
+ if not isinstance(arg, ClassicalParameterDeclaration):
57
+ continue
58
+ if arg.name in RESERVED_EXPRESSIONS:
59
+ continue
60
+ new_name = arg.name + suffix
61
+ var_mapping[arg.name] = new_name
62
+
63
+ return cls(var_mapping)
64
+
65
+ def rename_positional_arg_declarations(
66
+ self, positional_arg_declarations: Sequence[PositionalArg]
67
+ ) -> Sequence[PositionalArg]:
68
+ return tuple(
69
+ (
70
+ arg.rename(self.var_mapping[arg.name])
71
+ if isinstance(arg, ClassicalParameterDeclaration)
72
+ and arg.name in self.var_mapping
73
+ else arg
74
+ )
75
+ for arg in positional_arg_declarations
76
+ )