classiq 0.40.0__py3-none-any.whl → 0.41.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 (81) hide show
  1. classiq/__init__.py +4 -2
  2. classiq/_internals/api_wrapper.py +3 -21
  3. classiq/applications/chemistry/chemistry_model_constructor.py +86 -100
  4. classiq/applications/combinatorial_helpers/combinatorial_problem_utils.py +6 -24
  5. classiq/applications/combinatorial_helpers/pauli_helpers/pauli_utils.py +33 -45
  6. classiq/applications/combinatorial_optimization/__init__.py +2 -0
  7. classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +29 -26
  8. classiq/applications/finance/finance_model_constructor.py +23 -26
  9. classiq/applications/grover/grover_model_constructor.py +37 -38
  10. classiq/applications/qsvm/qsvm.py +1 -2
  11. classiq/applications/qsvm/qsvm_model_constructor.py +15 -16
  12. classiq/execution/__init__.py +4 -0
  13. classiq/execution/execution_session.py +151 -0
  14. classiq/execution/qnn.py +80 -0
  15. classiq/executor.py +2 -109
  16. classiq/interface/_version.py +1 -1
  17. classiq/interface/analyzer/analysis_params.py +11 -0
  18. classiq/interface/applications/qsvm.py +0 -8
  19. classiq/interface/ast_node.py +12 -2
  20. classiq/interface/backend/backend_preferences.py +25 -1
  21. classiq/interface/backend/quantum_backend_providers.py +4 -4
  22. classiq/interface/executor/execution_preferences.py +4 -59
  23. classiq/interface/executor/execution_result.py +22 -1
  24. classiq/interface/generator/arith/arithmetic_expression_validator.py +0 -2
  25. classiq/interface/generator/arith/binary_ops.py +88 -25
  26. classiq/interface/generator/arith/unary_ops.py +28 -19
  27. classiq/interface/generator/expressions/atomic_expression_functions.py +6 -2
  28. classiq/interface/generator/expressions/enums/__init__.py +10 -0
  29. classiq/interface/generator/expressions/enums/classical_enum.py +5 -1
  30. classiq/interface/generator/expressions/expression.py +9 -2
  31. classiq/interface/generator/expressions/qmod_qarray_proxy.py +7 -0
  32. classiq/interface/generator/expressions/qmod_qscalar_proxy.py +0 -1
  33. classiq/interface/generator/expressions/sympy_supported_expressions.py +10 -1
  34. classiq/interface/generator/functions/builtins/internal_operators.py +7 -62
  35. classiq/interface/generator/functions/builtins/open_lib_functions.py +810 -2
  36. classiq/interface/generator/functions/classical_type.py +1 -3
  37. classiq/interface/generator/synthesis_metadata/synthesis_duration.py +0 -4
  38. classiq/interface/model/bind_operation.py +3 -1
  39. classiq/interface/model/call_synthesis_data.py +2 -13
  40. classiq/interface/model/classical_if.py +3 -1
  41. classiq/interface/model/classical_parameter_declaration.py +13 -0
  42. classiq/interface/model/control.py +3 -101
  43. classiq/interface/model/inplace_binary_operation.py +3 -1
  44. classiq/interface/model/invert.py +3 -1
  45. classiq/interface/model/port_declaration.py +8 -1
  46. classiq/interface/model/power.py +3 -1
  47. classiq/interface/model/quantum_expressions/amplitude_loading_operation.py +4 -2
  48. classiq/interface/model/quantum_expressions/arithmetic_operation.py +3 -1
  49. classiq/interface/model/quantum_expressions/quantum_expression.py +11 -1
  50. classiq/interface/model/quantum_function_call.py +4 -10
  51. classiq/interface/model/quantum_function_declaration.py +26 -4
  52. classiq/interface/model/quantum_lambda_function.py +1 -20
  53. classiq/interface/model/quantum_statement.py +9 -2
  54. classiq/interface/model/repeat.py +3 -1
  55. classiq/interface/model/statement_block.py +19 -13
  56. classiq/interface/model/validations/handles_validator.py +8 -2
  57. classiq/interface/model/variable_declaration_statement.py +3 -1
  58. classiq/interface/model/within_apply_operation.py +3 -1
  59. classiq/interface/server/routes.py +0 -5
  60. classiq/qmod/__init__.py +1 -2
  61. classiq/qmod/builtins/classical_execution_primitives.py +22 -2
  62. classiq/qmod/builtins/functions.py +51 -1
  63. classiq/qmod/builtins/operations.py +6 -36
  64. classiq/qmod/declaration_inferrer.py +8 -15
  65. classiq/qmod/native/__init__.py +9 -0
  66. classiq/qmod/native/expression_to_qmod.py +12 -9
  67. classiq/qmod/native/pretty_printer.py +4 -4
  68. classiq/qmod/pretty_print/__init__.py +9 -0
  69. classiq/qmod/pretty_print/expression_to_python.py +221 -0
  70. classiq/qmod/pretty_print/pretty_printer.py +421 -0
  71. classiq/qmod/qmod_parameter.py +7 -4
  72. classiq/qmod/quantum_callable.py +2 -1
  73. classiq/qmod/quantum_expandable.py +4 -3
  74. classiq/qmod/quantum_function.py +4 -16
  75. classiq/qmod/symbolic.py +1 -6
  76. classiq/synthesis.py +15 -16
  77. {classiq-0.40.0.dist-info → classiq-0.41.0.dist-info}/METADATA +5 -4
  78. {classiq-0.40.0.dist-info → classiq-0.41.0.dist-info}/RECORD +79 -76
  79. classiq/interface/model/common_model_types.py +0 -23
  80. classiq/interface/model/quantum_expressions/control_state.py +0 -38
  81. {classiq-0.40.0.dist-info → classiq-0.41.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,421 @@
1
+ from typing import Dict, List, Optional, Tuple
2
+
3
+ import black
4
+
5
+ from classiq.interface.generator.constant import Constant
6
+ from classiq.interface.generator.expressions.expression import Expression
7
+ from classiq.interface.generator.functions.classical_type import (
8
+ ClassicalArray,
9
+ ConcreteClassicalType,
10
+ )
11
+ from classiq.interface.generator.functions.port_declaration import (
12
+ PortDeclarationDirection,
13
+ )
14
+ from classiq.interface.generator.visitor import NodeType, Visitor
15
+ from classiq.interface.model.bind_operation import BindOperation
16
+ from classiq.interface.model.classical_if import ClassicalIf
17
+ from classiq.interface.model.classical_parameter_declaration import (
18
+ ClassicalParameterDeclaration,
19
+ )
20
+ from classiq.interface.model.control import Control
21
+ from classiq.interface.model.handle_binding import (
22
+ HandleBinding,
23
+ SlicedHandleBinding,
24
+ SubscriptHandleBinding,
25
+ )
26
+ from classiq.interface.model.inplace_binary_operation import InplaceBinaryOperation
27
+ from classiq.interface.model.invert import Invert
28
+ from classiq.interface.model.model import Model
29
+ from classiq.interface.model.native_function_definition import NativeFunctionDefinition
30
+ from classiq.interface.model.port_declaration import PortDeclaration
31
+ from classiq.interface.model.power import Power
32
+ from classiq.interface.model.quantum_expressions.amplitude_loading_operation import (
33
+ AmplitudeLoadingOperation,
34
+ )
35
+ from classiq.interface.model.quantum_expressions.arithmetic_operation import (
36
+ ArithmeticOperation,
37
+ )
38
+ from classiq.interface.model.quantum_expressions.quantum_expression import (
39
+ QuantumAssignmentOperation,
40
+ )
41
+ from classiq.interface.model.quantum_function_call import (
42
+ OperandIdentifier,
43
+ QuantumFunctionCall,
44
+ )
45
+ from classiq.interface.model.quantum_function_declaration import (
46
+ PositionalArg,
47
+ QuantumFunctionDeclaration,
48
+ QuantumOperandDeclaration,
49
+ )
50
+ from classiq.interface.model.quantum_lambda_function import QuantumLambdaFunction
51
+ from classiq.interface.model.quantum_type import (
52
+ QuantumBit,
53
+ QuantumBitvector,
54
+ QuantumNumeric,
55
+ )
56
+ from classiq.interface.model.quantum_variable_declaration import (
57
+ QuantumVariableDeclaration,
58
+ )
59
+ from classiq.interface.model.repeat import Repeat
60
+ from classiq.interface.model.statement_block import StatementBlock
61
+ from classiq.interface.model.variable_declaration_statement import (
62
+ VariableDeclarationStatement,
63
+ )
64
+ from classiq.interface.model.within_apply_operation import WithinApply
65
+
66
+ import classiq
67
+ from classiq import Bool, ClassicalList, Integer, Pauli, Real, Struct, StructDeclaration
68
+ from classiq.qmod.pretty_print.expression_to_python import transform_expression
69
+ from classiq.qmod.utilities import DEFAULT_DECIMAL_PRECISION
70
+
71
+
72
+ class VariableDeclarationAssignment(Visitor):
73
+ def __init__(self, pretty_printer: "PythonPrettyPrinter") -> None:
74
+ self.pretty_printer = pretty_printer
75
+
76
+ def visit(self, node: NodeType) -> Tuple[str, Optional[List[str]]]:
77
+ res = super().visit(node)
78
+ if not isinstance(res, tuple):
79
+ raise AssertionError(f"Pretty printing for {type(node)} is not supported ")
80
+ return res # type: ignore[return-value]
81
+
82
+ def visit_QuantumBit(self, qtype: QuantumBit) -> Tuple[str, Optional[List[str]]]:
83
+ return "QBit", None
84
+
85
+ def visit_QuantumBitvector(
86
+ self, qtype: QuantumBitvector
87
+ ) -> Tuple[str, Optional[List[str]]]:
88
+ if qtype.length is not None:
89
+ return "QArray", ["QBit", self.pretty_printer.visit(qtype.length)]
90
+ return "QArray", ["QBit"]
91
+
92
+ def visit_QuantumNumeric(
93
+ self, qtype: QuantumNumeric
94
+ ) -> Tuple[str, Optional[List[str]]]:
95
+ params = []
96
+ if qtype.size is not None:
97
+ assert qtype.is_signed is not None
98
+ assert qtype.fraction_digits is not None
99
+
100
+ params = [
101
+ self.pretty_printer.visit(param)
102
+ for param in [qtype.size, qtype.is_signed, qtype.fraction_digits]
103
+ ]
104
+
105
+ return "QNum", params
106
+
107
+
108
+ class PythonPrettyPrinter(Visitor):
109
+ def __init__(self, decimal_precision: int = DEFAULT_DECIMAL_PRECISION) -> None:
110
+ self._level = 0
111
+ self._decimal_precision = decimal_precision
112
+ self._imports = {"qfunc": 1}
113
+ self._symbolic_imports: Dict[str, int] = dict()
114
+
115
+ def visit(self, node: NodeType) -> str:
116
+ res = super().visit(node)
117
+ if not isinstance(res, str):
118
+ raise AssertionError(f"Pretty printing for {type(node)} is not supported ")
119
+ return res
120
+
121
+ def visit_Model(self, model: Model) -> str:
122
+ struct_decls = [self.visit(decl) for decl in model.types]
123
+ func_defs = [self.visit(func) for func in model.functions]
124
+ constants = [self.visit(const) for const in model.constants]
125
+ classical_code = self.format_classical_code(model.classical_execution_code)
126
+
127
+ code = f"{self.format_imports()}\n\n{self.join_code_parts(*constants, *struct_decls, *func_defs, classical_code)}"
128
+ return black.format_str(code, mode=black.FileMode())
129
+
130
+ def format_classical_code(self, code: str) -> str:
131
+ if not code:
132
+ return ""
133
+ self._imports["cfunc"] = 1
134
+ self.check_execution_primitives(code)
135
+ formatted_code = code.replace("\n", "\n" + self._indent + " ")
136
+ return f"{self._indent}@cfunc\n{self._indent}def cmain() -> None:\n{self._indent} {formatted_code}"
137
+
138
+ def check_execution_primitives(self, code: str) -> None:
139
+ for primitive in dir(classiq.qmod.builtins.classical_execution_primitives):
140
+ if primitive + "(" in code:
141
+ self._imports[primitive] = 1
142
+
143
+ def format_imports(self) -> str:
144
+ imports = f"from classiq import {', '.join(self._imports.keys())}\n"
145
+ symbolic_imports = (
146
+ f"from classiq.qmod.symbolic import {', '.join(self._symbolic_imports.keys())}\n"
147
+ if self._symbolic_imports
148
+ else ""
149
+ )
150
+ return imports + symbolic_imports
151
+
152
+ def join_code_parts(self, *code_parts: str) -> str:
153
+ return "\n".join(code_parts)
154
+
155
+ def visit_Constant(self, constant: Constant) -> str:
156
+ self._imports["QConstant"] = 1
157
+ constant_name = self.visit(constant.name)
158
+ return f'{self._indent}{constant_name} = QConstant("{constant_name}", {self.visit(constant.const_type)}, {self.visit(constant.value)})\n'
159
+
160
+ def _visit_arg_decls(self, func_def: QuantumFunctionDeclaration) -> str:
161
+ return ", ".join(
162
+ self.visit(arg_decl) for arg_decl in func_def.get_positional_arg_decls()
163
+ )
164
+
165
+ def visit_QuantumFunctionDeclaration(
166
+ self, func_decl: QuantumFunctionDeclaration
167
+ ) -> str:
168
+ return (
169
+ f"@qfunc\ndef {func_decl.name}({self._visit_arg_decls(func_decl)}) -> None:"
170
+ )
171
+
172
+ def visit_StructDeclaration(self, struct_decl: StructDeclaration) -> str:
173
+ self._imports["struct"] = 1
174
+ return f"@struct\nclass {struct_decl.name}:\n{self._visit_variables(struct_decl.variables)}\n"
175
+
176
+ def _visit_variables(self, variables: Dict[str, ConcreteClassicalType]) -> str:
177
+ self._level += 1
178
+ variables_str = "".join(
179
+ f"{self._indent}{self.visit(field_name)}: {self.visit(var_decl)};\n"
180
+ for field_name, var_decl in variables.items()
181
+ )
182
+ self._level -= 1
183
+ return variables_str
184
+
185
+ def visit_QuantumVariableDeclaration(
186
+ self, var_decl: QuantumVariableDeclaration
187
+ ) -> str:
188
+ return f"{var_decl.name}: {self.visit(var_decl.quantum_type)}"
189
+
190
+ def visit_PortDeclaration(self, port_decl: PortDeclaration) -> str:
191
+ var = self.visit_QuantumVariableDeclaration(port_decl)
192
+ var_name, var_type = var.split(": ")
193
+ for direction in PortDeclarationDirection:
194
+ if port_decl.direction == PortDeclarationDirection.Inout:
195
+ return var
196
+ if port_decl.direction == direction:
197
+ direction_identifier = direction.name
198
+ self._imports[direction_identifier] = 1
199
+ return f"{var_name}: {direction_identifier}[{var_type}]"
200
+ raise RuntimeError("Should not reach here")
201
+
202
+ def visit_QuantumBit(self, qtype: QuantumBit) -> str:
203
+ self._imports["QBit"] = 1
204
+ return "QBit"
205
+
206
+ def visit_QuantumBitvector(self, qtype: QuantumBitvector) -> str:
207
+ self._imports.update({"QArray": 1, "QBit": 1})
208
+ if qtype.length is not None:
209
+ return f"QArray[QBit, {self.visit(qtype.length)}]"
210
+ return "QArray[QBit]"
211
+
212
+ def visit_QuantumNumeric(self, qtype: QuantumNumeric) -> str:
213
+ params = ""
214
+ self._imports["QNum"] = 1
215
+ if qtype.size is not None:
216
+ assert qtype.is_signed is not None
217
+ assert qtype.fraction_digits is not None
218
+
219
+ params = "[{}]".format(
220
+ ", ".join(
221
+ self.visit(param)
222
+ for param in [qtype.size, qtype.is_signed, qtype.fraction_digits]
223
+ )
224
+ )
225
+
226
+ return f"QNum{params}"
227
+
228
+ def visit_ClassicalParameterDeclaration(
229
+ self, cparam: ClassicalParameterDeclaration
230
+ ) -> str:
231
+ return f"{cparam.name}: {self.visit(cparam.classical_type)}"
232
+
233
+ def visit_Integer(self, ctint: Integer) -> str:
234
+ self._imports["CInt"] = 1
235
+ return "CInt"
236
+
237
+ def visit_Real(self, ctint: Real) -> str:
238
+ self._imports["CReal"] = 1
239
+ return "CReal"
240
+
241
+ def visit_Bool(self, ctbool: Bool) -> str:
242
+ self._imports["CBool"] = 1
243
+ return "CBool"
244
+
245
+ def visit_Pauli(self, ctbool: Pauli) -> str:
246
+ self._imports["Pauli"] = 1
247
+ return "Pauli"
248
+
249
+ def visit_ClassicalList(self, ctlist: ClassicalList) -> str:
250
+ self._imports["CArray"] = 1
251
+ return f"CArray[{self.visit(ctlist.element_type)}]"
252
+
253
+ def visit_ClassicalArray(self, ctarray: ClassicalArray) -> str:
254
+ self._imports["CArray"] = 1
255
+ return f"CArray[{self.visit(ctarray.element_type)}, {ctarray.size}]"
256
+
257
+ def visit_Struct(self, struct: Struct) -> str:
258
+ if struct.name in dir(classiq.qmod.builtins.structs):
259
+ self._imports[struct.name] = 1
260
+ return struct.name
261
+
262
+ def visit_VariableDeclarationStatement(
263
+ self, local_decl: VariableDeclarationStatement
264
+ ) -> str:
265
+ type_name, params = VariableDeclarationAssignment(self).visit(
266
+ local_decl.quantum_type
267
+ )
268
+ self._imports[type_name] = 1
269
+ params = [f'"{local_decl.name}"'] + (params or [])
270
+ param_args = ", ".join(params)
271
+ return f"{self._indent}{self.visit_QuantumVariableDeclaration(local_decl)} = {type_name}({param_args})\n"
272
+
273
+ def _visit_operand_arg_decl(self, arg_decl: PositionalArg) -> str:
274
+ if isinstance(arg_decl, QuantumVariableDeclaration):
275
+ return self.visit(arg_decl.quantum_type)
276
+ if isinstance(arg_decl, ClassicalParameterDeclaration):
277
+ return self.visit(arg_decl.classical_type)
278
+ if isinstance(arg_decl, QuantumOperandDeclaration):
279
+ return self.visit_QuantumOperandDeclaration(arg_decl)
280
+
281
+ def visit_QuantumOperandDeclaration(
282
+ self, op_decl: QuantumOperandDeclaration
283
+ ) -> str:
284
+ qcallable_identifier = "QCallable"
285
+ if op_decl.is_list:
286
+ qcallable_identifier = "QCallableList"
287
+ self._imports[qcallable_identifier] = 1
288
+ args = ", ".join(
289
+ self._visit_operand_arg_decl(arg_decl)
290
+ for arg_decl in op_decl.get_positional_arg_decls()
291
+ )
292
+
293
+ return f"{op_decl.name}: {qcallable_identifier}" + (f"[{args}]" if args else "")
294
+
295
+ def visit_NativeFunctionDefinition(self, func_def: NativeFunctionDefinition) -> str:
296
+ self._level += 1
297
+ body = "".join(self.visit(statement) for statement in func_def.body)
298
+ self._level -= 1
299
+ return f"{self.visit_QuantumFunctionDeclaration(func_def)} \n{body}\n"
300
+
301
+ def visit_QuantumFunctionCall(self, func_call: QuantumFunctionCall) -> str:
302
+ if len(func_call.get_positional_args()) <= 2:
303
+ args = ", ".join(self.visit(arg) for arg in func_call.get_positional_args())
304
+ else:
305
+ args = ", ".join(
306
+ f"{self.visit(arg_decl.name)}={self.visit(arg)}"
307
+ for arg_decl, arg in zip(
308
+ func_call.func_decl.get_positional_arg_decls(),
309
+ func_call.get_positional_args(),
310
+ )
311
+ )
312
+ if func_call.func_name in dir(classiq.qmod.builtins.functions):
313
+ self._imports[func_call.func_name] = 1
314
+ return f"{self._indent}{func_call.func_name}{f'[{self.visit(func_call.function.index)}]' if isinstance(func_call.function, OperandIdentifier) else ''}({args})\n"
315
+
316
+ def visit_Control(self, op: Control) -> str:
317
+ self._imports["control"] = 1
318
+ return f"{self._indent}control({self.visit(op.expression)}, {self._visit_body(op.body)})\n"
319
+
320
+ def visit_ClassicalIf(self, op: ClassicalIf) -> str:
321
+ self._imports["if_"] = 1
322
+ return f"{self._indent}if_(condition={self.visit(op.condition)}, then={self._visit_body(op.then)}, else_={self._visit_body(op.else_)})\n"
323
+
324
+ def visit_WithinApply(self, op: WithinApply) -> str:
325
+ self._imports["within_apply"] = 1
326
+ return f"{self._indent}within_apply({self._visit_body(op.compute)}, {self._visit_body(op.action)})\n"
327
+
328
+ def visit_Repeat(self, repeat: Repeat) -> str:
329
+ self._imports["repeat"] = 1
330
+ return f"{self._indent}repeat({self.visit(repeat.count)}, {self._visit_body(repeat.body, [repeat.iter_var])})\n"
331
+
332
+ def visit_Power(self, power: Power) -> str:
333
+ self._imports["power"] = 1
334
+ return f"{self._indent}power({self.visit(power.power)}, {self._visit_body(power.body)})\n"
335
+
336
+ def visit_Invert(self, invert: Invert) -> str:
337
+ self._imports["invert"] = 1
338
+ return f"{self._indent}invert({self._visit_body(invert.body)})\n"
339
+
340
+ def _visit_body(
341
+ self, body: StatementBlock, operand_arguments: Optional[List[str]] = None
342
+ ) -> str:
343
+ argument_string = (
344
+ (" " + ", ".join(operand_arguments))
345
+ if operand_arguments is not None
346
+ else ""
347
+ )
348
+ code = f"lambda{argument_string}: {'[' if len(body) > 1 else ''}\n"
349
+ self._level += 1
350
+ for i, statement in enumerate(body):
351
+ if isinstance(
352
+ statement, (QuantumAssignmentOperation, VariableDeclarationStatement)
353
+ ):
354
+ raise AssertionError(
355
+ "pretty printing quantum assignment operations or variable declaration statements in quantum lambda function is unsupported."
356
+ )
357
+ code += self.visit(statement)
358
+ if i < len(body) - 1:
359
+ code += ","
360
+ self._level -= 1
361
+ return f"{code}{']' if len(body) > 1 else ''}"
362
+
363
+ def visit_InplaceBinaryOperation(self, op: InplaceBinaryOperation) -> str:
364
+ self._imports[op.operation.value] = 1
365
+ return (
366
+ f"{self._indent}{op.operation.value}({op.value.name}, {op.target.name})\n"
367
+ )
368
+
369
+ def visit_Expression(self, expr: Expression) -> str:
370
+ return transform_expression(
371
+ expr.expr,
372
+ level=self._level,
373
+ decimal_precision=self._decimal_precision,
374
+ imports=self._imports,
375
+ symbolic_imports=self._symbolic_imports,
376
+ )
377
+
378
+ def visit_QuantumLambdaFunction(self, qlambda: QuantumLambdaFunction) -> str:
379
+ assert qlambda.func_decl is not None
380
+ return self._visit_body(
381
+ qlambda.body,
382
+ [
383
+ self.visit(arg_decl.name)
384
+ for arg_decl in qlambda.func_decl.get_positional_arg_decls()
385
+ ],
386
+ )
387
+
388
+ def visit_HandleBinding(self, var_ref: HandleBinding) -> str:
389
+ return var_ref.name
390
+
391
+ def visit_SlicedHandleBinding(self, var_ref: SlicedHandleBinding) -> str:
392
+ return f"{var_ref.name}[{self.visit(var_ref.start)}:{self.visit(var_ref.end)}]"
393
+
394
+ def visit_SubscriptHandleBinding(self, var_ref: SubscriptHandleBinding) -> str:
395
+ return f"{var_ref.name}[{self.visit(var_ref.index)}]"
396
+
397
+ def visit_ArithmeticOperation(self, arith_op: ArithmeticOperation) -> str:
398
+ op = "^=" if arith_op.inplace_result else "|="
399
+ return f"{self._indent}{self.visit(arith_op.result_var)} {op} {self.visit(arith_op.expression)}\n"
400
+
401
+ def visit_AmplitudeLoadingOperation(
402
+ self, amplitude_loading_op: AmplitudeLoadingOperation
403
+ ) -> str:
404
+ return f"{self._indent}{self.visit(amplitude_loading_op.result_var)} *= {self.visit(amplitude_loading_op.expression)}\n"
405
+
406
+ def _print_bind_handles(self, handles: List[HandleBinding]) -> str:
407
+ if len(handles) == 1:
408
+ return self.visit(handles[0])
409
+
410
+ return "[" + ", ".join(self.visit(handle) for handle in handles) + "]"
411
+
412
+ def visit_BindOperation(self, bind_op: BindOperation) -> str:
413
+ self._imports["bind"] = 1
414
+ return f"{self._indent}bind({self._print_bind_handles(bind_op.in_handles)}, {self._print_bind_handles(bind_op.out_handles)})\n"
415
+
416
+ def visit_list(self, node: list) -> str:
417
+ return "[" + ", ".join(self.visit(elem) for elem in node) + "]"
418
+
419
+ @property
420
+ def _indent(self) -> str:
421
+ return " " * self._level
@@ -35,10 +35,6 @@ class CParam(SymbolicSuperclass):
35
35
  pass
36
36
 
37
37
 
38
- class QParam(CParam, Generic[_T]):
39
- pass
40
-
41
-
42
38
  class CInt(CParam):
43
39
  pass
44
40
 
@@ -85,6 +81,13 @@ class CParamList(CParam):
85
81
  self._list_type = list_type
86
82
 
87
83
  def __getitem__(self, key: Any) -> CParam:
84
+ if isinstance(key, slice):
85
+ start = key.start if key.start is not None else ""
86
+ stop = key.stop if key.stop is not None else ""
87
+ if key.step is not None:
88
+ key = f"{start}:{key.step}:{stop}"
89
+ else:
90
+ key = f"{start}:{stop}"
88
91
  return create_param(
89
92
  f"({self})[{key}]",
90
93
  self._list_type.element_type,
@@ -41,10 +41,11 @@ class QExpandableInterface(ABC):
41
41
 
42
42
  class QCallable(Generic[P], ABC):
43
43
  CURRENT_EXPANDABLE: ClassVar[Optional[QExpandableInterface]] = None
44
+ FRAME_DEPTH = 1
44
45
 
45
46
  def __call__(self, *args: Any, **kwargs: Any) -> None:
46
47
  assert QCallable.CURRENT_EXPANDABLE is not None
47
- source_ref = get_source_ref(sys._getframe(1))
48
+ source_ref = get_source_ref(sys._getframe(self.FRAME_DEPTH))
48
49
  QCallable.CURRENT_EXPANDABLE.append_statement_to_body(
49
50
  self.create_quantum_function_call(source_ref, *args, **kwargs)
50
51
  )
@@ -81,8 +81,9 @@ class QExpandable(QCallable, QExpandableInterface, ABC):
81
81
  )
82
82
 
83
83
  def expand(self) -> None:
84
- with self:
85
- self._py_callable(*self._get_positional_args())
84
+ if self not in QExpandable.STACK:
85
+ with self:
86
+ self._py_callable(*self._get_positional_args())
86
87
 
87
88
  def infer_rename_params(self) -> Dict[str, str]:
88
89
  return {}
@@ -175,7 +176,7 @@ class QTerminalCallable(QCallable):
175
176
  def len(self) -> CParamScalar:
176
177
  if not self.is_list:
177
178
  raise ClassiqValueError("Cannot get length of a non-list operand")
178
- return CParamScalar(f"{self.func_decl.name}.len")
179
+ return CParamScalar(f"get_field({self.func_decl.name}, 'len')")
179
180
 
180
181
  @property
181
182
  def func_decl(self) -> QuantumFunctionDeclaration:
@@ -1,6 +1,5 @@
1
1
  import ast
2
2
  import functools
3
- import warnings
4
3
  from inspect import isclass
5
4
  from typing import Any, Callable, Dict, List, Optional, Tuple, get_origin
6
5
 
@@ -18,7 +17,7 @@ from classiq.exceptions import ClassiqError
18
17
  from classiq.qmod.classical_function import CFunc
19
18
  from classiq.qmod.declaration_inferrer import infer_func_decl
20
19
  from classiq.qmod.qmod_constant import QConstant
21
- from classiq.qmod.qmod_parameter import CArray, CParam, QParam
20
+ from classiq.qmod.qmod_parameter import CArray, CParam
22
21
  from classiq.qmod.qmod_variable import QVar
23
22
  from classiq.qmod.quantum_callable import QCallable, QCallableList
24
23
  from classiq.qmod.quantum_expandable import QExpandable, QTerminalCallable
@@ -47,20 +46,10 @@ def create_model(
47
46
 
48
47
 
49
48
  class QFunc(QExpandable):
49
+ FRAME_DEPTH = 2
50
+
50
51
  def __init__(self, py_callable: Callable) -> None:
51
52
  _validate_no_gen_params(py_callable.__annotations__)
52
- if any(
53
- get_origin(annotation) == QParam
54
- for annotation in py_callable.__annotations__.values()
55
- ):
56
- warnings.warn(
57
- "QParam is deprecated and will be removed in a future release. Use the "
58
- "new classical parameter types instead:\n * QParam[int] -> CInt\n * QParam[float] "
59
- "-> CReal\n * QParam[bool] -> CBool\n * QParam[list[int]] -> "
60
- "CArray[CInt]",
61
- category=DeprecationWarning,
62
- stacklevel=4,
63
- )
64
53
  super().__init__(py_callable)
65
54
  functools.update_wrapper(self, py_callable)
66
55
 
@@ -109,7 +98,7 @@ class QFunc(QExpandable):
109
98
  return
110
99
  self.expand()
111
100
  self._qmodule.native_defs[self.func_decl.name] = NativeFunctionDefinition(
112
- **self.func_decl.__dict__, body=self.body
101
+ **{**self.func_decl.dict(), **{"body": self.body}}
113
102
  )
114
103
 
115
104
  def _add_constants_from_classical_code(
@@ -174,7 +163,6 @@ def _validate_no_gen_params(annotations: Dict[str, Any]) -> None:
174
163
  for name, annotation in annotations.items()
175
164
  if not (
176
165
  name == "return"
177
- or get_origin(annotation) is QParam
178
166
  or isclass(annotation)
179
167
  and issubclass(annotation, CParam)
180
168
  or isclass(annotation)
classiq/qmod/symbolic.py CHANGED
@@ -7,15 +7,13 @@ from typing import (
7
7
  Tuple,
8
8
  Type,
9
9
  TypeVar,
10
- get_args,
11
- get_origin,
12
10
  overload,
13
11
  )
14
12
 
15
13
  from classiq.exceptions import ClassiqValueError
16
14
  from classiq.qmod import model_state_container
17
15
  from classiq.qmod.declaration_inferrer import python_type_to_qmod
18
- from classiq.qmod.qmod_parameter import CParam, CParamScalar, QParam, create_param
16
+ from classiq.qmod.qmod_parameter import CParam, CParamScalar, create_param
19
17
  from classiq.qmod.symbolic_expr import SymbolicExpr
20
18
  from classiq.qmod.symbolic_type import SymbolicTypes
21
19
 
@@ -47,9 +45,6 @@ def symbolic_function(*args: Any, return_type: Optional[Type[T]] = None) -> CPar
47
45
  if return_type is None:
48
46
  return CParamScalar(expr)
49
47
 
50
- if get_origin(return_type) is QParam:
51
- return_type = get_args(return_type)[0]
52
-
53
48
  if TYPE_CHECKING:
54
49
  assert return_type is not None
55
50
 
classiq/synthesis.py CHANGED
@@ -5,8 +5,7 @@ import pydantic
5
5
  from classiq.interface.executor.execution_preferences import ExecutionPreferences
6
6
  from classiq.interface.generator.model.constraints import Constraints
7
7
  from classiq.interface.generator.model.preferences.preferences import Preferences
8
- from classiq.interface.model.common_model_types import ModelInput, SerializedModelInput
9
- from classiq.interface.model.model import SerializedModel
8
+ from classiq.interface.model.model import Model, SerializedModel
10
9
 
11
10
  from classiq._internals.api_wrapper import ApiWrapper
12
11
  from classiq._internals.async_utils import syncify_function
@@ -15,9 +14,9 @@ SerializedQuantumProgram = NewType("SerializedQuantumProgram", str)
15
14
 
16
15
 
17
16
  async def synthesize_async(
18
- serialized_model: SerializedModelInput,
17
+ serialized_model: SerializedModel,
19
18
  ) -> SerializedQuantumProgram:
20
- model: ModelInput = pydantic.parse_raw_as(ModelInput, serialized_model) # type: ignore[arg-type]
19
+ model = pydantic.parse_raw_as(Model, serialized_model)
21
20
  quantum_program = await ApiWrapper.call_generation_task(model)
22
21
  return SerializedQuantumProgram(quantum_program.json(indent=2))
23
22
 
@@ -26,27 +25,27 @@ synthesize = syncify_function(synthesize_async)
26
25
 
27
26
 
28
27
  def set_preferences(
29
- serialized_model: SerializedModelInput, preferences: Preferences
30
- ) -> SerializedModelInput:
31
- model: ModelInput = pydantic.parse_raw_as(ModelInput, serialized_model) # type: ignore[arg-type]
28
+ serialized_model: SerializedModel, preferences: Preferences
29
+ ) -> SerializedModel:
30
+ model = pydantic.parse_raw_as(Model, serialized_model)
32
31
  model.preferences = preferences
33
- return model.get_model() # type: ignore[return-value]
32
+ return model.get_model()
34
33
 
35
34
 
36
35
  def set_constraints(
37
- serialized_model: SerializedModelInput, constraints: Constraints
38
- ) -> SerializedModelInput:
39
- model: ModelInput = pydantic.parse_raw_as(ModelInput, serialized_model) # type: ignore[arg-type]
36
+ serialized_model: SerializedModel, constraints: Constraints
37
+ ) -> SerializedModel:
38
+ model = pydantic.parse_raw_as(Model, serialized_model)
40
39
  model.constraints = constraints
41
- return model.get_model() # type: ignore[return-value]
40
+ return model.get_model()
42
41
 
43
42
 
44
43
  def set_execution_preferences(
45
- serialized_model: SerializedModelInput, execution_preferences: ExecutionPreferences
46
- ) -> SerializedModelInput:
47
- model: ModelInput = pydantic.parse_raw_as(ModelInput, serialized_model) # type: ignore[arg-type]
44
+ serialized_model: SerializedModel, execution_preferences: ExecutionPreferences
45
+ ) -> SerializedModel:
46
+ model = pydantic.parse_raw_as(Model, serialized_model)
48
47
  model.execution_preferences = execution_preferences
49
- return model.get_model() # type: ignore[return-value]
48
+ return model.get_model()
50
49
 
51
50
 
52
51
  __all__ = [
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: classiq
3
- Version: 0.40.0
3
+ Version: 0.41.0
4
4
  Summary: Classiq's Python SDK for quantum computing
5
5
  Home-page: https://classiq.io
6
6
  License: Proprietary
@@ -31,6 +31,7 @@ Provides-Extra: analyzer-sdk
31
31
  Provides-Extra: qml
32
32
  Requires-Dist: ConfigArgParse (>=1.5.3,<2.0.0)
33
33
  Requires-Dist: Pyomo (>=6.5,<6.6)
34
+ Requires-Dist: black (>=24.0,<25.0)
34
35
  Requires-Dist: httpx (>=0.23.0,<1)
35
36
  Requires-Dist: ipywidgets (>=7.7.1,<8.0.0) ; extra == "analyzer-sdk"
36
37
  Requires-Dist: keyring (>=23.5.0,<24.0.0)
@@ -38,11 +39,11 @@ Requires-Dist: matplotlib (>=3.4.3,<4.0.0)
38
39
  Requires-Dist: networkx (>=2.5.1,<3.0.0)
39
40
  Requires-Dist: numexpr (>=2.7.3,<3.0.0)
40
41
  Requires-Dist: numpy (>=1.20.1,<2.0.0)
41
- Requires-Dist: packaging (>=21.3,<22.0)
42
- Requires-Dist: pandas (>=1.4.0,<2.0.0)
42
+ Requires-Dist: packaging (>=22.0,<23.0)
43
+ Requires-Dist: pandas (>=1.4.0,<3.0.0)
43
44
  Requires-Dist: plotly (>=5.7.0,<6.0.0)
44
45
  Requires-Dist: pydantic (>=1.9.1,<2.0.0)
45
- Requires-Dist: scipy (==1.10.1)
46
+ Requires-Dist: scipy (>=1.10.1,<2.0.0)
46
47
  Requires-Dist: sympy (>=1.9.0,<1.11.0)
47
48
  Requires-Dist: tabulate (>=0.8.9,<1)
48
49
  Requires-Dist: torch (>=2.0,<3.0) ; extra == "qml"