classiq 0.37.0__py3-none-any.whl → 0.38.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 (228) hide show
  1. classiq/__init__.py +2 -2
  2. classiq/_analyzer_extras/_ipywidgets_async_extension.py +1 -1
  3. classiq/_analyzer_extras/interactive_hardware.py +3 -3
  4. classiq/_internals/api_wrapper.py +24 -16
  5. classiq/_internals/async_utils.py +1 -74
  6. classiq/_internals/authentication/device.py +9 -4
  7. classiq/_internals/authentication/password_manager.py +25 -10
  8. classiq/_internals/authentication/token_manager.py +2 -2
  9. classiq/_internals/client.py +13 -5
  10. classiq/_internals/jobs.py +10 -7
  11. classiq/analyzer/analyzer.py +26 -28
  12. classiq/analyzer/analyzer_utilities.py +5 -5
  13. classiq/analyzer/rb.py +4 -5
  14. classiq/analyzer/show_interactive_hack.py +6 -6
  15. classiq/applications/benchmarking/mirror_benchmarking.py +9 -6
  16. classiq/applications/combinatorial_optimization/__init__.py +5 -0
  17. classiq/applications/qnn/circuit_utils.py +2 -2
  18. classiq/applications/qnn/gradients/quantum_gradient.py +2 -2
  19. classiq/applications/qnn/types.py +2 -2
  20. classiq/applications/qsvm/qsvm.py +4 -7
  21. classiq/applications/qsvm/qsvm_data_generation.py +2 -5
  22. classiq/applications_model_constructors/__init__.py +9 -1
  23. classiq/applications_model_constructors/chemistry_model_constructor.py +9 -16
  24. classiq/applications_model_constructors/combinatorial_helpers/__init__.py +0 -0
  25. classiq/applications_model_constructors/combinatorial_helpers/allowed_constraints.py +20 -0
  26. classiq/applications_model_constructors/combinatorial_helpers/arithmetic/__init__.py +0 -0
  27. classiq/applications_model_constructors/combinatorial_helpers/arithmetic/arithmetic_expression.py +35 -0
  28. classiq/applications_model_constructors/combinatorial_helpers/arithmetic/isolation.py +42 -0
  29. classiq/applications_model_constructors/combinatorial_helpers/combinatorial_problem_utils.py +130 -0
  30. classiq/applications_model_constructors/combinatorial_helpers/encoding_mapping.py +107 -0
  31. classiq/applications_model_constructors/combinatorial_helpers/encoding_utils.py +122 -0
  32. classiq/applications_model_constructors/combinatorial_helpers/memory.py +79 -0
  33. classiq/applications_model_constructors/combinatorial_helpers/multiple_comp_basis_sp.py +34 -0
  34. classiq/applications_model_constructors/combinatorial_helpers/optimization_model.py +166 -0
  35. classiq/applications_model_constructors/combinatorial_helpers/pauli_helpers/__init__.py +0 -0
  36. classiq/applications_model_constructors/combinatorial_helpers/pauli_helpers/pauli_sparsing.py +31 -0
  37. classiq/applications_model_constructors/combinatorial_helpers/pauli_helpers/pauli_utils.py +65 -0
  38. classiq/applications_model_constructors/combinatorial_helpers/py.typed +0 -0
  39. classiq/applications_model_constructors/combinatorial_helpers/pyomo_utils.py +243 -0
  40. classiq/applications_model_constructors/combinatorial_helpers/sympy_utils.py +22 -0
  41. classiq/applications_model_constructors/combinatorial_helpers/transformations/__init__.py +0 -0
  42. classiq/applications_model_constructors/combinatorial_helpers/transformations/encoding.py +194 -0
  43. classiq/applications_model_constructors/combinatorial_helpers/transformations/fixed_variables.py +144 -0
  44. classiq/applications_model_constructors/combinatorial_helpers/transformations/ising_converter.py +124 -0
  45. classiq/applications_model_constructors/combinatorial_helpers/transformations/penalty.py +32 -0
  46. classiq/applications_model_constructors/combinatorial_helpers/transformations/penalty_support.py +41 -0
  47. classiq/applications_model_constructors/combinatorial_helpers/transformations/sign_seperation.py +75 -0
  48. classiq/applications_model_constructors/combinatorial_helpers/transformations/slack_variables.py +90 -0
  49. classiq/applications_model_constructors/combinatorial_optimization_model_constructor.py +48 -91
  50. classiq/applications_model_constructors/finance_model_constructor.py +4 -17
  51. classiq/applications_model_constructors/grover_model_constructor.py +20 -91
  52. classiq/applications_model_constructors/libraries/qmci_library.py +17 -19
  53. classiq/builtin_functions/standard_gates.py +1 -1
  54. classiq/exceptions.py +43 -1
  55. classiq/executor.py +10 -9
  56. classiq/interface/_version.py +1 -1
  57. classiq/interface/analyzer/analysis_params.py +6 -3
  58. classiq/interface/analyzer/result.py +12 -4
  59. classiq/interface/applications/qsvm.py +13 -1
  60. classiq/interface/backend/backend_preferences.py +4 -2
  61. classiq/interface/backend/pydantic_backend.py +3 -1
  62. classiq/interface/backend/quantum_backend_providers.py +1 -0
  63. classiq/interface/chemistry/fermionic_operator.py +15 -13
  64. classiq/interface/chemistry/ground_state_problem.py +18 -3
  65. classiq/interface/chemistry/molecule.py +8 -6
  66. classiq/interface/chemistry/operator.py +20 -14
  67. classiq/interface/combinatorial_optimization/examples/ascending_sequence.py +1 -1
  68. classiq/interface/combinatorial_optimization/examples/greater_than_ilp.py +1 -1
  69. classiq/interface/combinatorial_optimization/examples/ilp.py +2 -1
  70. classiq/interface/combinatorial_optimization/examples/integer_portfolio_optimization.py +2 -2
  71. classiq/interface/combinatorial_optimization/examples/mds.py +2 -1
  72. classiq/interface/combinatorial_optimization/examples/mht.py +3 -3
  73. classiq/interface/combinatorial_optimization/examples/mis.py +4 -1
  74. classiq/interface/combinatorial_optimization/examples/mvc.py +2 -1
  75. classiq/interface/combinatorial_optimization/examples/set_cover.py +2 -1
  76. classiq/interface/combinatorial_optimization/examples/tsp.py +4 -3
  77. classiq/interface/combinatorial_optimization/examples/tsp_digraph.py +6 -2
  78. classiq/interface/combinatorial_optimization/mht_qaoa_input.py +9 -3
  79. classiq/interface/executor/aws_execution_cost.py +4 -3
  80. classiq/interface/executor/estimation.py +2 -2
  81. classiq/interface/executor/execution_preferences.py +5 -34
  82. classiq/interface/executor/execution_request.py +19 -17
  83. classiq/interface/executor/optimizer_preferences.py +22 -13
  84. classiq/interface/executor/{quantum_program.py → quantum_code.py} +21 -15
  85. classiq/interface/executor/quantum_instruction_set.py +2 -1
  86. classiq/interface/executor/register_initialization.py +1 -3
  87. classiq/interface/executor/result.py +41 -10
  88. classiq/interface/executor/vqe_result.py +1 -1
  89. classiq/interface/finance/function_input.py +17 -4
  90. classiq/interface/finance/gaussian_model_input.py +3 -1
  91. classiq/interface/finance/log_normal_model_input.py +3 -1
  92. classiq/interface/finance/model_input.py +2 -0
  93. classiq/interface/generator/amplitude_loading.py +6 -3
  94. classiq/interface/generator/application_apis/__init__.py +1 -0
  95. classiq/interface/generator/application_apis/arithmetic_declarations.py +14 -0
  96. classiq/interface/generator/arith/argument_utils.py +14 -4
  97. classiq/interface/generator/arith/arithmetic.py +3 -1
  98. classiq/interface/generator/arith/arithmetic_arg_type_validator.py +12 -13
  99. classiq/interface/generator/arith/arithmetic_expression_abc.py +4 -1
  100. classiq/interface/generator/arith/arithmetic_expression_parser.py +8 -2
  101. classiq/interface/generator/arith/arithmetic_expression_validator.py +16 -2
  102. classiq/interface/generator/arith/arithmetic_operations.py +5 -10
  103. classiq/interface/generator/arith/ast_node_rewrite.py +1 -1
  104. classiq/interface/generator/arith/binary_ops.py +202 -54
  105. classiq/interface/generator/arith/extremum_operations.py +5 -3
  106. classiq/interface/generator/arith/logical_ops.py +4 -2
  107. classiq/interface/generator/arith/machine_precision.py +3 -0
  108. classiq/interface/generator/arith/number_utils.py +34 -44
  109. classiq/interface/generator/arith/register_user_input.py +21 -1
  110. classiq/interface/generator/arith/unary_ops.py +16 -25
  111. classiq/interface/generator/chemistry_function_params.py +4 -4
  112. classiq/interface/generator/commuting_pauli_exponentiation.py +3 -1
  113. classiq/interface/generator/compiler_keywords.py +4 -0
  114. classiq/interface/generator/complex_type.py +3 -10
  115. classiq/interface/generator/control_state.py +5 -3
  116. classiq/interface/generator/credit_risk_example/linear_gci.py +10 -3
  117. classiq/interface/generator/credit_risk_example/weighted_adder.py +14 -4
  118. classiq/interface/generator/expressions/atomic_expression_functions.py +5 -3
  119. classiq/interface/generator/expressions/evaluated_expression.py +18 -4
  120. classiq/interface/generator/expressions/expression.py +1 -1
  121. classiq/interface/generator/expressions/qmod_qscalar_proxy.py +33 -0
  122. classiq/interface/generator/expressions/sympy_supported_expressions.py +2 -1
  123. classiq/interface/generator/finance.py +1 -1
  124. classiq/interface/generator/function_params.py +7 -6
  125. classiq/interface/generator/functions/__init__.py +1 -1
  126. classiq/interface/generator/functions/core_lib_declarations/quantum_functions/std_lib_functions.py +505 -138
  127. classiq/interface/generator/functions/core_lib_declarations/quantum_operators.py +25 -99
  128. classiq/interface/generator/functions/foreign_function_definition.py +12 -4
  129. classiq/interface/generator/functions/function_implementation.py +8 -4
  130. classiq/interface/generator/functions/native_function_definition.py +4 -2
  131. classiq/interface/generator/functions/register.py +4 -2
  132. classiq/interface/generator/functions/register_mapping_data.py +14 -10
  133. classiq/interface/generator/generated_circuit_data.py +2 -2
  134. classiq/interface/generator/grover_operator.py +5 -3
  135. classiq/interface/generator/hamiltonian_evolution/suzuki_trotter.py +5 -1
  136. classiq/interface/generator/hardware/hardware_data.py +6 -4
  137. classiq/interface/generator/hardware_efficient_ansatz.py +25 -8
  138. classiq/interface/generator/hartree_fock.py +3 -1
  139. classiq/interface/generator/linear_pauli_rotations.py +3 -1
  140. classiq/interface/generator/mcu.py +5 -3
  141. classiq/interface/generator/mcx.py +7 -5
  142. classiq/interface/generator/model/constraints.py +2 -1
  143. classiq/interface/generator/model/model.py +11 -19
  144. classiq/interface/generator/model/preferences/preferences.py +4 -3
  145. classiq/interface/generator/oracles/custom_oracle.py +4 -2
  146. classiq/interface/generator/oracles/oracle_abc.py +2 -2
  147. classiq/interface/generator/qpe.py +6 -4
  148. classiq/interface/generator/qsvm.py +5 -8
  149. classiq/interface/generator/quantum_function_call.py +21 -16
  150. classiq/interface/generator/{generated_circuit.py → quantum_program.py} +10 -14
  151. classiq/interface/generator/range_types.py +3 -1
  152. classiq/interface/generator/slice_parsing_utils.py +8 -3
  153. classiq/interface/generator/standard_gates/controlled_standard_gates.py +4 -2
  154. classiq/interface/generator/state_preparation/metrics.py +2 -1
  155. classiq/interface/generator/state_preparation/state_preparation.py +7 -5
  156. classiq/interface/generator/state_propagator.py +16 -5
  157. classiq/interface/generator/types/builtin_struct_declarations/__init__.py +0 -1
  158. classiq/interface/generator/types/struct_declaration.py +8 -3
  159. classiq/interface/generator/ucc.py +6 -4
  160. classiq/interface/generator/unitary_gate.py +7 -3
  161. classiq/interface/generator/validations/flow_graph.py +6 -4
  162. classiq/interface/generator/validations/validator_functions.py +6 -4
  163. classiq/interface/hardware.py +2 -2
  164. classiq/interface/helpers/custom_encoders.py +3 -0
  165. classiq/interface/helpers/pydantic_model_helpers.py +0 -6
  166. classiq/interface/helpers/validation_helpers.py +1 -1
  167. classiq/interface/helpers/versioned_model.py +4 -1
  168. classiq/interface/ide/show.py +2 -2
  169. classiq/interface/jobs.py +72 -3
  170. classiq/interface/model/bind_operation.py +18 -11
  171. classiq/interface/model/call_synthesis_data.py +68 -0
  172. classiq/interface/model/inplace_binary_operation.py +2 -2
  173. classiq/interface/model/model.py +27 -21
  174. classiq/interface/model/native_function_definition.py +3 -5
  175. classiq/interface/model/quantum_expressions/amplitude_loading_operation.py +9 -4
  176. classiq/interface/model/quantum_expressions/control_state.py +2 -2
  177. classiq/interface/model/quantum_function_call.py +25 -139
  178. classiq/interface/model/quantum_function_declaration.py +8 -0
  179. classiq/interface/model/quantum_if_operation.py +2 -3
  180. classiq/interface/model/quantum_lambda_function.py +64 -0
  181. classiq/interface/model/quantum_type.py +57 -56
  182. classiq/interface/model/quantum_variable_declaration.py +1 -1
  183. classiq/interface/model/statement_block.py +32 -0
  184. classiq/interface/model/validations/handles_validator.py +14 -12
  185. classiq/interface/model/within_apply_operation.py +11 -0
  186. classiq/interface/pyomo_extension/pyomo_sympy_bimap.py +4 -1
  187. classiq/interface/server/routes.py +5 -0
  188. classiq/model/function_handler.py +5 -9
  189. classiq/model/model.py +2 -19
  190. classiq/qmod/__init__.py +13 -6
  191. classiq/qmod/builtins/classical_execution_primitives.py +27 -36
  192. classiq/qmod/builtins/classical_functions.py +24 -14
  193. classiq/qmod/builtins/functions.py +162 -145
  194. classiq/qmod/builtins/operations.py +24 -35
  195. classiq/qmod/builtins/structs.py +15 -15
  196. classiq/qmod/cfunc.py +42 -0
  197. classiq/qmod/classical_function.py +6 -14
  198. classiq/qmod/declaration_inferrer.py +12 -21
  199. classiq/qmod/expression_query.py +23 -0
  200. classiq/qmod/model_state_container.py +2 -0
  201. classiq/qmod/native/__init__.py +0 -0
  202. classiq/qmod/native/expression_to_qmod.py +189 -0
  203. classiq/qmod/native/pretty_printer.py +311 -0
  204. classiq/qmod/qfunc.py +27 -0
  205. classiq/qmod/qmod_constant.py +76 -0
  206. classiq/qmod/qmod_parameter.py +34 -12
  207. classiq/qmod/qmod_struct.py +3 -3
  208. classiq/qmod/qmod_variable.py +102 -18
  209. classiq/qmod/quantum_expandable.py +16 -16
  210. classiq/qmod/quantum_function.py +37 -8
  211. classiq/qmod/symbolic.py +47 -4
  212. classiq/qmod/symbolic_expr.py +9 -0
  213. classiq/qmod/utilities.py +13 -0
  214. classiq/qmod/write_qmod.py +39 -0
  215. classiq/quantum_functions/__init__.py +2 -2
  216. classiq/quantum_functions/annotation_parser.py +9 -11
  217. classiq/quantum_functions/function_parser.py +1 -1
  218. classiq/quantum_functions/quantum_function.py +3 -3
  219. classiq/quantum_register.py +17 -9
  220. {classiq-0.37.0.dist-info → classiq-0.38.0.dist-info}/METADATA +2 -1
  221. {classiq-0.37.0.dist-info → classiq-0.38.0.dist-info}/RECORD +222 -186
  222. {classiq-0.37.0.dist-info → classiq-0.38.0.dist-info}/WHEEL +1 -1
  223. classiq/interface/generator/expressions/qmod_qnum_proxy.py +0 -22
  224. classiq/interface/generator/types/builtin_struct_declarations/qaoa_declarations.py +0 -23
  225. classiq/interface/generator/types/combinatorial_problem.py +0 -26
  226. classiq/interface/model/numeric_reinterpretation.py +0 -25
  227. classiq/interface/model/operator_synthesis_data.py +0 -48
  228. classiq/model/function_handler.pyi +0 -152
@@ -0,0 +1,311 @@
1
+ from typing import Dict, List
2
+
3
+ from classiq.interface.generator.constant import Constant
4
+ from classiq.interface.generator.expressions.expression import Expression
5
+ from classiq.interface.generator.functions.classical_type import (
6
+ ClassicalArray,
7
+ ConcreteClassicalType,
8
+ )
9
+ from classiq.interface.generator.functions.port_declaration import (
10
+ PortDeclarationDirection,
11
+ )
12
+ from classiq.interface.generator.visitor import NodeType, Visitor
13
+ from classiq.interface.model.bind_operation import BindOperation
14
+ from classiq.interface.model.classical_parameter_declaration import (
15
+ ClassicalParameterDeclaration,
16
+ )
17
+ from classiq.interface.model.handle_binding import (
18
+ HandleBinding,
19
+ SlicedHandleBinding,
20
+ SubscriptHandleBinding,
21
+ )
22
+ from classiq.interface.model.inplace_binary_operation import InplaceBinaryOperation
23
+ from classiq.interface.model.model import Model
24
+ from classiq.interface.model.native_function_definition import NativeFunctionDefinition
25
+ from classiq.interface.model.port_declaration import PortDeclaration
26
+ from classiq.interface.model.quantum_expressions.amplitude_loading_operation import (
27
+ AmplitudeLoadingOperation,
28
+ )
29
+ from classiq.interface.model.quantum_expressions.arithmetic_operation import (
30
+ ArithmeticOperation,
31
+ )
32
+ from classiq.interface.model.quantum_function_call import (
33
+ OperandIdentifier,
34
+ QuantumFunctionCall,
35
+ )
36
+ from classiq.interface.model.quantum_function_declaration import (
37
+ QuantumFunctionDeclaration,
38
+ QuantumOperandDeclaration,
39
+ )
40
+ from classiq.interface.model.quantum_if_operation import QuantumIfOperation
41
+ from classiq.interface.model.quantum_lambda_function import QuantumLambdaFunction
42
+ from classiq.interface.model.quantum_type import (
43
+ QuantumBit,
44
+ QuantumBitvector,
45
+ QuantumNumeric,
46
+ )
47
+ from classiq.interface.model.quantum_variable_declaration import (
48
+ QuantumVariableDeclaration,
49
+ )
50
+ from classiq.interface.model.statement_block import StatementBlock
51
+ from classiq.interface.model.variable_declaration_statement import (
52
+ VariableDeclarationStatement,
53
+ )
54
+ from classiq.interface.model.within_apply_operation import WithinApplyOperation
55
+
56
+ from classiq import Bool, ClassicalList, Integer, Pauli, Real, Struct, StructDeclaration
57
+ from classiq.qmod.native.expression_to_qmod import transform_expression
58
+ from classiq.qmod.utilities import DEFAULT_DECIMAL_PRECISION
59
+
60
+
61
+ class DSLPrettyPrinter(Visitor):
62
+ def __init__(self, decimal_precision: int = DEFAULT_DECIMAL_PRECISION) -> None:
63
+ self._level = 0
64
+ self._decimal_precision = decimal_precision
65
+
66
+ def visit(self, node: NodeType) -> str:
67
+ res = super().visit(node)
68
+ if not isinstance(res, str):
69
+ raise AssertionError(f"Pretty printing for {type(node)} is not supported ")
70
+ return res
71
+
72
+ def visit_Model(self, model: Model) -> str:
73
+ struct_decls = [self.visit(struct_decl) for struct_decl in model.types]
74
+ func_defs = [self.visit(func_def) for func_def in model.functions]
75
+ constants = [self.visit(constant) for constant in model.constants]
76
+ classical_code = (
77
+ f"cscope ```\n{model.classical_execution_code}\n```"
78
+ if model.classical_execution_code
79
+ else ""
80
+ )
81
+ return "\n".join([*constants, *struct_decls, *func_defs, classical_code])
82
+
83
+ def visit_Constant(self, constant: Constant) -> str:
84
+ return f"{self._indent}{self.visit(constant.name)}: {self.visit(constant.const_type)} = {self.visit(constant.value)};\n"
85
+
86
+ def _visit_arg_decls(self, func_def: QuantumFunctionDeclaration) -> str:
87
+ gen_time_args = ", ".join(
88
+ self.visit(arg_decl)
89
+ for arg_decl in func_def.get_positional_arg_decls()
90
+ if not isinstance(arg_decl, PortDeclaration)
91
+ )
92
+ quantum_args = ", ".join(
93
+ self.visit(arg_decl)
94
+ for arg_decl in func_def.get_positional_arg_decls()
95
+ if isinstance(arg_decl, PortDeclaration)
96
+ )
97
+ gen_time_arg_list = f"<{gen_time_args}>" if gen_time_args else ""
98
+ return f"{gen_time_arg_list}({quantum_args})"
99
+
100
+ def visit_QuantumFunctionDeclaration(
101
+ self, func_decl: QuantumFunctionDeclaration
102
+ ) -> str:
103
+ return f"qfunc {func_decl.name}{self._visit_arg_decls(func_decl)}"
104
+
105
+ def visit_StructDeclaration(self, struct_decl: StructDeclaration) -> str:
106
+ return f"struct {struct_decl.name} {{\n{self._visit_variables(struct_decl.variables)}}}\n"
107
+
108
+ def _visit_variables(self, variables: Dict[str, ConcreteClassicalType]) -> str:
109
+ self._level += 1
110
+ variables_str = "".join(
111
+ f"{self._indent}{self.visit(field_name)}: {self.visit(var_decl)};\n"
112
+ for field_name, var_decl in variables.items()
113
+ )
114
+ self._level -= 1
115
+ return variables_str
116
+
117
+ def visit_QuantumVariableDeclaration(
118
+ self, var_decl: QuantumVariableDeclaration
119
+ ) -> str:
120
+ return f"{var_decl.name}: {self.visit(var_decl.quantum_type)}"
121
+
122
+ def visit_PortDeclaration(self, port_decl: PortDeclaration) -> str:
123
+ dir_str = (
124
+ f"{port_decl.direction} "
125
+ if port_decl.direction != PortDeclarationDirection.Inout
126
+ else ""
127
+ )
128
+ return f"{dir_str}{self.visit_QuantumVariableDeclaration(port_decl)}"
129
+
130
+ def visit_QuantumBit(self, qtype: QuantumBit) -> str:
131
+ return "qbit"
132
+
133
+ def visit_QuantumBitvector(self, qtype: QuantumBitvector) -> str:
134
+ if qtype.length is not None:
135
+ if qtype.length.is_evaluated() and qtype.length.to_int_value() == 1:
136
+ return "qbit"
137
+ return f"qbit[{self.visit(qtype.length)}]"
138
+ return "qbit[]"
139
+
140
+ def visit_QuantumNumeric(self, qtype: QuantumNumeric) -> str:
141
+ params = ""
142
+ if qtype.size is not None:
143
+ assert qtype.is_signed is not None
144
+ assert qtype.fraction_digits is not None
145
+
146
+ params = "<{}>".format(
147
+ ", ".join(
148
+ self.visit(param)
149
+ for param in [qtype.size, qtype.is_signed, qtype.fraction_digits]
150
+ )
151
+ )
152
+
153
+ return f"qnum{params}"
154
+
155
+ def visit_ClassicalParameterDeclaration(
156
+ self, cparam: ClassicalParameterDeclaration
157
+ ) -> str:
158
+ return f"{cparam.name}: {self.visit(cparam.classical_type)}"
159
+
160
+ def visit_Integer(self, ctint: Integer) -> str:
161
+ return "int"
162
+
163
+ def visit_Real(self, ctint: Real) -> str:
164
+ return "real"
165
+
166
+ def visit_Bool(self, ctbool: Bool) -> str:
167
+ return "bool"
168
+
169
+ def visit_Pauli(self, ctbool: Pauli) -> str:
170
+ return "Pauli"
171
+
172
+ def visit_ClassicalList(self, ctlist: ClassicalList) -> str:
173
+ return f"{self.visit(ctlist.element_type)}[]"
174
+
175
+ def visit_ClassicalArray(self, ctarray: ClassicalArray) -> str:
176
+ return f"{self.visit(ctarray.element_type)}[{ctarray.size}]"
177
+
178
+ def visit_Struct(self, struct: Struct) -> str:
179
+ return struct.name
180
+
181
+ def visit_VariableDeclarationStatement(
182
+ self, local_decl: VariableDeclarationStatement
183
+ ) -> str:
184
+ return f"{self._indent}{self.visit_QuantumVariableDeclaration(local_decl)};\n"
185
+
186
+ def visit_QuantumOperandDeclaration(
187
+ self, op_decl: QuantumOperandDeclaration
188
+ ) -> str:
189
+ return f"{op_decl.name}: qfunc{[] if op_decl.is_list else ''} {self._visit_arg_decls(op_decl)}"
190
+
191
+ def visit_NativeFunctionDefinition(self, func_def: NativeFunctionDefinition) -> str:
192
+ self._level += 1
193
+ body = "".join(self.visit(qvar_decl) for qvar_decl in func_def.body)
194
+ self._level -= 1
195
+ return f"{self.visit_QuantumFunctionDeclaration(func_def)} {{\n{body}}}\n"
196
+
197
+ def visit_QuantumFunctionCall(self, func_call: QuantumFunctionCall) -> str:
198
+ gen_time_args = ", ".join(
199
+ self.visit(arg_decl)
200
+ for arg_decl in func_call.get_positional_args()
201
+ if not isinstance(arg_decl, HandleBinding)
202
+ )
203
+ gen_time_arg_list = f"<{gen_time_args}>" if gen_time_args else ""
204
+ quantum_args = ", ".join(
205
+ self.visit(arg_decl)
206
+ for arg_decl in func_call.get_positional_args()
207
+ if isinstance(arg_decl, HandleBinding)
208
+ )
209
+ return f"{self._indent}{func_call.func_name}{f'[{self.visit(func_call.function.index)}]' if isinstance(func_call.function, OperandIdentifier) else ''}{gen_time_arg_list}({quantum_args});\n"
210
+
211
+ def visit_QuantumIfOperation(self, op: QuantumIfOperation) -> str:
212
+ quantum_if = f"{self._indent}quantum_if ({self.visit(op.expression)}) {{\n"
213
+ operand = op.then
214
+ if not isinstance(operand, QuantumLambdaFunction):
215
+ raise AssertionError(
216
+ "Expected quantum_if to be implemented using a lambda function"
217
+ )
218
+ quantum_if += self._visit_body(operand.body)
219
+ quantum_if += f"{self._indent}}}\n"
220
+ return quantum_if
221
+
222
+ def visit_WithinApplyOperation(self, op: WithinApplyOperation) -> str:
223
+ action = op.action
224
+ compute = op.compute
225
+ if not isinstance(action, QuantumLambdaFunction) or not isinstance(
226
+ compute, QuantumLambdaFunction
227
+ ):
228
+ raise AssertionError(
229
+ "Expected within-apply to be implemented using a lambda functions"
230
+ )
231
+ within_apply_code = f"{self._indent}within {{\n"
232
+ within_apply_code += self._visit_body(compute.body)
233
+ within_apply_code += f"{self._indent}}} apply {{\n"
234
+ within_apply_code += self._visit_body(action.body)
235
+ within_apply_code += f"{self._indent}}}\n"
236
+ return within_apply_code
237
+
238
+ def _visit_body(self, body: StatementBlock) -> str:
239
+ code = ""
240
+ self._level += 1
241
+ for statement in body:
242
+ code += self.visit(statement)
243
+ self._level -= 1
244
+ return code
245
+
246
+ def visit_InplaceBinaryOperation(self, op: InplaceBinaryOperation) -> str:
247
+ return (
248
+ f"{self._indent}{op.operation.value}({op.value.name}, {op.target.name});\n"
249
+ )
250
+
251
+ def _visit_pack_expr(self, vars: List[HandleBinding]) -> str:
252
+ if len(vars) == 1:
253
+ return self.visit(vars[0])
254
+
255
+ var_list_str = ", ".join(self.visit(var) for var in vars)
256
+ return f"{{{var_list_str}}}"
257
+
258
+ def visit_Expression(self, expr: Expression) -> str:
259
+ return transform_expression(
260
+ expr.expr, level=self._level, decimal_precision=self._decimal_precision
261
+ )
262
+
263
+ def visit_QuantumLambdaFunction(self, qlambda: QuantumLambdaFunction) -> str:
264
+ assert qlambda.func_decl is not None
265
+ gen_time_args = ", ".join(
266
+ qlambda.rename_params.get(arg_decl.name, arg_decl.name)
267
+ for arg_decl in qlambda.func_decl.get_positional_arg_decls()
268
+ if not isinstance(arg_decl, PortDeclaration)
269
+ )
270
+ quantum_args = ", ".join(
271
+ arg_decl.name
272
+ for arg_decl in qlambda.func_decl.get_positional_arg_decls()
273
+ if isinstance(arg_decl, PortDeclaration)
274
+ )
275
+ gen_time_arg_list = f"<{gen_time_args}>" if gen_time_args else ""
276
+ body = self._visit_body(qlambda.body)
277
+ return f"lambda{gen_time_arg_list}({quantum_args}) {{\n{body}{self._indent}}}"
278
+
279
+ def visit_HandleBinding(self, var_ref: HandleBinding) -> str:
280
+ return var_ref.name
281
+
282
+ def visit_SlicedHandleBinding(self, var_ref: SlicedHandleBinding) -> str:
283
+ return str(var_ref)
284
+
285
+ def visit_SubscriptHandleBinding(self, var_ref: SubscriptHandleBinding) -> str:
286
+ return str(var_ref)
287
+
288
+ def visit_ArithmeticOperation(self, arith_op: ArithmeticOperation) -> str:
289
+ op = "^=" if arith_op.inplace_result else "="
290
+ return f"{self._indent}{self.visit(arith_op.result_var)} {op} {self.visit(arith_op.expression)};\n"
291
+
292
+ def visit_AmplitudeLoadingOperation(
293
+ self, amplitude_loading_op: AmplitudeLoadingOperation
294
+ ) -> str:
295
+ return f"{self._indent}{self.visit(amplitude_loading_op.result_var)} *= {self.visit(amplitude_loading_op.expression)};\n"
296
+
297
+ def _print_bind_handles(self, handles: List[HandleBinding]) -> str:
298
+ if len(handles) == 1:
299
+ return self.visit(handles[0])
300
+
301
+ return "{" + ", ".join(self.visit(handle) for handle in handles) + "}"
302
+
303
+ def visit_BindOperation(self, bind_op: BindOperation) -> str:
304
+ return f"{self._indent}{self._print_bind_handles(bind_op.in_handles)} -> {self._print_bind_handles(bind_op.out_handles)};\n"
305
+
306
+ def visit_list(self, node: list) -> str:
307
+ return "[" + ", ".join(self.visit(elem) for elem in node) + "]"
308
+
309
+ @property
310
+ def _indent(self) -> str:
311
+ return " " * self._level
classiq/qmod/qfunc.py ADDED
@@ -0,0 +1,27 @@
1
+ from typing import Callable, Optional, Union, overload
2
+
3
+ from classiq.qmod.quantum_callable import QCallable
4
+ from classiq.qmod.quantum_function import ExternalQFunc, QFunc
5
+
6
+
7
+ @overload
8
+ def qfunc(func: Callable, *, external: bool = False) -> QFunc: ...
9
+
10
+
11
+ @overload
12
+ def qfunc(func: None = None, *, external: bool) -> Callable[[Callable], QCallable]: ...
13
+
14
+
15
+ def qfunc(
16
+ func: Optional[Callable] = None, *, external: bool = False
17
+ ) -> Union[Callable[[Callable], QCallable], QCallable]:
18
+ def wrapper(func: Callable) -> QCallable:
19
+ if external:
20
+ return ExternalQFunc(func)
21
+
22
+ return QFunc(func)
23
+
24
+ if func is not None:
25
+ return wrapper(func)
26
+
27
+ return wrapper
@@ -0,0 +1,76 @@
1
+ import inspect
2
+ from typing import Any, Optional
3
+
4
+ from classiq.interface.generator.constant import Constant
5
+ from classiq.interface.generator.expressions.expression import Expression
6
+ from classiq.interface.generator.functions.classical_type import QStructBase
7
+
8
+ from classiq.exceptions import ClassiqError
9
+ from classiq.qmod.declaration_inferrer import python_type_to_qmod
10
+ from classiq.qmod.model_state_container import ModelStateContainer
11
+ from classiq.qmod.qmod_parameter import QParam, QParamStruct
12
+
13
+
14
+ class QConstant:
15
+ CURRENT_QMODULE: Optional[ModelStateContainer] = None
16
+
17
+ def __init__(self, name: str, py_type: type, value: Any) -> None:
18
+ self.name = name
19
+ self._py_type = py_type
20
+ self._value = value
21
+
22
+ @staticmethod
23
+ def set_current_model(qmodule: ModelStateContainer) -> None:
24
+ QConstant.CURRENT_QMODULE = qmodule
25
+
26
+ def add_to_model(self) -> None:
27
+ if QConstant.CURRENT_QMODULE is None:
28
+ raise ClassiqError(
29
+ "Error trying to add a constant to a model without a current QModule."
30
+ )
31
+
32
+ expr = str(self._value)
33
+ if (
34
+ self.name in QConstant.CURRENT_QMODULE.constants
35
+ and expr != QConstant.CURRENT_QMODULE.constants[self.name].value.expr
36
+ ):
37
+ raise ClassiqError(f"Constant {self.name} is already defined in the model")
38
+
39
+ if isinstance(self._value, QConstant):
40
+ QConstant.CURRENT_QMODULE.constants[self.name] = Constant(
41
+ name=self.name,
42
+ const_type=QConstant.CURRENT_QMODULE.constants[
43
+ self._value.name
44
+ ].const_type,
45
+ value=Expression(expr=self._value.name),
46
+ )
47
+ else:
48
+ qmod_type = python_type_to_qmod(
49
+ self._py_type, qmodule=QConstant.CURRENT_QMODULE
50
+ )
51
+ if qmod_type is None:
52
+ raise ClassiqError("Invalid QMOD type")
53
+
54
+ QConstant.CURRENT_QMODULE.constants[self.name] = Constant(
55
+ name=self.name,
56
+ const_type=qmod_type,
57
+ value=Expression(expr=expr),
58
+ )
59
+
60
+ def __getattr__(self, name: str) -> QParam:
61
+ self.add_to_model()
62
+
63
+ py_type = type(self._value)
64
+ if (
65
+ QConstant.CURRENT_QMODULE is None
66
+ or not inspect.isclass(py_type)
67
+ or not issubclass(py_type, QStructBase)
68
+ ):
69
+ return self.__getattribute__(name)
70
+
71
+ return QParamStruct.get_field(
72
+ QConstant.CURRENT_QMODULE, self.name, py_type.__name__, name
73
+ )
74
+
75
+ def __str__(self) -> str:
76
+ return self.name
@@ -1,6 +1,12 @@
1
1
  import sys
2
- from typing import _GenericAlias # type: ignore[attr-defined]
3
- from typing import Any, Generic, TypeVar, Union
2
+ from typing import ( # type: ignore[attr-defined]
3
+ TYPE_CHECKING,
4
+ Any,
5
+ Generic,
6
+ TypeVar,
7
+ Union,
8
+ _GenericAlias,
9
+ )
4
10
 
5
11
  from typing_extensions import ParamSpec
6
12
 
@@ -19,8 +25,15 @@ from classiq.qmod.symbolic_expr import Symbolic, SymbolicExpr
19
25
  _T = TypeVar("_T")
20
26
 
21
27
 
22
- class QParam(Symbolic, Generic[_T]):
23
- pass
28
+ if TYPE_CHECKING:
29
+
30
+ class QParam(SymbolicExpr, Generic[_T]):
31
+ pass
32
+
33
+ else:
34
+
35
+ class QParam(Symbolic, Generic[_T]):
36
+ pass
24
37
 
25
38
 
26
39
  class QParamScalar(QParam, SymbolicExpr):
@@ -46,7 +59,7 @@ class QParamList(QParam):
46
59
  )
47
60
 
48
61
  def __len__(self) -> int:
49
- raise ValueError(
62
+ raise ClassiqValueError(
50
63
  "len(<expr>) is not supported for QMod lists - use <expr>.len() instead"
51
64
  )
52
65
 
@@ -63,22 +76,31 @@ class QParamStruct(QParam):
63
76
  self._struct_type = struct_type
64
77
 
65
78
  def __getattr__(self, field_name: str) -> QParam:
79
+ return QParamStruct.get_field(
80
+ self._qmodule, str(self), self._struct_type.name, field_name
81
+ )
82
+
83
+ @staticmethod
84
+ def get_field(
85
+ qmodule: ModelStateContainer,
86
+ variable_name: str,
87
+ struct_name: str,
88
+ field_name: str,
89
+ ) -> QParam:
66
90
  struct_decl = StructDeclaration.BUILTIN_STRUCT_DECLARATIONS.get(
67
- self._struct_type.name
91
+ struct_name, qmodule.type_decls.get(struct_name)
68
92
  )
69
- if struct_decl is None:
70
- struct_decl = self._qmodule.type_decls.get(self._struct_type.name)
71
93
  assert struct_decl is not None
72
94
  field_type = struct_decl.variables.get(field_name)
73
95
  if field_type is None:
74
96
  raise ClassiqValueError(
75
- f"Struct {self._struct_type.name!r} doesn't have field {field_name!r}"
97
+ f"Struct {struct_name!r} doesn't have field {field_name!r}"
76
98
  )
77
99
 
78
100
  return create_param(
79
- f"get_field({self},{field_name!r})",
101
+ f"get_field({variable_name},{field_name!r})",
80
102
  field_type,
81
- qmodule=self._qmodule,
103
+ qmodule=qmodule,
82
104
  )
83
105
 
84
106
 
@@ -100,7 +122,7 @@ class Array(ArrayBase[_P]):
100
122
  def create_param(
101
123
  expr_str: str, ctype: ClassicalType, qmodule: ModelStateContainer
102
124
  ) -> QParam:
103
- if isinstance(ctype, ClassicalList) or isinstance(ctype, ClassicalArray):
125
+ if isinstance(ctype, (ClassicalList, ClassicalArray)):
104
126
  return QParamList(expr_str, ctype, qmodule=qmodule)
105
127
  elif isinstance(ctype, Struct):
106
128
  return QParamStruct(expr_str, ctype, qmodule=qmodule)
@@ -1,5 +1,5 @@
1
1
  import dataclasses
2
- from typing import Any, List, Type
2
+ from typing import Any, Type
3
3
 
4
4
  from typing_extensions import dataclass_transform
5
5
 
@@ -16,7 +16,7 @@ def _qmod_val_to_expr_str(val: Any) -> str:
16
16
  )
17
17
  return f"struct_literal({type(val).__name__}, {kwargs_str})"
18
18
 
19
- if isinstance(val, List):
19
+ if isinstance(val, list):
20
20
  elements_str = ", ".join([_qmod_val_to_expr_str(elem) for elem in val])
21
21
  return f"[{elements_str}]"
22
22
 
@@ -24,7 +24,7 @@ def _qmod_val_to_expr_str(val: Any) -> str:
24
24
 
25
25
 
26
26
  @dataclass_transform()
27
- def QStruct(user_class: Type) -> Type: # noqa: N802 - for consistency with 'QFunc'
27
+ def struct(user_class: Type) -> Type:
28
28
  def _new_repr(self: Any) -> str:
29
29
  return _qmod_val_to_expr_str(self)
30
30