classiq 0.75.0__py3-none-any.whl → 0.77.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 (101) 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 +15 -7
  4. classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +11 -1
  5. classiq/applications/combinatorial_optimization/combinatorial_problem.py +8 -7
  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 +14 -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 +16 -2
  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 +45 -21
  22. classiq/interface/generator/expressions/proxies/classical/qmod_struct_instance.py +7 -0
  23. classiq/interface/generator/expressions/proxies/classical/utils.py +12 -11
  24. classiq/interface/generator/expressions/proxies/quantum/qmod_qarray_proxy.py +6 -15
  25. classiq/interface/generator/expressions/proxies/quantum/qmod_qscalar_proxy.py +22 -6
  26. classiq/interface/generator/expressions/proxies/quantum/qmod_sized_proxy.py +9 -4
  27. classiq/interface/generator/expressions/sympy_supported_expressions.py +1 -0
  28. classiq/interface/generator/functions/classical_type.py +6 -1
  29. classiq/interface/generator/functions/type_name.py +7 -2
  30. classiq/interface/generator/functions/type_qualifier.py +15 -0
  31. classiq/interface/generator/model/preferences/preferences.py +7 -0
  32. classiq/interface/generator/quantum_program.py +5 -19
  33. classiq/interface/helpers/backward_compatibility.py +9 -0
  34. classiq/interface/helpers/datastructures.py +6 -0
  35. classiq/interface/model/handle_binding.py +8 -0
  36. classiq/interface/model/model.py +3 -6
  37. classiq/interface/model/port_declaration.py +1 -2
  38. classiq/interface/model/quantum_function_call.py +31 -1
  39. classiq/interface/model/quantum_lambda_function.py +2 -1
  40. classiq/interface/model/quantum_statement.py +14 -1
  41. classiq/interface/server/routes.py +6 -0
  42. classiq/interface/source_reference.py +7 -2
  43. classiq/model_expansions/atomic_expression_functions_defs.py +62 -19
  44. classiq/model_expansions/capturing/captured_vars.py +18 -6
  45. classiq/model_expansions/closure.py +5 -0
  46. classiq/model_expansions/evaluators/arg_type_match.py +2 -2
  47. classiq/model_expansions/evaluators/argument_types.py +3 -3
  48. classiq/model_expansions/evaluators/classical_expression.py +9 -9
  49. classiq/model_expansions/evaluators/classical_type_inference.py +17 -6
  50. classiq/model_expansions/evaluators/parameter_types.py +45 -24
  51. classiq/model_expansions/expression_evaluator.py +21 -12
  52. classiq/model_expansions/function_builder.py +45 -0
  53. classiq/model_expansions/generative_functions.py +62 -35
  54. classiq/model_expansions/interpreters/base_interpreter.py +32 -7
  55. classiq/model_expansions/interpreters/frontend_generative_interpreter.py +9 -3
  56. classiq/model_expansions/interpreters/generative_interpreter.py +17 -5
  57. classiq/model_expansions/quantum_operations/allocate.py +8 -3
  58. classiq/model_expansions/quantum_operations/assignment_result_processor.py +221 -20
  59. classiq/model_expansions/quantum_operations/bind.py +54 -30
  60. classiq/model_expansions/quantum_operations/block_evaluator.py +42 -0
  61. classiq/model_expansions/quantum_operations/call_emitter.py +35 -18
  62. classiq/model_expansions/quantum_operations/composite_emitter.py +1 -1
  63. classiq/model_expansions/quantum_operations/declarative_call_emitter.py +23 -9
  64. classiq/model_expansions/quantum_operations/emitter.py +21 -9
  65. classiq/model_expansions/quantum_operations/quantum_function_call.py +4 -3
  66. classiq/model_expansions/scope.py +63 -10
  67. classiq/model_expansions/sympy_conversion/arithmetics.py +18 -0
  68. classiq/model_expansions/sympy_conversion/expression_to_sympy.py +2 -0
  69. classiq/model_expansions/sympy_conversion/sympy_to_python.py +10 -1
  70. classiq/model_expansions/transformers/model_renamer.py +45 -7
  71. classiq/model_expansions/utils/handles_collector.py +1 -1
  72. classiq/model_expansions/visitors/symbolic_param_inference.py +3 -3
  73. classiq/model_expansions/visitors/variable_references.py +45 -9
  74. classiq/open_library/functions/lookup_table.py +1 -1
  75. classiq/open_library/functions/state_preparation.py +1 -1
  76. classiq/qmod/builtins/functions/allocation.py +2 -2
  77. classiq/qmod/builtins/functions/arithmetic.py +14 -12
  78. classiq/qmod/builtins/functions/standard_gates.py +23 -23
  79. classiq/qmod/create_model_function.py +21 -3
  80. classiq/qmod/declaration_inferrer.py +19 -7
  81. classiq/qmod/generative.py +9 -1
  82. classiq/qmod/global_declarative_switch.py +19 -0
  83. classiq/qmod/native/expression_to_qmod.py +4 -0
  84. classiq/qmod/native/pretty_printer.py +12 -3
  85. classiq/qmod/pretty_print/pretty_printer.py +5 -1
  86. classiq/qmod/python_classical_type.py +4 -5
  87. classiq/qmod/qfunc.py +31 -23
  88. classiq/qmod/qmod_constant.py +15 -7
  89. classiq/qmod/qmod_variable.py +7 -1
  90. classiq/qmod/quantum_expandable.py +29 -1
  91. classiq/qmod/quantum_function.py +45 -25
  92. classiq/qmod/semantics/lambdas.py +6 -2
  93. classiq/qmod/semantics/validation/main_validation.py +17 -4
  94. classiq/qmod/symbolic.py +8 -19
  95. classiq/qmod/symbolic_expr.py +26 -0
  96. classiq/qmod/write_qmod.py +36 -10
  97. classiq/synthesis.py +24 -37
  98. classiq/visualization.py +35 -0
  99. {classiq-0.75.0.dist-info → classiq-0.77.0.dist-info}/METADATA +1 -1
  100. {classiq-0.75.0.dist-info → classiq-0.77.0.dist-info}/RECORD +101 -96
  101. {classiq-0.75.0.dist-info → classiq-0.77.0.dist-info}/WHEEL +0 -0
@@ -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
 
@@ -1,4 +1,4 @@
1
- from typing import Optional, Union
1
+ from typing import Optional, Union, cast
2
2
 
3
3
  from classiq.interface.exceptions import ClassiqError
4
4
  from classiq.interface.executor.execution_preferences import ExecutionPreferences
@@ -7,10 +7,26 @@ from classiq.interface.generator.model.preferences.preferences import Preference
7
7
  from classiq.interface.model.model import MAIN_FUNCTION_NAME, SerializedModel
8
8
 
9
9
  from classiq.qmod.classical_function import CFunc
10
- from classiq.qmod.quantum_function import GenerativeQFunc, QFunc
10
+ from classiq.qmod.quantum_function import BaseQFunc, GenerativeQFunc, QFunc
11
11
  from classiq.qmod.write_qmod import write_qmod
12
12
 
13
13
 
14
+ class _EntryPointWrapper(str):
15
+ entry_point: BaseQFunc
16
+
17
+
18
+ def add_entry_point(
19
+ new_model: SerializedModel, old_model: SerializedModel
20
+ ) -> SerializedModel:
21
+ if not hasattr(old_model, "entry_point"):
22
+ return new_model
23
+ new_model_with_entry_point = _EntryPointWrapper(new_model)
24
+ new_model_with_entry_point.entry_point = cast(
25
+ _EntryPointWrapper, old_model
26
+ ).entry_point
27
+ return cast(SerializedModel, new_model_with_entry_point)
28
+
29
+
14
30
  def create_model(
15
31
  entry_point: Union[QFunc, GenerativeQFunc],
16
32
  constraints: Optional[Constraints] = None,
@@ -48,7 +64,9 @@ def create_model(
48
64
  preferences,
49
65
  classical_execution_function,
50
66
  )
51
- result = model.get_model()
67
+ serialized_model = _EntryPointWrapper(model.get_model())
68
+ serialized_model.entry_point = entry_point
69
+ result = cast(SerializedModel, serialized_model)
52
70
 
53
71
  if out_file is not None:
54
72
  write_qmod(result, out_file)
@@ -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
@@ -0,0 +1,19 @@
1
+ from collections.abc import Iterator
2
+ from contextlib import contextmanager
3
+
4
+ _DECLARATIVE_SWITCH = False
5
+
6
+
7
+ def get_global_declarative_switch() -> bool:
8
+ return _DECLARATIVE_SWITCH
9
+
10
+
11
+ @contextmanager
12
+ def set_global_declarative_switch() -> Iterator[None]:
13
+ global _DECLARATIVE_SWITCH
14
+ previous = _DECLARATIVE_SWITCH
15
+ _DECLARATIVE_SWITCH = True
16
+ try:
17
+ yield
18
+ finally:
19
+ _DECLARATIVE_SWITCH = previous
@@ -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,
@@ -34,6 +35,7 @@ from classiq.interface.model.control import Control
34
35
  from classiq.interface.model.handle_binding import (
35
36
  FieldHandleBinding,
36
37
  HandleBinding,
38
+ HandlesList,
37
39
  SlicedHandleBinding,
38
40
  SubscriptHandleBinding,
39
41
  )
@@ -192,7 +194,7 @@ class DSLPrettyPrinter(ModelVisitor):
192
194
  def visit_AnonPortDeclaration(self, port_decl: AnonPortDeclaration) -> str:
193
195
  qualifier_str = (
194
196
  f"{port_decl.type_qualifier} "
195
- if port_decl.type_qualifier is not TypeQualifier.Quantum
197
+ if port_decl.type_qualifier in [TypeQualifier.Const, TypeQualifier.QFree]
196
198
  else ""
197
199
  )
198
200
  dir_str = (
@@ -308,8 +310,6 @@ class DSLPrettyPrinter(ModelVisitor):
308
310
 
309
311
  def visit_ClassicalIf(self, op: ClassicalIf) -> str:
310
312
  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
313
  classical_if += self._visit_body(op.then)
314
314
 
315
315
  if op.else_:
@@ -345,6 +345,12 @@ class DSLPrettyPrinter(ModelVisitor):
345
345
  invert_code += f"{self._indent}}}\n"
346
346
  return invert_code
347
347
 
348
+ def visit_Block(self, block: Block) -> str:
349
+ invert_code = f"{self._indent}block {{\n"
350
+ invert_code += self._visit_body(block.statements)
351
+ invert_code += f"{self._indent}}}\n"
352
+ return invert_code
353
+
348
354
  def _visit_body(self, body: StatementBlock) -> str:
349
355
  code = ""
350
356
  self._level += 1
@@ -390,6 +396,9 @@ class DSLPrettyPrinter(ModelVisitor):
390
396
  def visit_FieldHandleBinding(self, var_ref: FieldHandleBinding) -> str:
391
397
  return f"{self.visit(var_ref.base_handle)}.{self.visit(var_ref.field)}"
392
398
 
399
+ def visit_HandlesList(self, handles: HandlesList) -> str:
400
+ return f"{{{', '.join(map(self.visit, handles.handles))}}}"
401
+
393
402
  def visit_ArithmeticOperation(self, arith_op: ArithmeticOperation) -> str:
394
403
  if arith_op.operation_kind == ArithmeticOperationKind.Assignment:
395
404
  op = "="
@@ -37,6 +37,7 @@ from classiq.interface.model.control import Control
37
37
  from classiq.interface.model.handle_binding import (
38
38
  FieldHandleBinding,
39
39
  HandleBinding,
40
+ HandlesList,
40
41
  SlicedHandleBinding,
41
42
  SubscriptHandleBinding,
42
43
  )
@@ -264,7 +265,7 @@ class PythonPrettyPrinter(ModelVisitor):
264
265
  if port_decl.direction is not PortDeclarationDirection.Inout:
265
266
  self._imports[port_decl.direction.name] = 1
266
267
  var_type = f"{port_decl.direction.name}[{var_type}]"
267
- if port_decl.type_qualifier is not TypeQualifier.Quantum:
268
+ if port_decl.type_qualifier in [TypeQualifier.Const, TypeQualifier.QFree]:
268
269
  self._imports[port_decl.type_qualifier.name] = 1
269
270
  var_type = f"{port_decl.type_qualifier.name}[{var_type}]"
270
271
  return var_type
@@ -518,6 +519,9 @@ class PythonPrettyPrinter(ModelVisitor):
518
519
  def visit_FieldHandleBinding(self, var_ref: FieldHandleBinding) -> str:
519
520
  return f"{self.visit(var_ref.base_handle)}.{self.visit(var_ref.field)}"
520
521
 
522
+ def visit_HandlesList(self, handles: HandlesList) -> str:
523
+ return self.visit(handles.handles)
524
+
521
525
  def visit_ArithmeticOperation(
522
526
  self, arith_op: ArithmeticOperation, in_lambda: bool = False
523
527
  ) -> str:
@@ -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
classiq/qmod/qfunc.py CHANGED
@@ -1,9 +1,9 @@
1
- from collections.abc import Iterator
2
- from contextlib import contextmanager
1
+ import warnings
3
2
  from typing import Callable, Literal, Optional, Union, overload
4
3
 
5
- from classiq.interface.exceptions import ClassiqInternalError
4
+ from classiq.interface.exceptions import ClassiqDeprecationWarning, ClassiqInternalError
6
5
 
6
+ from classiq.qmod.global_declarative_switch import get_global_declarative_switch
7
7
  from classiq.qmod.quantum_callable import QCallable
8
8
  from classiq.qmod.quantum_function import (
9
9
  BaseQFunc,
@@ -12,22 +12,9 @@ from classiq.qmod.quantum_function import (
12
12
  QFunc,
13
13
  )
14
14
 
15
- _GENERATIVE_SWITCH = False
16
-
17
-
18
- @contextmanager
19
- def set_global_generative_switch() -> Iterator[None]:
20
- global _GENERATIVE_SWITCH
21
- previous = _GENERATIVE_SWITCH
22
- _GENERATIVE_SWITCH = True
23
- try:
24
- yield
25
- finally:
26
- _GENERATIVE_SWITCH = previous
27
-
28
15
 
29
16
  @overload
30
- def qfunc(func: Callable) -> QFunc: ...
17
+ def qfunc(func: Callable) -> GenerativeQFunc: ...
31
18
 
32
19
 
33
20
  @overload
@@ -42,16 +29,16 @@ def qfunc(
42
29
  @overload
43
30
  def qfunc(
44
31
  *,
45
- generative: Literal[True],
32
+ generative: Literal[False],
46
33
  synthesize_separately: bool = False,
47
34
  atomic_qualifiers: Optional[list[str]] = None,
48
- ) -> Callable[[Callable], GenerativeQFunc]: ...
35
+ ) -> Callable[[Callable], QFunc]: ...
49
36
 
50
37
 
51
38
  @overload
52
39
  def qfunc(
53
40
  *, synthesize_separately: bool, atomic_qualifiers: Optional[list[str]] = None
54
- ) -> Callable[[Callable], QFunc]: ...
41
+ ) -> Callable[[Callable], GenerativeQFunc]: ...
55
42
 
56
43
 
57
44
  @overload
@@ -59,17 +46,38 @@ def qfunc(
59
46
  *,
60
47
  synthesize_separately: bool = False,
61
48
  atomic_qualifiers: Optional[list[str]] = None,
62
- ) -> Callable[[Callable], QFunc]: ...
49
+ ) -> Callable[[Callable], GenerativeQFunc]: ...
63
50
 
64
51
 
65
52
  def qfunc(
66
53
  func: Optional[Callable] = None,
67
54
  *,
68
55
  external: bool = False,
69
- generative: bool = False,
56
+ generative: Optional[bool] = None,
70
57
  synthesize_separately: bool = False,
71
58
  atomic_qualifiers: Optional[list[str]] = None,
72
59
  ) -> Union[Callable[[Callable], QCallable], QCallable]:
60
+ if generative is True:
61
+ warnings.warn(
62
+ "The use of `generative=True` is no longer required. Note that the "
63
+ "treatment of parameters of Qmod types will change from Python value to "
64
+ "symbolic in a near release. Change Qmod types to the corresponding Python "
65
+ "built-in types in order to use the parameters in Python expression "
66
+ "contexts.\n"
67
+ "Recommended changes:\n"
68
+ "@qfunc(generative=True) -> @qfunc\n"
69
+ "CInt->int\n"
70
+ "CReal->float\n"
71
+ "CArray->list\n\n"
72
+ "For more information see https://docs.classiq.io/latest/qmod-reference/language-reference/generative-descriptions/",
73
+ ClassiqDeprecationWarning,
74
+ stacklevel=2,
75
+ )
76
+ elif generative is None:
77
+ generative = True
78
+ if get_global_declarative_switch():
79
+ generative = False
80
+
73
81
  def wrapper(func: Callable) -> QCallable:
74
82
  qfunc: BaseQFunc
75
83
 
@@ -77,7 +85,7 @@ def qfunc(
77
85
  _validate_directives(synthesize_separately, atomic_qualifiers)
78
86
  return ExternalQFunc(func)
79
87
 
80
- if generative or _GENERATIVE_SWITCH:
88
+ if generative:
81
89
  qfunc = GenerativeQFunc(func)
82
90
  else:
83
91
  qfunc = QFunc(func)
@@ -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__()
@@ -70,6 +70,8 @@ from classiq.qmod.generative import (
70
70
  )
71
71
  from classiq.qmod.model_state_container import QMODULE, ModelStateContainer
72
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
73
75
  from classiq.qmod.symbolic_expr import Symbolic, SymbolicExpr
74
76
  from classiq.qmod.symbolic_type import SymbolicTypes
75
77
  from classiq.qmod.utilities import (
@@ -696,6 +698,8 @@ def _register_qstruct(
696
698
  if type_hint is QStruct or struct_name in qmodule.qstruct_decls:
697
699
  return
698
700
 
701
+ # temp assignment for recursive qstruct definitions
702
+ qmodule.qstruct_decls[struct_name] = QStructDeclaration(name=struct_name)
699
703
  _validate_fields(type_hint)
700
704
  struct_decl = QStructDeclaration(
701
705
  name=struct_name,
@@ -705,6 +709,8 @@ def _register_qstruct(
705
709
  },
706
710
  )
707
711
  qmodule.qstruct_decls[struct_name] = struct_decl
712
+ QStructAnnotator().visit(struct_decl)
713
+ validate_qstruct(struct_decl)
708
714
 
709
715
 
710
716
  def _validate_fields(type_hint: type[QStruct]) -> None:
@@ -749,5 +755,5 @@ def _get_root_type(py_type: Any) -> type[QVar]:
749
755
  )
750
756
  root_type = get_origin(non_annotated_type) or non_annotated_type
751
757
  if not issubclass(root_type, QVar):
752
- raise ClassiqInternalError(f"Invalid quantum type {root_type}")
758
+ raise ClassiqValueError(f"Invalid quantum type {root_type.__name__!r}")
753
759
  return root_type