classiq 0.74.0__py3-none-any.whl → 0.76.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 (96) hide show
  1. classiq/_internals/api_wrapper.py +36 -0
  2. classiq/analyzer/show_interactive_hack.py +58 -2
  3. classiq/applications/chemistry/chemistry_model_constructor.py +8 -1
  4. classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +2 -0
  5. classiq/applications/combinatorial_optimization/combinatorial_problem.py +4 -4
  6. classiq/applications/qnn/gradients/quantum_gradient.py +3 -5
  7. classiq/applications/qnn/gradients/simple_quantum_gradient.py +2 -2
  8. classiq/applications/qnn/qlayer.py +23 -19
  9. classiq/applications/qnn/types.py +1 -4
  10. classiq/execution/__init__.py +3 -0
  11. classiq/execution/execution_session.py +3 -16
  12. classiq/execution/qnn.py +2 -2
  13. classiq/execution/user_budgets.py +38 -0
  14. classiq/executor.py +7 -19
  15. classiq/interface/_version.py +1 -1
  16. classiq/interface/debug_info/debug_info.py +18 -13
  17. classiq/interface/executor/user_budget.py +56 -0
  18. classiq/interface/generator/application_apis/finance_declarations.py +3 -0
  19. classiq/interface/generator/expressions/atomic_expression_functions.py +3 -0
  20. classiq/interface/generator/expressions/proxies/classical/any_classical_value.py +30 -124
  21. classiq/interface/generator/expressions/proxies/classical/classical_array_proxy.py +46 -22
  22. classiq/interface/generator/expressions/proxies/classical/qmod_struct_instance.py +7 -0
  23. classiq/interface/generator/expressions/proxies/classical/utils.py +14 -13
  24. classiq/interface/generator/expressions/proxies/quantum/qmod_qscalar_proxy.py +9 -2
  25. classiq/interface/generator/expressions/proxies/quantum/qmod_sized_proxy.py +4 -1
  26. classiq/interface/generator/expressions/sympy_supported_expressions.py +1 -0
  27. classiq/interface/generator/functions/classical_type.py +36 -1
  28. classiq/interface/generator/functions/type_name.py +32 -5
  29. classiq/interface/generator/functions/type_qualifier.py +15 -0
  30. classiq/interface/generator/generated_circuit_data.py +11 -25
  31. classiq/interface/generator/model/preferences/preferences.py +7 -0
  32. classiq/interface/generator/quantum_program.py +5 -19
  33. classiq/interface/generator/synthesis_metadata/synthesis_execution_data.py +10 -13
  34. classiq/interface/helpers/backward_compatibility.py +9 -0
  35. classiq/interface/helpers/datastructures.py +6 -0
  36. classiq/interface/helpers/versioned_model.py +12 -0
  37. classiq/interface/interface_version.py +1 -1
  38. classiq/interface/model/handle_binding.py +12 -0
  39. classiq/interface/model/port_declaration.py +1 -2
  40. classiq/interface/model/quantum_lambda_function.py +2 -1
  41. classiq/interface/model/statement_block.py +9 -1
  42. classiq/interface/model/within_apply_operation.py +12 -0
  43. classiq/interface/server/routes.py +6 -0
  44. classiq/model_expansions/atomic_expression_functions_defs.py +82 -23
  45. classiq/model_expansions/capturing/captured_vars.py +2 -0
  46. classiq/model_expansions/closure.py +18 -0
  47. classiq/model_expansions/evaluators/argument_types.py +6 -5
  48. classiq/model_expansions/evaluators/classical_type_inference.py +17 -6
  49. classiq/model_expansions/evaluators/parameter_types.py +26 -13
  50. classiq/model_expansions/evaluators/type_type_match.py +2 -2
  51. classiq/model_expansions/expression_evaluator.py +1 -1
  52. classiq/model_expansions/generative_functions.py +66 -33
  53. classiq/model_expansions/interpreters/base_interpreter.py +27 -19
  54. classiq/model_expansions/interpreters/frontend_generative_interpreter.py +26 -0
  55. classiq/model_expansions/interpreters/generative_interpreter.py +25 -1
  56. classiq/model_expansions/quantum_operations/allocate.py +27 -11
  57. classiq/model_expansions/quantum_operations/assignment_result_processor.py +220 -19
  58. classiq/model_expansions/quantum_operations/bind.py +54 -30
  59. classiq/model_expansions/quantum_operations/block_evaluator.py +42 -0
  60. classiq/model_expansions/quantum_operations/call_emitter.py +14 -12
  61. classiq/model_expansions/quantum_operations/composite_emitter.py +1 -1
  62. classiq/model_expansions/quantum_operations/declarative_call_emitter.py +23 -9
  63. classiq/model_expansions/quantum_operations/emitter.py +21 -8
  64. classiq/model_expansions/quantum_operations/expression_evaluator.py +1 -0
  65. classiq/model_expansions/quantum_operations/handle_evaluator.py +1 -0
  66. classiq/model_expansions/quantum_operations/quantum_function_call.py +4 -3
  67. classiq/model_expansions/scope.py +10 -7
  68. classiq/model_expansions/sympy_conversion/arithmetics.py +18 -0
  69. classiq/model_expansions/sympy_conversion/expression_to_sympy.py +2 -0
  70. classiq/model_expansions/sympy_conversion/sympy_to_python.py +10 -1
  71. classiq/model_expansions/transformers/model_renamer.py +48 -8
  72. classiq/model_expansions/utils/handles_collector.py +1 -1
  73. classiq/model_expansions/visitors/symbolic_param_inference.py +197 -0
  74. classiq/model_expansions/visitors/variable_references.py +45 -9
  75. classiq/qmod/builtins/functions/allocation.py +2 -2
  76. classiq/qmod/builtins/functions/arithmetic.py +14 -12
  77. classiq/qmod/builtins/functions/standard_gates.py +23 -23
  78. classiq/qmod/declaration_inferrer.py +19 -7
  79. classiq/qmod/generative.py +9 -1
  80. classiq/qmod/native/expression_to_qmod.py +4 -0
  81. classiq/qmod/native/pretty_printer.py +8 -3
  82. classiq/qmod/pretty_print/pretty_printer.py +1 -1
  83. classiq/qmod/python_classical_type.py +4 -5
  84. classiq/qmod/qmod_constant.py +15 -7
  85. classiq/qmod/qmod_variable.py +30 -2
  86. classiq/qmod/quantum_function.py +19 -6
  87. classiq/qmod/semantics/lambdas.py +6 -2
  88. classiq/qmod/semantics/validation/main_validation.py +17 -4
  89. classiq/qmod/symbolic.py +8 -19
  90. classiq/qmod/symbolic_expr.py +34 -2
  91. classiq/qmod/write_qmod.py +5 -1
  92. classiq/synthesis.py +17 -31
  93. classiq/visualization.py +35 -0
  94. {classiq-0.74.0.dist-info → classiq-0.76.0.dist-info}/METADATA +1 -1
  95. {classiq-0.74.0.dist-info → classiq-0.76.0.dist-info}/RECORD +96 -91
  96. {classiq-0.74.0.dist-info → classiq-0.76.0.dist-info}/WHEEL +1 -1
@@ -2,7 +2,7 @@ from typing import Literal
2
2
 
3
3
  from classiq.qmod.qfunc import qfunc
4
4
  from classiq.qmod.qmod_parameter import CReal
5
- from classiq.qmod.qmod_variable import QArray, QBit
5
+ from classiq.qmod.qmod_variable import Const, QArray, QBit, QFree
6
6
 
7
7
 
8
8
  @qfunc(external=True)
@@ -25,7 +25,7 @@ def H(target: QBit) -> None:
25
25
 
26
26
 
27
27
  @qfunc(external=True)
28
- def X(target: QBit) -> None:
28
+ def X(target: QFree[QBit]) -> None:
29
29
  """
30
30
  [Qmod core-library function]
31
31
 
@@ -44,7 +44,7 @@ def X(target: QBit) -> None:
44
44
 
45
45
 
46
46
  @qfunc(external=True)
47
- def Y(target: QBit) -> None:
47
+ def Y(target: QFree[QBit]) -> None:
48
48
  """
49
49
  [Qmod core-library function]
50
50
 
@@ -63,7 +63,7 @@ def Y(target: QBit) -> None:
63
63
 
64
64
 
65
65
  @qfunc(external=True)
66
- def Z(target: QBit) -> None:
66
+ def Z(target: Const[QBit]) -> None:
67
67
  """
68
68
  [Qmod core-library function]
69
69
 
@@ -82,7 +82,7 @@ def Z(target: QBit) -> None:
82
82
 
83
83
 
84
84
  @qfunc(external=True)
85
- def I(target: QBit) -> None:
85
+ def I(target: Const[QBit]) -> None:
86
86
  """
87
87
  [Qmod core-library function]
88
88
 
@@ -101,7 +101,7 @@ def I(target: QBit) -> None:
101
101
 
102
102
 
103
103
  @qfunc(external=True)
104
- def S(target: QBit) -> None:
104
+ def S(target: Const[QBit]) -> None:
105
105
  """
106
106
  [Qmod core-library function]
107
107
 
@@ -120,7 +120,7 @@ def S(target: QBit) -> None:
120
120
 
121
121
 
122
122
  @qfunc(external=True)
123
- def T(target: QBit) -> None:
123
+ def T(target: Const[QBit]) -> None:
124
124
  """
125
125
  [Qmod core-library function]
126
126
 
@@ -139,7 +139,7 @@ def T(target: QBit) -> None:
139
139
 
140
140
 
141
141
  @qfunc(external=True)
142
- def SDG(target: QBit) -> None:
142
+ def SDG(target: Const[QBit]) -> None:
143
143
  """
144
144
  [Qmod core-library function]
145
145
 
@@ -158,7 +158,7 @@ def SDG(target: QBit) -> None:
158
158
 
159
159
 
160
160
  @qfunc(external=True)
161
- def TDG(target: QBit) -> None:
161
+ def TDG(target: Const[QBit]) -> None:
162
162
  """
163
163
  [Qmod core-library function]
164
164
 
@@ -177,7 +177,7 @@ def TDG(target: QBit) -> None:
177
177
 
178
178
 
179
179
  @qfunc(external=True)
180
- def PHASE(theta: CReal, target: QBit) -> None:
180
+ def PHASE(theta: CReal, target: Const[QBit]) -> None:
181
181
  """
182
182
  [Qmod core-library function]
183
183
 
@@ -239,7 +239,7 @@ def RY(theta: CReal, target: QBit) -> None:
239
239
 
240
240
 
241
241
  @qfunc(external=True)
242
- def RZ(theta: CReal, target: QBit) -> None:
242
+ def RZ(theta: CReal, target: Const[QBit]) -> None:
243
243
  """
244
244
  [Qmod core-library function]
245
245
 
@@ -324,7 +324,7 @@ def RYY(theta: CReal, target: QArray[QBit, Literal[2]]) -> None:
324
324
 
325
325
 
326
326
  @qfunc(external=True)
327
- def RZZ(theta: CReal, target: QArray[QBit, Literal[2]]) -> None:
327
+ def RZZ(theta: CReal, target: Const[QArray[QBit, Literal[2]]]) -> None:
328
328
  """
329
329
  [Qmod core-library function]
330
330
 
@@ -345,7 +345,7 @@ def RZZ(theta: CReal, target: QArray[QBit, Literal[2]]) -> None:
345
345
 
346
346
 
347
347
  @qfunc(external=True)
348
- def CH(ctrl: QBit, target: QBit) -> None:
348
+ def CH(ctrl: Const[QBit], target: QBit) -> None:
349
349
  """
350
350
  [Qmod core-library function]
351
351
 
@@ -370,7 +370,7 @@ def CH(ctrl: QBit, target: QBit) -> None:
370
370
 
371
371
 
372
372
  @qfunc(external=True)
373
- def CX(ctrl: QBit, target: QBit) -> None:
373
+ def CX(ctrl: Const[QBit], target: QFree[QBit]) -> None:
374
374
  """
375
375
  [Qmod core-library function]
376
376
 
@@ -395,7 +395,7 @@ def CX(ctrl: QBit, target: QBit) -> None:
395
395
 
396
396
 
397
397
  @qfunc(external=True)
398
- def CY(ctrl: QBit, target: QBit) -> None:
398
+ def CY(ctrl: Const[QBit], target: QFree[QBit]) -> None:
399
399
  """
400
400
  [Qmod core-library function]
401
401
 
@@ -420,7 +420,7 @@ def CY(ctrl: QBit, target: QBit) -> None:
420
420
 
421
421
 
422
422
  @qfunc(external=True)
423
- def CZ(ctrl: QBit, target: QBit) -> None:
423
+ def CZ(ctrl: Const[QBit], target: Const[QBit]) -> None:
424
424
  """
425
425
  [Qmod core-library function]
426
426
 
@@ -445,7 +445,7 @@ def CZ(ctrl: QBit, target: QBit) -> None:
445
445
 
446
446
 
447
447
  @qfunc(external=True)
448
- def CRX(theta: CReal, ctrl: QBit, target: QBit) -> None:
448
+ def CRX(theta: CReal, ctrl: Const[QBit], target: QBit) -> None:
449
449
  """
450
450
  [Qmod core-library function]
451
451
 
@@ -471,7 +471,7 @@ def CRX(theta: CReal, ctrl: QBit, target: QBit) -> None:
471
471
 
472
472
 
473
473
  @qfunc(external=True)
474
- def CRY(theta: CReal, ctrl: QBit, target: QBit) -> None:
474
+ def CRY(theta: CReal, ctrl: Const[QBit], target: QBit) -> None:
475
475
  """
476
476
  [Qmod core-library function]
477
477
 
@@ -497,7 +497,7 @@ def CRY(theta: CReal, ctrl: QBit, target: QBit) -> None:
497
497
 
498
498
 
499
499
  @qfunc(external=True)
500
- def CRZ(theta: CReal, ctrl: QBit, target: QBit) -> None:
500
+ def CRZ(theta: CReal, ctrl: Const[QBit], target: Const[QBit]) -> None:
501
501
  """
502
502
  [Qmod core-library function]
503
503
 
@@ -523,7 +523,7 @@ def CRZ(theta: CReal, ctrl: QBit, target: QBit) -> None:
523
523
 
524
524
 
525
525
  @qfunc(external=True)
526
- def CPHASE(theta: CReal, ctrl: QBit, target: QBit) -> None:
526
+ def CPHASE(theta: CReal, ctrl: Const[QBit], target: Const[QBit]) -> None:
527
527
  """
528
528
  [Qmod core-library function]
529
529
 
@@ -549,7 +549,7 @@ def CPHASE(theta: CReal, ctrl: QBit, target: QBit) -> None:
549
549
 
550
550
 
551
551
  @qfunc(external=True)
552
- def SWAP(qbit0: QBit, qbit1: QBit) -> None:
552
+ def SWAP(qbit0: QFree[QBit], qbit1: QFree[QBit]) -> None:
553
553
  """
554
554
  [Qmod core-library function]
555
555
 
@@ -574,7 +574,7 @@ def SWAP(qbit0: QBit, qbit1: QBit) -> None:
574
574
 
575
575
 
576
576
  @qfunc(external=True)
577
- def IDENTITY(target: QArray[QBit]) -> None:
577
+ def IDENTITY(target: Const[QArray[QBit]]) -> None:
578
578
  """
579
579
  [Qmod core-library function]
580
580
 
@@ -623,7 +623,7 @@ def U(theta: CReal, phi: CReal, lam: CReal, gam: CReal, target: QBit) -> None:
623
623
 
624
624
 
625
625
  @qfunc(external=True)
626
- def CCX(ctrl: QArray[QBit, Literal[2]], target: QBit) -> None:
626
+ def CCX(ctrl: Const[QArray[QBit, Literal[2]]], target: QFree[QBit]) -> None:
627
627
  """
628
628
  [Qmod core-library function]
629
629
 
@@ -19,6 +19,7 @@ from classiq.interface.generator.functions.concrete_types import ConcreteClassic
19
19
  from classiq.interface.generator.functions.port_declaration import (
20
20
  PortDeclarationDirection,
21
21
  )
22
+ from classiq.interface.generator.functions.type_name import TypeName
22
23
  from classiq.interface.generator.functions.type_qualifier import TypeQualifier
23
24
  from classiq.interface.generator.types.enum_declaration import declaration_from_enum
24
25
  from classiq.interface.generator.types.struct_declaration import StructDeclaration
@@ -40,6 +41,10 @@ from classiq.qmod.python_classical_type import PythonClassicalType
40
41
  from classiq.qmod.qmod_variable import QVar, get_port_from_type_hint
41
42
  from classiq.qmod.quantum_callable import QCallableList
42
43
  from classiq.qmod.semantics.validation.type_hints import validate_annotation
44
+ from classiq.qmod.semantics.validation.types_validation import (
45
+ check_duplicate_types,
46
+ validate_cstruct,
47
+ )
43
48
  from classiq.qmod.utilities import unmangle_keyword, version_portable_get_args
44
49
 
45
50
  if sys.version_info[0:2] >= (3, 9):
@@ -61,14 +66,16 @@ class _PythonClassicalType(PythonClassicalType):
61
66
 
62
67
  enum_decl = declaration_from_enum(py_type)
63
68
  self.qmodule.enum_decls[py_type.__name__] = enum_decl
69
+ check_duplicate_types([enum_decl])
64
70
 
65
- def register_struct(self, py_type: type) -> None:
66
- if (
67
- self.qmodule is None
68
- or py_type.__name__ in BUILTIN_STRUCT_DECLARATIONS
69
- or py_type.__name__ in self.qmodule.type_decls
70
- ):
71
- return
71
+ def register_struct(self, py_type: type) -> TypeName:
72
+ classical_type = super().register_struct(py_type)
73
+ if self.qmodule is None:
74
+ return classical_type
75
+ all_decls = BUILTIN_STRUCT_DECLARATIONS | self.qmodule.type_decls
76
+ if py_type.__name__ in all_decls:
77
+ classical_type.set_classical_struct_decl(all_decls[py_type.__name__])
78
+ return classical_type
72
79
 
73
80
  struct_decl = StructDeclaration(
74
81
  name=py_type.__name__,
@@ -77,6 +84,11 @@ class _PythonClassicalType(PythonClassicalType):
77
84
  },
78
85
  )
79
86
  self.qmodule.type_decls[py_type.__name__] = struct_decl
87
+ check_duplicate_types([struct_decl])
88
+ validate_cstruct(struct_decl)
89
+
90
+ classical_type.set_classical_struct_decl(struct_decl)
91
+ return classical_type
80
92
 
81
93
 
82
94
  def python_type_to_qmod(
@@ -4,6 +4,11 @@ from typing import TYPE_CHECKING, Any, Optional
4
4
 
5
5
  from classiq.interface.exceptions import ClassiqError
6
6
  from classiq.interface.generator.expressions.expression import Expression
7
+ from classiq.interface.generator.expressions.proxies.classical.any_classical_value import (
8
+ AnyClassicalValue,
9
+ )
10
+
11
+ from classiq.qmod.cparam import CParamScalar
7
12
 
8
13
  if TYPE_CHECKING:
9
14
  from classiq.model_expansions.interpreters.generative_interpreter import (
@@ -41,4 +46,7 @@ def get_frontend_interpreter() -> "GenerativeInterpreter":
41
46
 
42
47
 
43
48
  def interpret_expression(expr: str) -> Any:
44
- return get_frontend_interpreter().evaluate(Expression(expr=expr)).value
49
+ val = get_frontend_interpreter().evaluate(Expression(expr=expr)).value
50
+ if isinstance(val, AnyClassicalValue):
51
+ return CParamScalar(str(val))
52
+ return val
@@ -131,6 +131,10 @@ class ASTToQMODCode:
131
131
  ]
132
132
  )
133
133
  return f"{self.ast_to_code(node.args[0])} {{{initializer_list}}}"
134
+ elif func == "do_subscript":
135
+ if len(node.args) != 2:
136
+ raise AssertionError("Error parsing array access.")
137
+ return f"{self.ast_to_code(node.args[0])}[{self.ast_to_code(node.args[1])}]"
134
138
  else:
135
139
  return "{}({})".format(
136
140
  func, ", ".join(self._cleaned_ast_to_code(arg) for arg in node.args)
@@ -26,6 +26,7 @@ from classiq.interface.generator.types.struct_declaration import StructDeclarati
26
26
  from classiq.interface.generator.visitor import NodeType
27
27
  from classiq.interface.model.allocate import Allocate
28
28
  from classiq.interface.model.bind_operation import BindOperation
29
+ from classiq.interface.model.block import Block
29
30
  from classiq.interface.model.classical_if import ClassicalIf
30
31
  from classiq.interface.model.classical_parameter_declaration import (
31
32
  AnonClassicalParameterDeclaration,
@@ -192,7 +193,7 @@ class DSLPrettyPrinter(ModelVisitor):
192
193
  def visit_AnonPortDeclaration(self, port_decl: AnonPortDeclaration) -> str:
193
194
  qualifier_str = (
194
195
  f"{port_decl.type_qualifier} "
195
- if port_decl.type_qualifier is not TypeQualifier.Quantum
196
+ if port_decl.type_qualifier in [TypeQualifier.Const, TypeQualifier.QFree]
196
197
  else ""
197
198
  )
198
199
  dir_str = (
@@ -308,8 +309,6 @@ class DSLPrettyPrinter(ModelVisitor):
308
309
 
309
310
  def visit_ClassicalIf(self, op: ClassicalIf) -> str:
310
311
  classical_if = f"{self._indent}if ({self.visit(op.condition)}) {{\n"
311
- if not op.then:
312
- raise AssertionError('Expected non empty "then" block')
313
312
  classical_if += self._visit_body(op.then)
314
313
 
315
314
  if op.else_:
@@ -345,6 +344,12 @@ class DSLPrettyPrinter(ModelVisitor):
345
344
  invert_code += f"{self._indent}}}\n"
346
345
  return invert_code
347
346
 
347
+ def visit_Block(self, block: Block) -> str:
348
+ invert_code = f"{self._indent}block {{\n"
349
+ invert_code += self._visit_body(block.statements)
350
+ invert_code += f"{self._indent}}}\n"
351
+ return invert_code
352
+
348
353
  def _visit_body(self, body: StatementBlock) -> str:
349
354
  code = ""
350
355
  self._level += 1
@@ -264,7 +264,7 @@ class PythonPrettyPrinter(ModelVisitor):
264
264
  if port_decl.direction is not PortDeclarationDirection.Inout:
265
265
  self._imports[port_decl.direction.name] = 1
266
266
  var_type = f"{port_decl.direction.name}[{var_type}]"
267
- if port_decl.type_qualifier is not TypeQualifier.Quantum:
267
+ if port_decl.type_qualifier in [TypeQualifier.Const, TypeQualifier.QFree]:
268
268
  self._imports[port_decl.type_qualifier.name] = 1
269
269
  var_type = f"{port_decl.type_qualifier.name}[{var_type}]"
270
270
  return var_type
@@ -19,7 +19,7 @@ from classiq.interface.generator.functions.classical_type import (
19
19
  Real,
20
20
  )
21
21
  from classiq.interface.generator.functions.concrete_types import ConcreteClassicalType
22
- from classiq.interface.generator.functions.type_name import Enum, Struct
22
+ from classiq.interface.generator.functions.type_name import Enum, Struct, TypeName
23
23
 
24
24
  from classiq.qmod.cparam import CArray, CBool, CInt, CReal
25
25
  from classiq.qmod.utilities import version_portable_get_args
@@ -59,8 +59,7 @@ class PythonClassicalType:
59
59
  )
60
60
  raise ClassiqValueError(CARRAY_ERROR_MESSAGE)
61
61
  elif inspect.isclass(py_type) and dataclasses.is_dataclass(py_type):
62
- self.register_struct(py_type)
63
- return Struct(name=py_type.__name__)
62
+ return self.register_struct(py_type)
64
63
  elif inspect.isclass(py_type) and isinstance(py_type, EnumMeta):
65
64
  self.register_enum(py_type)
66
65
  return Enum(name=py_type.__name__)
@@ -68,8 +67,8 @@ class PythonClassicalType:
68
67
  raise ClassiqValueError(CARRAY_ERROR_MESSAGE)
69
68
  return None
70
69
 
71
- def register_struct(self, py_type: type) -> None:
72
- pass
70
+ def register_struct(self, py_type: type) -> TypeName:
71
+ return Struct(name=py_type.__name__)
73
72
 
74
73
  def register_enum(self, py_type: EnumMeta) -> None:
75
74
  pass
@@ -1,6 +1,6 @@
1
1
  import inspect
2
2
  from dataclasses import is_dataclass
3
- from typing import Any, Optional, cast, get_origin
3
+ from typing import TYPE_CHECKING, Any, Optional, cast, get_origin
4
4
 
5
5
  from classiq.interface.exceptions import ClassiqError, ClassiqValueError
6
6
  from classiq.interface.generator.constant import Constant
@@ -17,7 +17,7 @@ from classiq.qmod.generative import (
17
17
  interpret_expression,
18
18
  is_generative_mode,
19
19
  )
20
- from classiq.qmod.model_state_container import ModelStateContainer
20
+ from classiq.qmod.model_state_container import QMODULE, ModelStateContainer
21
21
  from classiq.qmod.qmod_parameter import CParam, CParamList, CParamStruct
22
22
  from classiq.qmod.symbolic_expr import SymbolicExpr
23
23
  from classiq.qmod.utilities import qmod_val_to_expr_str
@@ -42,11 +42,12 @@ class QConstant(SymbolicExpr):
42
42
 
43
43
  def add_to_model(self) -> None:
44
44
  from classiq.qmod.builtins.constants import __all__ as builtin_constants
45
+ from classiq.qmod.semantics.validation.constants_validation import (
46
+ check_duplicate_constants,
47
+ )
45
48
 
46
- if self.name in builtin_constants:
49
+ if self.name in builtin_constants or QConstant.CURRENT_QMODULE is None:
47
50
  return
48
- if QConstant.CURRENT_QMODULE is None:
49
- raise ClassiqError(QMODULE_ERROR_MESSAGE)
50
51
 
51
52
  expr = qmod_val_to_expr_str(self._value)
52
53
  if (
@@ -57,6 +58,7 @@ class QConstant(SymbolicExpr):
57
58
 
58
59
  constant = self._get_constant_node()
59
60
  QConstant.CURRENT_QMODULE.constants[self.name] = constant
61
+ check_duplicate_constants([constant])
60
62
  if is_generative_mode():
61
63
  get_frontend_interpreter().add_constant(constant)
62
64
 
@@ -106,8 +108,10 @@ class QConstant(SymbolicExpr):
106
108
  def __getitem__(self, item: Any) -> CParam:
107
109
  self.add_to_model()
108
110
 
109
- assert QConstant.CURRENT_QMODULE is not None
110
-
111
+ if QConstant.CURRENT_QMODULE is None:
112
+ QConstant.set_current_model(QMODULE)
113
+ if TYPE_CHECKING:
114
+ assert QConstant.CURRENT_QMODULE is not None
111
115
  qmod_type = python_type_to_qmod(
112
116
  self._py_type, qmodule=QConstant.CURRENT_QMODULE
113
117
  )
@@ -141,3 +145,7 @@ class QConstant(SymbolicExpr):
141
145
  if is_generative_mode():
142
146
  return interpret_expression(len_expr)
143
147
  return cast(int, CParamScalar(len_expr))
148
+
149
+ def __str__(self) -> str:
150
+ self.add_to_model()
151
+ return super().__str__()
@@ -22,7 +22,12 @@ from typing import ( # type: ignore[attr-defined]
22
22
 
23
23
  from typing_extensions import ParamSpec, Self, _AnnotatedAlias
24
24
 
25
- from classiq.interface.exceptions import ClassiqInternalError, ClassiqValueError
25
+ from classiq.interface.exceptions import (
26
+ ClassiqInternalError,
27
+ ClassiqNotImplementedError,
28
+ ClassiqValueError,
29
+ )
30
+ from classiq.interface.generator.arith.register_user_input import RegisterArithmeticInfo
26
31
  from classiq.interface.generator.expressions.expression import Expression
27
32
  from classiq.interface.generator.expressions.non_symbolic_expr import NonSymbolicExpr
28
33
  from classiq.interface.generator.expressions.proxies.quantum.qmod_qarray_proxy import (
@@ -65,6 +70,8 @@ from classiq.qmod.generative import (
65
70
  )
66
71
  from classiq.qmod.model_state_container import QMODULE, ModelStateContainer
67
72
  from classiq.qmod.quantum_callable import QCallable
73
+ from classiq.qmod.semantics.annotation.qstruct_annotator import QStructAnnotator
74
+ from classiq.qmod.semantics.validation.types_validation import validate_qstruct
68
75
  from classiq.qmod.symbolic_expr import Symbolic, SymbolicExpr
69
76
  from classiq.qmod.symbolic_type import SymbolicTypes
70
77
  from classiq.qmod.utilities import (
@@ -340,6 +347,23 @@ class QNum(Generic[_P], QScalar):
340
347
  return interpret_expression(str(self.is_signed))
341
348
  return CParamScalar(f"get_field({self}, 'is_signed')")
342
349
 
350
+ def get_maximal_bounds(self) -> tuple[float, float]:
351
+ if not is_generative_mode():
352
+ raise ClassiqNotImplementedError(
353
+ "get_maximal_bounds() is supported in generative mode only"
354
+ )
355
+
356
+ if TYPE_CHECKING:
357
+ assert isinstance(self.size, int)
358
+ assert isinstance(self.is_signed, bool)
359
+ assert isinstance(self.fraction_digits, int)
360
+
361
+ return RegisterArithmeticInfo.get_maximal_bounds(
362
+ size=self.size,
363
+ is_signed=self.is_signed,
364
+ fraction_places=self.fraction_digits,
365
+ )
366
+
343
367
  # Support comma-separated generic args in older Python versions
344
368
  if sys.version_info[0:2] < (3, 10):
345
369
 
@@ -674,6 +698,8 @@ def _register_qstruct(
674
698
  if type_hint is QStruct or struct_name in qmodule.qstruct_decls:
675
699
  return
676
700
 
701
+ # temp assignment for recursive qstruct definitions
702
+ qmodule.qstruct_decls[struct_name] = QStructDeclaration(name=struct_name)
677
703
  _validate_fields(type_hint)
678
704
  struct_decl = QStructDeclaration(
679
705
  name=struct_name,
@@ -683,6 +709,8 @@ def _register_qstruct(
683
709
  },
684
710
  )
685
711
  qmodule.qstruct_decls[struct_name] = struct_decl
712
+ QStructAnnotator().visit(struct_decl)
713
+ validate_qstruct(struct_decl)
686
714
 
687
715
 
688
716
  def _validate_fields(type_hint: type[QStruct]) -> None:
@@ -727,5 +755,5 @@ def _get_root_type(py_type: Any) -> type[QVar]:
727
755
  )
728
756
  root_type = get_origin(non_annotated_type) or non_annotated_type
729
757
  if not issubclass(root_type, QVar):
730
- raise ClassiqInternalError(f"Invalid quantum type {root_type}")
758
+ raise ClassiqValueError(f"Invalid quantum type {root_type.__name__!r}")
731
759
  return root_type
@@ -14,7 +14,7 @@ from classiq.interface.generator.functions.port_declaration import (
14
14
  from classiq.interface.generator.model.constraints import Constraints
15
15
  from classiq.interface.generator.model.preferences.preferences import Preferences
16
16
  from classiq.interface.generator.types.compilation_metadata import CompilationMetadata
17
- from classiq.interface.model.model import Model
17
+ from classiq.interface.model.model import MAIN_FUNCTION_NAME, Model
18
18
  from classiq.interface.model.native_function_definition import (
19
19
  NativeFunctionDefinition,
20
20
  )
@@ -31,6 +31,7 @@ from classiq.qmod.qmod_parameter import CArray
31
31
  from classiq.qmod.quantum_callable import QCallable, QCallableList
32
32
  from classiq.qmod.quantum_expandable import QExpandable, QTerminalCallable
33
33
  from classiq.qmod.semantics.annotation.qstruct_annotator import QStructAnnotator
34
+ from classiq.qmod.semantics.validation.main_validation import validate_main_function
34
35
  from classiq.qmod.utilities import mangle_keyword
35
36
 
36
37
 
@@ -104,7 +105,7 @@ class QFunc(BaseQFunc):
104
105
  @property
105
106
  def func_decl(self) -> NamedParamsQuantumFunctionDeclaration:
106
107
  name = self._py_callable.__name__
107
- if hasattr(self._qmodule, "native_defs") and name in self._qmodule.native_defs:
108
+ if name in self._qmodule.native_defs:
108
109
  return self._qmodule.native_defs[name]
109
110
  return infer_func_decl(self._py_callable, qmodule=self._qmodule)
110
111
 
@@ -250,15 +251,23 @@ class GenerativeQFunc(BaseQFunc):
250
251
  ) -> None:
251
252
  super().__init__(py_callable, compilation_metadata)
252
253
  self._func_decl = func_decl
254
+ self._inferred_func_decl: Optional[NamedParamsQuantumFunctionDeclaration] = None
253
255
 
254
256
  @property
255
257
  def func_decl(self) -> NamedParamsQuantumFunctionDeclaration:
256
- if self._func_decl is None:
257
- self._func_decl = infer_func_decl(self._py_callable, self._qmodule)
258
- return self._func_decl
258
+ if self._func_decl is not None:
259
+ return self._func_decl
260
+ if self._inferred_func_decl is None:
261
+ self._inferred_func_decl = infer_func_decl(self._py_callable, self._qmodule)
262
+ return self._inferred_func_decl
259
263
 
260
264
  def __call__(self, *args: Any, **kwargs: Any) -> None:
261
- self._qmodule.generative_functions[self.func_decl.name] = self
265
+ if self.func_decl.name not in self._qmodule.generative_functions:
266
+ self._qmodule.generative_functions[self.func_decl.name] = self
267
+ if self._func_decl is None:
268
+ self._inferred_func_decl = infer_func_decl(
269
+ self._py_callable, self._qmodule
270
+ )
262
271
  super().__call__(*args, **kwargs)
263
272
 
264
273
  def create_model(
@@ -268,6 +277,10 @@ class GenerativeQFunc(BaseQFunc):
268
277
  preferences: Optional[Preferences] = None,
269
278
  classical_execution_function: Optional[CFunc] = None,
270
279
  ) -> Model:
280
+ self._qmodule.reset()
281
+ if self.func_decl.name == MAIN_FUNCTION_NAME:
282
+ validate_main_function(self.func_decl)
283
+
271
284
  def _dec_main(*args: Any, **kwargs: Any) -> None:
272
285
  self(*args, **kwargs)
273
286
 
@@ -1,3 +1,4 @@
1
+ from classiq.interface.helpers.backward_compatibility import zip_strict
1
2
  from classiq.interface.model.port_declaration import PortDeclaration
2
3
  from classiq.interface.model.quantum_function_declaration import (
3
4
  AnonQuantumOperandDeclaration,
@@ -14,8 +15,11 @@ def get_renamed_parameters(
14
15
  renamed_parameters: list[str] = []
15
16
  renamed_operands: dict[str, QuantumOperandDeclaration] = {}
16
17
  renamed_ports: list[PortDeclaration] = []
17
- for idx, param in enumerate(lambda_func.func_decl.positional_arg_declarations):
18
- param_name = lambda_func.pos_rename_params[idx]
18
+ for param, param_name in zip_strict(
19
+ lambda_func.func_decl.positional_arg_declarations,
20
+ lambda_func.pos_rename_params,
21
+ strict=False,
22
+ ):
19
23
  if isinstance(param, AnonClassicalParameterDeclaration):
20
24
  renamed_parameters.append(param_name)
21
25
  elif isinstance(param, AnonQuantumOperandDeclaration):
@@ -1,16 +1,22 @@
1
- from classiq.interface.exceptions import ClassiqExpansionError
1
+ from classiq.interface.exceptions import ClassiqExpansionError, ClassiqValueError
2
2
  from classiq.interface.generator.functions.classical_type import (
3
3
  ClassicalArray,
4
4
  ClassicalList,
5
5
  )
6
6
  from classiq.interface.generator.functions.concrete_types import ConcreteClassicalType
7
- from classiq.interface.model.native_function_definition import NativeFunctionDefinition
8
- from classiq.interface.model.quantum_function_declaration import PositionalArg
7
+ from classiq.interface.generator.functions.port_declaration import (
8
+ PortDeclarationDirection,
9
+ )
10
+ from classiq.interface.model.port_declaration import PortDeclaration
11
+ from classiq.interface.model.quantum_function_declaration import (
12
+ NamedParamsQuantumFunctionDeclaration,
13
+ PositionalArg,
14
+ )
9
15
 
10
16
  from classiq import ClassicalParameterDeclaration
11
17
 
12
18
 
13
- def validate_main_function(func: NativeFunctionDefinition) -> None:
19
+ def validate_main_function(func: NamedParamsQuantumFunctionDeclaration) -> None:
14
20
  for param in func.positional_arg_declarations:
15
21
  _validate_main_param(param)
16
22
 
@@ -18,6 +24,8 @@ def validate_main_function(func: NativeFunctionDefinition) -> None:
18
24
  def _validate_main_param(param: PositionalArg) -> None:
19
25
  if isinstance(param, ClassicalParameterDeclaration):
20
26
  _validate_main_classical_param_type(param.classical_type, param.name)
27
+ if isinstance(param, PortDeclaration):
28
+ _validate_main_quantum_param_type(param)
21
29
 
22
30
 
23
31
  def _validate_main_classical_param_type(
@@ -30,3 +38,8 @@ def _validate_main_classical_param_type(
30
38
  )
31
39
  if isinstance(param, ClassicalArray):
32
40
  _validate_main_classical_param_type(param.element_type, param_name)
41
+
42
+
43
+ def _validate_main_quantum_param_type(param: PortDeclaration) -> None:
44
+ if param.direction != PortDeclarationDirection.Output:
45
+ raise ClassiqValueError("Function 'main' cannot declare quantum inputs")
classiq/qmod/symbolic.py CHANGED
@@ -1,5 +1,4 @@
1
1
  import sys
2
- from collections.abc import Sequence
3
2
  from typing import (
4
3
  TYPE_CHECKING,
5
4
  Any,
@@ -12,6 +11,9 @@ from typing import (
12
11
  import numpy as np
13
12
 
14
13
  from classiq.interface.exceptions import ClassiqValueError
14
+ from classiq.interface.generator.expressions.proxies.classical.any_classical_value import (
15
+ subscript_to_str,
16
+ )
15
17
 
16
18
  from classiq.qmod import model_state_container
17
19
  from classiq.qmod.declaration_inferrer import python_type_to_qmod
@@ -24,6 +26,7 @@ from classiq.qmod.qmod_parameter import (
24
26
  )
25
27
  from classiq.qmod.symbolic_expr import SymbolicExpr
26
28
  from classiq.qmod.symbolic_type import SymbolicTypes
29
+ from classiq.qmod.utilities import qmod_val_to_expr_str
27
30
 
28
31
  pi = SymbolicExpr("pi", False)
29
32
  E = SymbolicExpr("E", False)
@@ -306,27 +309,13 @@ def sum(arr: SymbolicTypes) -> CParamScalar:
306
309
  return symbolic_function(arr)
307
310
 
308
311
 
309
- def _subscript_to_str(index: Any) -> str:
310
- if not isinstance(index, slice):
311
- return str(index)
312
- expr = ""
313
- if index.start is not None:
314
- expr += str(index.start)
315
- expr += ":"
316
- if index.stop is not None:
317
- expr += str(index.stop)
318
- if index.step is not None:
319
- expr += f":{index.step}"
320
- return expr
321
-
322
-
323
312
  def subscript(
324
- array: Union[
325
- Sequence[Union[float, CReal, CParamScalar]], CArray[CReal], np.ndarray
326
- ],
313
+ array: Union[list, CArray[CReal], np.ndarray],
327
314
  index: Any,
328
315
  ) -> CParamScalar:
329
- return CParamScalar(expr=f"{_unwrap_numpy(array)}[{_subscript_to_str(index)}]")
316
+ return CParamScalar(
317
+ expr=f"{qmod_val_to_expr_str(_unwrap_numpy(array))}[{subscript_to_str(index)}]"
318
+ )
330
319
 
331
320
 
332
321
  __all__ = [