classiq 0.42.2__py3-none-any.whl → 0.43.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. classiq/__init__.py +2 -6
  2. classiq/_internals/api_wrapper.py +6 -12
  3. classiq/_internals/authentication/token_manager.py +5 -2
  4. classiq/_internals/jobs.py +5 -10
  5. classiq/analyzer/rb.py +3 -3
  6. classiq/applications/chemistry/chemistry_model_constructor.py +12 -8
  7. classiq/applications/combinatorial_helpers/pauli_helpers/pauli_utils.py +2 -2
  8. classiq/applications/finance/finance_model_constructor.py +16 -13
  9. classiq/applications/qsvm/__init__.py +1 -3
  10. classiq/applications/qsvm/qsvm_model_constructor.py +7 -6
  11. classiq/exceptions.py +9 -4
  12. classiq/execution/execution_session.py +5 -2
  13. classiq/execution/qnn.py +1 -1
  14. classiq/executor.py +0 -2
  15. classiq/interface/_version.py +1 -1
  16. classiq/interface/chemistry/operator.py +19 -5
  17. classiq/interface/executor/constants.py +1 -0
  18. classiq/interface/finance/function_input.py +16 -10
  19. classiq/interface/generator/application_apis/chemistry_declarations.py +2 -2
  20. classiq/interface/generator/application_apis/qsvm_declarations.py +4 -2
  21. classiq/interface/generator/arith/argument_utils.py +20 -3
  22. classiq/interface/generator/arith/arithmetic_expression_validator.py +3 -26
  23. classiq/interface/generator/arith/binary_ops.py +8 -14
  24. classiq/interface/generator/arith/extremum_operations.py +30 -0
  25. classiq/interface/generator/arith/number_utils.py +1 -1
  26. classiq/interface/generator/arith/unary_ops.py +1 -3
  27. classiq/interface/generator/compiler_keywords.py +1 -1
  28. classiq/interface/generator/expressions/atomic_expression_functions.py +13 -3
  29. classiq/interface/generator/expressions/enums/__init__.py +0 -20
  30. classiq/interface/generator/expressions/enums/finance_functions.py +11 -18
  31. classiq/interface/generator/expressions/non_symbolic_expr.py +119 -0
  32. classiq/interface/generator/expressions/qmod_qarray_proxy.py +52 -37
  33. classiq/interface/generator/expressions/qmod_qscalar_proxy.py +16 -11
  34. classiq/interface/generator/expressions/qmod_sized_proxy.py +5 -5
  35. classiq/interface/generator/function_param_list_without_self_reference.py +0 -10
  36. classiq/interface/generator/function_params.py +0 -4
  37. classiq/interface/generator/functions/__init__.py +0 -20
  38. classiq/interface/generator/functions/builtins/core_library/exponentiation_functions.py +2 -2
  39. classiq/interface/generator/functions/builtins/open_lib_functions.py +530 -1
  40. classiq/interface/generator/functions/classical_type.py +22 -69
  41. classiq/interface/generator/functions/port_declaration.py +0 -11
  42. classiq/interface/generator/model/__init__.py +0 -1
  43. classiq/interface/generator/model/model.py +9 -185
  44. classiq/interface/generator/synthesis_metadata/synthesis_execution_data.py +3 -1
  45. classiq/interface/generator/types/builtin_enum_declarations.py +69 -0
  46. classiq/interface/generator/types/builtin_struct_declarations/pauli_struct_declarations.py +2 -2
  47. classiq/interface/generator/types/enum_declaration.py +57 -0
  48. classiq/interface/jobs.py +36 -65
  49. classiq/interface/model/bind_operation.py +3 -0
  50. classiq/interface/model/classical_parameter_declaration.py +3 -0
  51. classiq/interface/model/handle_binding.py +7 -0
  52. classiq/interface/model/inplace_binary_operation.py +13 -15
  53. classiq/interface/model/model.py +8 -20
  54. classiq/interface/model/native_function_definition.py +0 -17
  55. classiq/interface/model/quantum_function_call.py +63 -182
  56. classiq/interface/model/quantum_type.py +71 -10
  57. classiq/interface/server/routes.py +0 -6
  58. classiq/qmod/__init__.py +3 -3
  59. classiq/qmod/builtins/__init__.py +10 -1
  60. classiq/qmod/builtins/classical_execution_primitives.py +4 -2
  61. classiq/qmod/builtins/enums.py +177 -0
  62. classiq/qmod/builtins/functions.py +1 -2
  63. classiq/qmod/builtins/operations.py +2 -4
  64. classiq/qmod/builtins/structs.py +16 -17
  65. classiq/qmod/declaration_inferrer.py +23 -20
  66. classiq/qmod/model_state_container.py +2 -0
  67. classiq/qmod/native/pretty_printer.py +31 -13
  68. classiq/qmod/pretty_print/pretty_printer.py +52 -27
  69. classiq/qmod/qmod_constant.py +7 -3
  70. classiq/qmod/qmod_parameter.py +2 -1
  71. classiq/qmod/qmod_struct.py +9 -33
  72. classiq/qmod/qmod_variable.py +55 -22
  73. classiq/qmod/quantum_callable.py +6 -1
  74. classiq/qmod/quantum_expandable.py +29 -11
  75. classiq/qmod/quantum_function.py +8 -4
  76. classiq/qmod/semantics/annotation.py +38 -0
  77. classiq/qmod/semantics/error_manager.py +49 -0
  78. classiq/qmod/semantics/static_semantics_visitor.py +308 -0
  79. classiq/qmod/semantics/validation/func_call_validation.py +149 -0
  80. classiq/qmod/semantics/validation/types_validation.py +21 -0
  81. classiq/qmod/symbolic.py +6 -6
  82. classiq/qmod/symbolic_expr.py +26 -11
  83. classiq/qmod/utilities.py +23 -1
  84. {classiq-0.42.2.dist-info → classiq-0.43.1.dist-info}/METADATA +2 -2
  85. {classiq-0.42.2.dist-info → classiq-0.43.1.dist-info}/RECORD +88 -103
  86. classiq/_internals/_qfunc_ext.py +0 -6
  87. classiq/applications/libraries/ampltitude_estimation_library.py +0 -11
  88. classiq/interface/generator/credit_risk_example/linear_gci.py +0 -122
  89. classiq/interface/generator/credit_risk_example/weighted_adder.py +0 -69
  90. classiq/interface/generator/expressions/enums/chemistry.py +0 -28
  91. classiq/interface/generator/expressions/enums/classical_enum.py +0 -20
  92. classiq/interface/generator/expressions/enums/ladder_operator.py +0 -6
  93. classiq/interface/generator/expressions/enums/optimizers.py +0 -9
  94. classiq/interface/generator/expressions/enums/pauli.py +0 -8
  95. classiq/interface/generator/expressions/enums/qsvm_feature_map_entanglement.py +0 -9
  96. classiq/interface/generator/functions/foreign_function_definition.py +0 -114
  97. classiq/interface/generator/functions/function_implementation.py +0 -107
  98. classiq/interface/generator/functions/native_function_definition.py +0 -155
  99. classiq/interface/generator/functions/quantum_function_declaration.py +0 -69
  100. classiq/interface/generator/functions/register.py +0 -44
  101. classiq/interface/generator/functions/register_mapping_data.py +0 -106
  102. classiq/interface/generator/inequality_mixer.py +0 -51
  103. classiq/interface/generator/model/classical_main_validator.py +0 -106
  104. classiq/interface/generator/range_mixer.py +0 -56
  105. classiq/interface/generator/state_propagator.py +0 -74
  106. classiq/interface/model/resolvers/function_call_resolver.py +0 -64
  107. classiq/interface/model/validations/__init__.py +0 -0
  108. classiq/interface/model/validations/handle_validation_base.py +0 -55
  109. classiq/interface/model/validations/handles_validator.py +0 -153
  110. classiq/interface/model/validations/port_to_wire_name_generator.py +0 -12
  111. /classiq/{interface/generator/credit_risk_example → qmod/semantics}/__init__.py +0 -0
  112. /classiq/{interface/model/resolvers → qmod/semantics/validation}/__init__.py +0 -0
  113. {classiq-0.42.2.dist-info → classiq-0.43.1.dist-info}/WHEEL +0 -0
@@ -5,10 +5,12 @@ from classiq.interface.generator.expressions.expression import Expression
5
5
  from classiq.interface.generator.functions.classical_type import (
6
6
  ClassicalArray,
7
7
  ConcreteClassicalType,
8
+ TypeName,
8
9
  )
9
10
  from classiq.interface.generator.functions.port_declaration import (
10
11
  PortDeclarationDirection,
11
12
  )
13
+ from classiq.interface.generator.types.enum_declaration import EnumDeclaration
12
14
  from classiq.interface.generator.visitor import NodeType, Visitor
13
15
  from classiq.interface.model.bind_operation import BindOperation
14
16
  from classiq.interface.model.classical_if import ClassicalIf
@@ -57,8 +59,11 @@ from classiq.interface.model.variable_declaration_statement import (
57
59
  )
58
60
  from classiq.interface.model.within_apply_operation import WithinApply
59
61
 
60
- from classiq import Bool, ClassicalList, Integer, Pauli, Real, Struct, StructDeclaration
62
+ from classiq import Bool, ClassicalList, Integer, Real, StructDeclaration
61
63
  from classiq.qmod.native.expression_to_qmod import transform_expression
64
+ from classiq.qmod.semantics.static_semantics_visitor import (
65
+ static_semantics_analysis_pass,
66
+ )
62
67
  from classiq.qmod.utilities import DEFAULT_DECIMAL_PRECISION
63
68
 
64
69
 
@@ -76,6 +81,9 @@ class DSLPrettyPrinter(Visitor):
76
81
  return res
77
82
 
78
83
  def visit_Model(self, model: Model) -> str:
84
+ # FIXME - CAD-20149: Remove this line once the froggies are removed, and the visit of lambdas can be done without accessing the func_decl property (with rename_params values only).
85
+ static_semantics_analysis_pass(model)
86
+ enum_decls = [self.visit(enum_decl) for enum_decl in model.enums]
79
87
  struct_decls = [self.visit(struct_decl) for struct_decl in model.types]
80
88
  func_defs = [self.visit(func_def) for func_def in model.functions]
81
89
  constants = [self.visit(constant) for constant in model.constants]
@@ -84,7 +92,9 @@ class DSLPrettyPrinter(Visitor):
84
92
  if model.classical_execution_code
85
93
  else ""
86
94
  )
87
- return "\n".join([*constants, *struct_decls, *func_defs, classical_code])
95
+ return "\n".join(
96
+ [*constants, *enum_decls, *struct_decls, *func_defs, classical_code]
97
+ )
88
98
 
89
99
  def visit_Constant(self, constant: Constant) -> str:
90
100
  return f"{self._indent}{self.visit(constant.name)}: {self.visit(constant.const_type)} = {self.visit(constant.value)};\n"
@@ -108,6 +118,18 @@ class DSLPrettyPrinter(Visitor):
108
118
  ) -> str:
109
119
  return f"qfunc {func_decl.name}{self._visit_arg_decls(func_decl)}"
110
120
 
121
+ def visit_EnumDeclaration(self, enum_decl: EnumDeclaration) -> str:
122
+ return f"enum {enum_decl.name} {{\n{self._visit_members(enum_decl.members)}}}\n"
123
+
124
+ def _visit_members(self, members: Dict[str, int]) -> str:
125
+ self._level += 1
126
+ members_str = "".join(
127
+ f"{self._indent}{self.visit(member_name)} = {member_value};\n"
128
+ for member_name, member_value in members.items()
129
+ )
130
+ self._level -= 1
131
+ return members_str
132
+
111
133
  def visit_StructDeclaration(self, struct_decl: StructDeclaration) -> str:
112
134
  return f"struct {struct_decl.name} {{\n{self._visit_variables(struct_decl.variables)}}}\n"
113
135
 
@@ -137,9 +159,10 @@ class DSLPrettyPrinter(Visitor):
137
159
  return "qbit"
138
160
 
139
161
  def visit_QuantumBitvector(self, qtype: QuantumBitvector) -> str:
162
+ element_type = self.visit(qtype.element_type)
140
163
  if qtype.length is not None:
141
- return f"qbit[{self.visit(qtype.length)}]"
142
- return "qbit[]"
164
+ return f"{element_type}[{self.visit(qtype.length)}]"
165
+ return f"{element_type}[]"
143
166
 
144
167
  def visit_QuantumNumeric(self, qtype: QuantumNumeric) -> str:
145
168
  params = ""
@@ -170,17 +193,14 @@ class DSLPrettyPrinter(Visitor):
170
193
  def visit_Bool(self, ctbool: Bool) -> str:
171
194
  return "bool"
172
195
 
173
- def visit_Pauli(self, ctbool: Pauli) -> str:
174
- return "Pauli"
175
-
176
196
  def visit_ClassicalList(self, ctlist: ClassicalList) -> str:
177
197
  return f"{self.visit(ctlist.element_type)}[]"
178
198
 
179
199
  def visit_ClassicalArray(self, ctarray: ClassicalArray) -> str:
180
200
  return f"{self.visit(ctarray.element_type)}[{ctarray.size}]"
181
201
 
182
- def visit_Struct(self, struct: Struct) -> str:
183
- return struct.name
202
+ def visit_TypeName(self, type_: TypeName) -> str:
203
+ return type_.name
184
204
 
185
205
  def visit_VariableDeclarationStatement(
186
206
  self, local_decl: VariableDeclarationStatement
@@ -266,9 +286,7 @@ class DSLPrettyPrinter(Visitor):
266
286
  return code
267
287
 
268
288
  def visit_InplaceBinaryOperation(self, op: InplaceBinaryOperation) -> str:
269
- return (
270
- f"{self._indent}{op.operation.value}({op.value.name}, {op.target.name});\n"
271
- )
289
+ return f"{self._indent}{op.operation.value}({self.visit(op.value)}, {self.visit(op.target)});\n"
272
290
 
273
291
  def _visit_pack_expr(self, vars: List[HandleBinding]) -> str:
274
292
  if len(vars) == 1:
@@ -290,7 +308,7 @@ class DSLPrettyPrinter(Visitor):
290
308
  if not isinstance(arg_decl, PortDeclaration)
291
309
  )
292
310
  quantum_args = ", ".join(
293
- arg_decl.name
311
+ qlambda.rename_params.get(arg_decl.name, arg_decl.name)
294
312
  for arg_decl in qlambda.func_decl.get_positional_arg_decls()
295
313
  if isinstance(arg_decl, PortDeclaration)
296
314
  )
@@ -7,10 +7,12 @@ from classiq.interface.generator.expressions.expression import Expression
7
7
  from classiq.interface.generator.functions.classical_type import (
8
8
  ClassicalArray,
9
9
  ConcreteClassicalType,
10
+ TypeName,
10
11
  )
11
12
  from classiq.interface.generator.functions.port_declaration import (
12
13
  PortDeclarationDirection,
13
14
  )
15
+ from classiq.interface.generator.types.enum_declaration import EnumDeclaration
14
16
  from classiq.interface.generator.visitor import NodeType, Visitor
15
17
  from classiq.interface.model.bind_operation import BindOperation
16
18
  from classiq.interface.model.classical_if import ClassicalIf
@@ -64,7 +66,7 @@ from classiq.interface.model.variable_declaration_statement import (
64
66
  from classiq.interface.model.within_apply_operation import WithinApply
65
67
 
66
68
  import classiq
67
- from classiq import Bool, ClassicalList, Integer, Pauli, Real, Struct, StructDeclaration
69
+ from classiq import Bool, ClassicalList, Integer, Real, StructDeclaration
68
70
  from classiq.qmod.pretty_print.expression_to_python import transform_expression
69
71
  from classiq.qmod.utilities import DEFAULT_DECIMAL_PRECISION
70
72
 
@@ -110,7 +112,10 @@ class PythonPrettyPrinter(Visitor):
110
112
  self._level = 0
111
113
  self._decimal_precision = decimal_precision
112
114
  self._imports = {"qfunc": 1}
115
+ self._import_enum = False
116
+ self._import_dataclass = False
113
117
  self._symbolic_imports: Dict[str, int] = dict()
118
+ self._functions: Optional[Dict[str, QuantumFunctionDeclaration]] = None
114
119
 
115
120
  def visit(self, node: NodeType) -> str:
116
121
  res = super().visit(node)
@@ -119,12 +124,17 @@ class PythonPrettyPrinter(Visitor):
119
124
  return res
120
125
 
121
126
  def visit_Model(self, model: Model) -> str:
127
+ self._functions = {
128
+ **model.function_dict,
129
+ **QuantumFunctionDeclaration.BUILTIN_FUNCTION_DECLARATIONS,
130
+ }
131
+ enum_decls = [self.visit(decl) for decl in model.enums]
122
132
  struct_decls = [self.visit(decl) for decl in model.types]
123
133
  func_defs = [self.visit(func) for func in model.functions]
124
134
  constants = [self.visit(const) for const in model.constants]
125
135
  classical_code = self.format_classical_code(model.classical_execution_code)
126
136
 
127
- code = f"{self.format_imports()}\n\n{self.join_code_parts(*constants, *struct_decls, *func_defs, classical_code)}"
137
+ code = f"{self.format_imports()}\n\n{self.join_code_parts(*constants, *enum_decls, *struct_decls, *func_defs, classical_code)}"
128
138
  return black.format_str(code, mode=black.FileMode())
129
139
 
130
140
  def format_classical_code(self, code: str) -> str:
@@ -147,7 +157,16 @@ class PythonPrettyPrinter(Visitor):
147
157
  if self._symbolic_imports
148
158
  else ""
149
159
  )
150
- return imports + symbolic_imports
160
+ return self.special_imports + imports + symbolic_imports
161
+
162
+ @property
163
+ def special_imports(self) -> str:
164
+ imports = ""
165
+ if self._import_dataclass:
166
+ imports += "from dataclasses import dataclass\n"
167
+ if self._import_enum:
168
+ imports += "from enum import IntEnum\n"
169
+ return imports
151
170
 
152
171
  def join_code_parts(self, *code_parts: str) -> str:
153
172
  return "\n".join(code_parts)
@@ -169,9 +188,22 @@ class PythonPrettyPrinter(Visitor):
169
188
  f"@qfunc\ndef {func_decl.name}({self._visit_arg_decls(func_decl)}) -> None:"
170
189
  )
171
190
 
191
+ def visit_EnumDeclaration(self, enum_decl: EnumDeclaration) -> str:
192
+ self._import_enum = True
193
+ return f"class {enum_decl.name}(IntEnum):\n{self._visit_members(enum_decl.members)}\n"
194
+
195
+ def _visit_members(self, members: Dict[str, int]) -> str:
196
+ self._level += 1
197
+ members_str = "".join(
198
+ f"{self._indent}{self.visit(member_name)} = {member_value}\n"
199
+ for member_name, member_value in members.items()
200
+ )
201
+ self._level -= 1
202
+ return members_str
203
+
172
204
  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"
205
+ self._import_dataclass = True
206
+ return f"@dataclass\nclass {struct_decl.name}:\n{self._visit_variables(struct_decl.variables)}\n"
175
207
 
176
208
  def _visit_variables(self, variables: Dict[str, ConcreteClassicalType]) -> str:
177
209
  self._level += 1
@@ -205,9 +237,10 @@ class PythonPrettyPrinter(Visitor):
205
237
 
206
238
  def visit_QuantumBitvector(self, qtype: QuantumBitvector) -> str:
207
239
  self._imports.update({"QArray": 1, "QBit": 1})
240
+ element_type = self.visit(qtype.element_type)
208
241
  if qtype.length is not None:
209
- return f"QArray[QBit, {_add_quotes(self.visit(qtype.length))}]"
210
- return "QArray[QBit]"
242
+ return f"QArray[{element_type}, {_add_quotes(self.visit(qtype.length))}]"
243
+ return f"QArray[{element_type}]"
211
244
 
212
245
  def visit_QuantumNumeric(self, qtype: QuantumNumeric) -> str:
213
246
  params = ""
@@ -242,10 +275,6 @@ class PythonPrettyPrinter(Visitor):
242
275
  self._imports["CBool"] = 1
243
276
  return "CBool"
244
277
 
245
- def visit_Pauli(self, ctbool: Pauli) -> str:
246
- self._imports["Pauli"] = 1
247
- return "Pauli"
248
-
249
278
  def visit_ClassicalList(self, ctlist: ClassicalList) -> str:
250
279
  self._imports["CArray"] = 1
251
280
  return f"CArray[{self.visit(ctlist.element_type)}]"
@@ -254,10 +283,12 @@ class PythonPrettyPrinter(Visitor):
254
283
  self._imports["CArray"] = 1
255
284
  return f"CArray[{self.visit(ctarray.element_type)}, {ctarray.size}]"
256
285
 
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
286
+ def visit_TypeName(self, type_: TypeName) -> str:
287
+ if type_.name in dir(classiq.qmod.builtins.enums) + dir(
288
+ classiq.qmod.builtins.structs
289
+ ):
290
+ self._imports[type_.name] = 1
291
+ return type_.name
261
292
 
262
293
  def visit_VariableDeclarationStatement(
263
294
  self, local_decl: VariableDeclarationStatement
@@ -299,13 +330,14 @@ class PythonPrettyPrinter(Visitor):
299
330
  return f"{self.visit_QuantumFunctionDeclaration(func_def)} \n{body}\n"
300
331
 
301
332
  def visit_QuantumFunctionCall(self, func_call: QuantumFunctionCall) -> str:
302
- if len(func_call.get_positional_args()) <= 2:
333
+ if len(func_call.get_positional_args()) <= 2 or self._functions is None:
303
334
  args = ", ".join(self.visit(arg) for arg in func_call.get_positional_args())
304
335
  else:
336
+ func_decl = self._functions[func_call.func_name]
305
337
  args = ", ".join(
306
338
  f"{self.visit(arg_decl.name)}={self.visit(arg)}"
307
339
  for arg_decl, arg in zip(
308
- func_call.func_decl.get_positional_arg_decls(),
340
+ func_decl.get_positional_arg_decls(),
309
341
  func_call.get_positional_args(),
310
342
  )
311
343
  )
@@ -343,9 +375,7 @@ class PythonPrettyPrinter(Visitor):
343
375
  if len(body) == 0:
344
376
  return "None"
345
377
  argument_string = (
346
- (" " + ", ".join(operand_arguments))
347
- if operand_arguments is not None
348
- else ""
378
+ (" " + ", ".join(operand_arguments)) if operand_arguments else ""
349
379
  )
350
380
  code = f"lambda{argument_string}: {'[' if len(body) > 1 else ''}\n"
351
381
  self._level += 1
@@ -364,9 +394,7 @@ class PythonPrettyPrinter(Visitor):
364
394
 
365
395
  def visit_InplaceBinaryOperation(self, op: InplaceBinaryOperation) -> str:
366
396
  self._imports[op.operation.value] = 1
367
- return (
368
- f"{self._indent}{op.operation.value}({op.value.name}, {op.target.name})\n"
369
- )
397
+ return f"{self._indent}{op.operation.value}({self.visit(op.value)}, {self.visit(op.target)})\n"
370
398
 
371
399
  def visit_Expression(self, expr: Expression) -> str:
372
400
  return transform_expression(
@@ -381,10 +409,7 @@ class PythonPrettyPrinter(Visitor):
381
409
  assert qlambda.func_decl is not None
382
410
  return self._visit_body(
383
411
  qlambda.body,
384
- [
385
- self.visit(arg_decl.name)
386
- for arg_decl in qlambda.func_decl.get_positional_arg_decls()
387
- ],
412
+ list(qlambda.rename_params.values()),
388
413
  )
389
414
 
390
415
  def visit_HandleBinding(self, var_ref: HandleBinding) -> str:
@@ -1,4 +1,5 @@
1
1
  import inspect
2
+ from dataclasses import is_dataclass
2
3
  from typing import Any, Optional
3
4
 
4
5
  from classiq.interface.generator.constant import Constant
@@ -6,7 +7,6 @@ from classiq.interface.generator.expressions.expression import Expression
6
7
  from classiq.interface.generator.functions.classical_type import (
7
8
  ClassicalArray,
8
9
  ClassicalList,
9
- CStructBase,
10
10
  )
11
11
 
12
12
  from classiq.exceptions import ClassiqError
@@ -14,6 +14,7 @@ from classiq.qmod.declaration_inferrer import python_type_to_qmod
14
14
  from classiq.qmod.model_state_container import ModelStateContainer
15
15
  from classiq.qmod.qmod_parameter import CParam, CParamList, CParamStruct
16
16
  from classiq.qmod.symbolic_expr import SymbolicExpr
17
+ from classiq.qmod.utilities import qmod_val_to_expr_str
17
18
 
18
19
 
19
20
  class QConstant(SymbolicExpr):
@@ -34,7 +35,7 @@ class QConstant(SymbolicExpr):
34
35
  "Error trying to add a constant to a model without a current QModule."
35
36
  )
36
37
 
37
- expr = str(self._value)
38
+ expr = qmod_val_to_expr_str(self._value)
38
39
  if (
39
40
  self.name in QConstant.CURRENT_QMODULE.constants
40
41
  and expr != QConstant.CURRENT_QMODULE.constants[self.name].value.expr
@@ -65,11 +66,14 @@ class QConstant(SymbolicExpr):
65
66
  def __getattr__(self, name: str) -> CParam:
66
67
  self.add_to_model()
67
68
 
69
+ if name == "is_quantum":
70
+ return False # type:ignore[return-value]
71
+
68
72
  py_type = type(self._value)
69
73
  if (
70
74
  QConstant.CURRENT_QMODULE is None
71
75
  or not inspect.isclass(py_type)
72
- or not issubclass(py_type, CStructBase)
76
+ or not is_dataclass(py_type)
73
77
  ):
74
78
  return self.__getattribute__(name)
75
79
 
@@ -34,7 +34,8 @@ else:
34
34
 
35
35
 
36
36
  class CParam(SymbolicSuperclass):
37
- pass
37
+ def __init__(self, expr: str) -> None:
38
+ super().__init__(expr, False)
38
39
 
39
40
 
40
41
  class CInt(CParam):
@@ -1,37 +1,13 @@
1
- import dataclasses
2
- from typing import Any, Type
1
+ import warnings
2
+ from dataclasses import dataclass
3
+ from typing import Type
3
4
 
4
- from typing_extensions import dataclass_transform
5
5
 
6
- from classiq.interface.generator.functions.classical_type import CStructBase
7
-
8
-
9
- def _qmod_val_to_expr_str(val: Any) -> str:
10
- if dataclasses.is_dataclass(type(val)):
11
- kwargs_str = ", ".join(
12
- [
13
- f"{field.name}={_qmod_val_to_expr_str(vars(val)[field.name])}"
14
- for field in dataclasses.fields(val)
15
- ]
16
- )
17
- return f"struct_literal({type(val).__name__}, {kwargs_str})"
18
-
19
- if isinstance(val, list):
20
- elements_str = ", ".join([_qmod_val_to_expr_str(elem) for elem in val])
21
- return f"[{elements_str}]"
22
-
23
- return str(val)
24
-
25
-
26
- @dataclass_transform()
27
6
  def struct(user_class: Type) -> Type:
28
- def _new_repr(self: Any) -> str:
29
- return _qmod_val_to_expr_str(self)
30
-
31
- user_dataclass = type(
32
- user_class.__name__,
33
- (CStructBase, dataclasses.dataclass(user_class)),
34
- dict(),
7
+ warnings.warn(
8
+ "@struct is deprecated and will be removed in a future release. "
9
+ "Use @dataclass instead.",
10
+ DeprecationWarning,
11
+ stacklevel=2,
35
12
  )
36
- user_dataclass.__repr__ = _new_repr # type:ignore[assignment]
37
- return user_dataclass
13
+ return dataclass(user_class)
@@ -54,7 +54,7 @@ from classiq.qmod.utilities import get_source_ref, version_portable_get_args
54
54
 
55
55
  ILLEGAL_SLICING_STEP_MSG = "Slicing with a step of a quantum variable is not supported"
56
56
  SLICE_OUT_OF_BOUNDS_MSG = "Slice end index out of bounds"
57
- UNSUPPORTED_ELEMENT_TYPE = "Only QBit is supported as element type for QArray"
57
+ UNSUPPORTED_ELEMENT_TYPE = "Only QBit and QNum are supported as element type for QArray"
58
58
  QARRAY_ELEMENT_NOT_SUBSCRIPTABLE = "Subscripting an element in QArray is illegal"
59
59
 
60
60
 
@@ -84,12 +84,13 @@ def _no_current_expandable() -> Iterator[None]:
84
84
 
85
85
 
86
86
  class QVar(Symbolic):
87
- def __init__(self, name: str) -> None:
88
- super().__init__(name)
87
+ def __init__(self, name: str, depth: int = 2) -> None:
88
+ super().__init__(name, True)
89
89
  self._name = name
90
+ source_ref = get_source_ref(sys._getframe(depth))
90
91
  if QCallable.CURRENT_EXPANDABLE is not None:
91
92
  QCallable.CURRENT_EXPANDABLE.add_local_handle(
92
- self._name, self.get_qmod_type()
93
+ self._name, self.get_qmod_type(), source_ref
93
94
  )
94
95
 
95
96
  @abc.abstractmethod
@@ -132,9 +133,9 @@ Input = Annotated[_Q, PortDeclarationDirection.Input]
132
133
 
133
134
 
134
135
  class QScalar(QVar, SymbolicExpr):
135
- def __init__(self, name: str) -> None:
136
- QVar.__init__(self, name)
137
- SymbolicExpr.__init__(self, name)
136
+ def __init__(self, name: str, depth: int = 2) -> None:
137
+ QVar.__init__(self, name, depth)
138
+ SymbolicExpr.__init__(self, name, True)
138
139
 
139
140
  def _insert_arith_operation(
140
141
  self, expr: SymbolicTypes, inplace: bool, source_ref: SourceReference
@@ -245,7 +246,7 @@ class QNum(Generic[_P], QScalar):
245
246
  self._fraction_digits = (
246
247
  None if fraction_digits is None else Expression(expr=str(fraction_digits))
247
248
  )
248
- super().__init__(name)
249
+ super().__init__(name, 3)
249
250
 
250
251
  @classmethod
251
252
  def to_qmod_quantum_type(cls, type_hint: Any) -> QuantumType:
@@ -300,7 +301,7 @@ class QArray(ArrayBase[_P], QVar):
300
301
  slice_: Optional[Tuple[int, int]] = None,
301
302
  index_: Optional[Union[int, CInt]] = None,
302
303
  ) -> None:
303
- if element_type is not QBit:
304
+ if not issubclass(get_origin(element_type) or element_type, (QBit, QNum)):
304
305
  raise ClassiqValueError(UNSUPPORTED_ELEMENT_TYPE)
305
306
  self._element_type = element_type
306
307
  self._length = length
@@ -324,7 +325,7 @@ class QArray(ArrayBase[_P], QVar):
324
325
 
325
326
  return HandleBinding(name=self._name)
326
327
 
327
- def __getitem__(self, key: Union[slice, int, CInt]) -> "QArray":
328
+ def __getitem__(self, key: Union[slice, int, CInt]) -> Any:
328
329
  if self._index is not None:
329
330
  raise ClassiqValueError(QARRAY_ELEMENT_NOT_SUBSCRIPTABLE)
330
331
 
@@ -356,7 +357,11 @@ class QArray(ArrayBase[_P], QVar):
356
357
  raise ClassiqValueError(SLICE_OUT_OF_BOUNDS_MSG)
357
358
  # prevent addition to local handles, since this is used for slicing existing local handles
358
359
  with _no_current_expandable():
359
- return QArray(
360
+ if new_index is None:
361
+ array_class = QArray
362
+ else:
363
+ array_class = QArraySubscript
364
+ return array_class(
360
365
  self._name, length=self._length, slice_=new_slice, index_=new_index
361
366
  )
362
367
 
@@ -386,27 +391,55 @@ class QArray(ArrayBase[_P], QVar):
386
391
 
387
392
  @classmethod
388
393
  def to_qmod_quantum_type(cls, type_hint: Any) -> QuantumType:
394
+ type_args = version_portable_get_args(type_hint)
395
+ if len(type_args) == 1 and isinstance(type_args[0], (str, int)):
396
+ type_args = (QBit, type_args[0])
397
+
398
+ api_element_type = QBit if len(type_args) == 0 else type_args[0]
399
+ api_element_class = get_origin(api_element_type) or api_element_type
400
+ element_type = api_element_class.to_qmod_quantum_type(api_element_type)
401
+
389
402
  length_expr: Optional[Expression] = None
390
- if len(version_portable_get_args(type_hint)) == 2:
391
- length_expr = Expression(
392
- expr=get_type_hint_expr(version_portable_get_args(type_hint)[1])
393
- )
394
- return QuantumBitvector(length=length_expr)
403
+ if len(type_args) == 2:
404
+ length_expr = Expression(expr=get_type_hint_expr(type_args[1]))
405
+
406
+ return QuantumBitvector(element_type=element_type, length=length_expr)
395
407
 
396
408
  def get_qmod_type(self) -> QuantumType:
409
+ element_class = get_origin(self._element_type) or self._element_type
410
+ length = None
397
411
  if self._length is not None:
398
- return QuantumBitvector(length=Expression(expr=str(self._length)))
399
- return QuantumBitvector()
412
+ length = Expression(expr=str(self._length))
413
+ return QuantumBitvector(
414
+ element_type=element_class.to_qmod_quantum_type(self._element_type),
415
+ length=length,
416
+ )
417
+
418
+
419
+ class QArraySubscript(QArray, QScalar):
420
+ @property
421
+ def size(self) -> CParamScalar:
422
+ return CParamScalar(f"get_field({self.get_handle_binding()}, 'size')")
423
+
424
+ @property
425
+ def fraction_digits(self) -> CParamScalar:
426
+ return CParamScalar(
427
+ f"get_field({self.get_handle_binding()}, 'fraction_digits')"
428
+ )
429
+
430
+ @property
431
+ def is_signed(self) -> CParamScalar:
432
+ return CParamScalar(f"get_field({self.get_handle_binding()}, 'is_signed')")
400
433
 
401
434
 
402
- def create_qvar_for_port_decl(port: PortDeclaration) -> QVar:
435
+ def create_qvar_for_port_decl(port: PortDeclaration, name: str) -> QVar:
403
436
  # prevent addition to local handles, since this is used for ports
404
437
  with _no_current_expandable():
405
438
  if _is_single_qbit_vector(port):
406
- return QBit(port.name)
439
+ return QBit(name)
407
440
  elif isinstance(port.quantum_type, QuantumNumeric):
408
- return QNum(port.name)
409
- return QArray(port.name)
441
+ return QNum(name)
442
+ return QArray(name)
410
443
 
411
444
 
412
445
  def _is_single_qbit_vector(port: PortDeclaration) -> bool:
@@ -35,7 +35,12 @@ class QExpandableInterface(ABC):
35
35
  raise NotImplementedError()
36
36
 
37
37
  @abstractmethod
38
- def add_local_handle(self, name: str, qtype: QuantumType) -> None:
38
+ def add_local_handle(
39
+ self,
40
+ name: str,
41
+ qtype: QuantumType,
42
+ source_ref: Optional[SourceReference] = None,
43
+ ) -> None:
39
44
  raise NotImplementedError()
40
45
 
41
46
 
@@ -1,5 +1,7 @@
1
1
  import inspect
2
2
  from abc import ABC
3
+ from dataclasses import is_dataclass
4
+ from enum import Enum as PythonEnum
3
5
  from types import TracebackType
4
6
  from typing import (
5
7
  TYPE_CHECKING,
@@ -51,7 +53,7 @@ from classiq.qmod.qmod_parameter import CInt, CParam, CParamScalar, create_param
51
53
  from classiq.qmod.qmod_variable import QVar, create_qvar_for_port_decl
52
54
  from classiq.qmod.quantum_callable import QCallable, QExpandableInterface
53
55
  from classiq.qmod.symbolic_expr import SymbolicExpr
54
- from classiq.qmod.utilities import mangle_keyword
56
+ from classiq.qmod.utilities import mangle_keyword, qmod_val_to_expr_str
55
57
 
56
58
  ArgType = Union[CParam, QVar, QCallable]
57
59
 
@@ -93,8 +95,17 @@ class QExpandable(QCallable, QExpandableInterface, ABC):
93
95
  def infer_rename_params(self) -> Dict[str, str]:
94
96
  return {}
95
97
 
96
- def add_local_handle(self, name: str, qtype: QuantumType) -> None:
97
- self._body.append(VariableDeclarationStatement(name=name, quantum_type=qtype))
98
+ def add_local_handle(
99
+ self,
100
+ name: str,
101
+ qtype: QuantumType,
102
+ source_ref: Optional[SourceReference] = None,
103
+ ) -> None:
104
+ self._body.append(
105
+ VariableDeclarationStatement(
106
+ name=name, quantum_type=qtype, source_ref=source_ref
107
+ )
108
+ )
98
109
 
99
110
  def append_statement_to_body(self, stmt: QuantumStatement) -> None:
100
111
  self._body.append(stmt)
@@ -102,14 +113,14 @@ class QExpandable(QCallable, QExpandableInterface, ABC):
102
113
  def _get_positional_args(self) -> List[ArgType]:
103
114
  result: List[ArgType] = []
104
115
  for arg in self.func_decl.get_positional_arg_decls():
116
+ rename_dict = self.infer_rename_params()
117
+ actual_name = rename_dict.get(arg.name, arg.name)
105
118
  if isinstance(arg, ClassicalParameterDeclaration):
106
- rename_dict = self.infer_rename_params()
107
- actual_name = rename_dict.get(arg.name, arg.name)
108
119
  result.append(
109
120
  create_param(actual_name, arg.classical_type, self._qmodule)
110
121
  )
111
122
  elif isinstance(arg, PortDeclaration):
112
- result.append(create_qvar_for_port_decl(arg))
123
+ result.append(create_qvar_for_port_decl(arg, actual_name))
113
124
  else:
114
125
  assert isinstance(arg, QuantumOperandDeclaration)
115
126
  result.append(QTerminalCallable(arg))
@@ -135,11 +146,11 @@ class QLambdaFunction(QExpandable):
135
146
 
136
147
  def infer_rename_params(self) -> Dict[str, str]:
137
148
  py_params = inspect.getfullargspec(self._py_callable)
138
- decl_params = self.func_decl.param_decls.keys()
149
+ decl_params = self.func_decl.get_positional_arg_decls()
139
150
  return {
140
- decl_param: py_param
151
+ decl_param.name: py_param
141
152
  for decl_param, py_param in zip(decl_params, py_params.args)
142
- if decl_param != py_param
153
+ if decl_param.name != py_param
143
154
  }
144
155
 
145
156
 
@@ -215,7 +226,7 @@ def prepare_arg(arg_decl: PositionalArg, val: Any, func_name: str) -> ArgValue:
215
226
  return Expression(expr=str(val.name))
216
227
  if isinstance(arg_decl, ClassicalParameterDeclaration):
217
228
  _validate_classical_arg(val, arg_decl, func_name)
218
- return Expression(expr=str(val))
229
+ return Expression(expr=qmod_val_to_expr_str(val))
219
230
  elif isinstance(arg_decl, PortDeclaration):
220
231
  if not isinstance(val, QVar):
221
232
  raise ClassiqValueError(
@@ -249,7 +260,14 @@ def prepare_arg(arg_decl: PositionalArg, val: Any, func_name: str) -> ArgValue:
249
260
  def _validate_classical_arg(
250
261
  arg: Any, arg_decl: ClassicalParameterDeclaration, func_name: str
251
262
  ) -> None:
252
- if not isinstance(arg, (*PythonClassicalTypes, CParam, SymbolicExpr, Basic)):
263
+ if (
264
+ not isinstance(
265
+ arg, (*PythonClassicalTypes, CParam, SymbolicExpr, Basic, PythonEnum)
266
+ )
267
+ and not is_dataclass(arg) # type:ignore[unreachable]
268
+ or isinstance(arg, SymbolicExpr)
269
+ and arg.is_quantum
270
+ ):
253
271
  raise ClassiqValueError(
254
272
  f"Argument {str(arg)!r} to parameter {arg_decl.name!r} of function "
255
273
  f"{func_name!r} has incompatible type; expected "