classiq 0.75.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 (83) 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 +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_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 +6 -1
  28. classiq/interface/generator/functions/type_name.py +7 -2
  29. classiq/interface/generator/functions/type_qualifier.py +15 -0
  30. classiq/interface/generator/model/preferences/preferences.py +7 -0
  31. classiq/interface/generator/quantum_program.py +5 -19
  32. classiq/interface/helpers/backward_compatibility.py +9 -0
  33. classiq/interface/helpers/datastructures.py +6 -0
  34. classiq/interface/model/port_declaration.py +1 -2
  35. classiq/interface/model/quantum_lambda_function.py +2 -1
  36. classiq/interface/server/routes.py +6 -0
  37. classiq/model_expansions/atomic_expression_functions_defs.py +62 -19
  38. classiq/model_expansions/capturing/captured_vars.py +2 -0
  39. classiq/model_expansions/closure.py +5 -0
  40. classiq/model_expansions/evaluators/classical_type_inference.py +17 -6
  41. classiq/model_expansions/evaluators/parameter_types.py +26 -13
  42. classiq/model_expansions/expression_evaluator.py +1 -1
  43. classiq/model_expansions/generative_functions.py +61 -34
  44. classiq/model_expansions/interpreters/base_interpreter.py +17 -6
  45. classiq/model_expansions/interpreters/frontend_generative_interpreter.py +5 -0
  46. classiq/model_expansions/interpreters/generative_interpreter.py +13 -1
  47. classiq/model_expansions/quantum_operations/allocate.py +6 -1
  48. classiq/model_expansions/quantum_operations/assignment_result_processor.py +219 -20
  49. classiq/model_expansions/quantum_operations/bind.py +54 -30
  50. classiq/model_expansions/quantum_operations/block_evaluator.py +42 -0
  51. classiq/model_expansions/quantum_operations/call_emitter.py +14 -7
  52. classiq/model_expansions/quantum_operations/composite_emitter.py +1 -1
  53. classiq/model_expansions/quantum_operations/declarative_call_emitter.py +23 -9
  54. classiq/model_expansions/quantum_operations/emitter.py +20 -3
  55. classiq/model_expansions/quantum_operations/quantum_function_call.py +4 -3
  56. classiq/model_expansions/scope.py +10 -7
  57. classiq/model_expansions/sympy_conversion/arithmetics.py +18 -0
  58. classiq/model_expansions/sympy_conversion/expression_to_sympy.py +2 -0
  59. classiq/model_expansions/sympy_conversion/sympy_to_python.py +10 -1
  60. classiq/model_expansions/transformers/model_renamer.py +45 -7
  61. classiq/model_expansions/utils/handles_collector.py +1 -1
  62. classiq/model_expansions/visitors/variable_references.py +45 -9
  63. classiq/qmod/builtins/functions/allocation.py +2 -2
  64. classiq/qmod/builtins/functions/arithmetic.py +14 -12
  65. classiq/qmod/builtins/functions/standard_gates.py +23 -23
  66. classiq/qmod/declaration_inferrer.py +19 -7
  67. classiq/qmod/generative.py +9 -1
  68. classiq/qmod/native/expression_to_qmod.py +4 -0
  69. classiq/qmod/native/pretty_printer.py +8 -3
  70. classiq/qmod/pretty_print/pretty_printer.py +1 -1
  71. classiq/qmod/python_classical_type.py +4 -5
  72. classiq/qmod/qmod_constant.py +15 -7
  73. classiq/qmod/qmod_variable.py +7 -1
  74. classiq/qmod/quantum_function.py +19 -6
  75. classiq/qmod/semantics/lambdas.py +6 -2
  76. classiq/qmod/semantics/validation/main_validation.py +17 -4
  77. classiq/qmod/symbolic.py +8 -19
  78. classiq/qmod/symbolic_expr.py +26 -0
  79. classiq/synthesis.py +17 -31
  80. classiq/visualization.py +35 -0
  81. {classiq-0.75.0.dist-info → classiq-0.76.0.dist-info}/METADATA +1 -1
  82. {classiq-0.75.0.dist-info → classiq-0.76.0.dist-info}/RECORD +83 -79
  83. {classiq-0.75.0.dist-info → classiq-0.76.0.dist-info}/WHEEL +0 -0
@@ -66,8 +66,6 @@ class QuantumSymbol:
66
66
  raise ClassiqExpansionError(
67
67
  f"{self.quantum_type.type_name} is not subscriptable"
68
68
  )
69
- if TYPE_CHECKING:
70
- assert self.quantum_type.length is not None
71
69
  if isinstance(start, int) and isinstance(end, int) and start >= end:
72
70
  raise ClassiqExpansionError(
73
71
  f"{self.quantum_type.type_name} slice '{self.handle}[{start}:{end}]' "
@@ -75,6 +73,7 @@ class QuantumSymbol:
75
73
  )
76
74
  if (isinstance(start, int) and start < 0) or (
77
75
  isinstance(end, int)
76
+ and self.quantum_type.length is not None
78
77
  and self.quantum_type.length.is_constant()
79
78
  and end > self.quantum_type.length_value
80
79
  ):
@@ -100,19 +99,23 @@ class QuantumSymbol:
100
99
  raise ClassiqExpansionError(
101
100
  f"{self.quantum_type.type_name} is not subscriptable"
102
101
  )
103
- if TYPE_CHECKING:
104
- assert self.quantum_type.length is not None
105
102
  if isinstance(index, int) and (
106
103
  index < 0
107
104
  or (
108
- self.quantum_type.length.is_constant()
105
+ self.quantum_type.length is not None
106
+ and self.quantum_type.length.is_constant()
109
107
  and index >= self.quantum_type.length_value
110
108
  )
111
109
  ):
110
+ length_suffix = (
111
+ f" (of length {self.quantum_type.length})"
112
+ if self.quantum_type.length is not None
113
+ else ""
114
+ )
112
115
  raise ClassiqExpansionError(
113
116
  f"Index {index} is out of bounds for "
114
- f"{self.quantum_type.type_name.lower()} {str(self.handle)!r} (of "
115
- f"length {self.quantum_type.length})"
117
+ f"{self.quantum_type.type_name.lower()} {str(self.handle)!r}"
118
+ f"{length_suffix}"
116
119
  )
117
120
  return QuantumSymbol(
118
121
  handle=SubscriptHandleBinding(
@@ -47,3 +47,21 @@ class BitwiseNot(Function):
47
47
  return ~a
48
48
 
49
49
  return None
50
+
51
+
52
+ class RShift(Function):
53
+ @classmethod
54
+ def eval(cls, a: Any, b: Any) -> Optional[int]:
55
+ if isinstance(a, Integer) and isinstance(b, Integer):
56
+ return a >> b
57
+
58
+ return None
59
+
60
+
61
+ class LShift(Function):
62
+ @classmethod
63
+ def eval(cls, a: Any, b: Any) -> Optional[int]:
64
+ if isinstance(a, Integer) and isinstance(b, Integer):
65
+ return a << b
66
+
67
+ return None
@@ -23,6 +23,8 @@ class ExpressionSympyTranslator(ast.NodeTransformer):
23
23
  ast.BitAnd: "BitwiseAnd",
24
24
  ast.BitXor: "BitwiseXor",
25
25
  ast.Div: "do_div",
26
+ ast.RShift: "RShift",
27
+ ast.LShift: "LShift",
26
28
  }
27
29
 
28
30
  UNARY_OPERATORS: dict[type[ast.AST], str] = {
@@ -17,6 +17,9 @@ from sympy.printing.pycode import PythonCodePrinter
17
17
 
18
18
  from classiq.interface.exceptions import ClassiqInternalExpansionError
19
19
  from classiq.interface.generator.expressions.expression_types import ExpressionValue
20
+ from classiq.interface.generator.expressions.proxies.classical.any_classical_value import (
21
+ AnyClassicalValue,
22
+ )
20
23
 
21
24
  from classiq.model_expansions.sympy_conversion.arithmetics import LogicalXor
22
25
 
@@ -24,7 +27,9 @@ from classiq.model_expansions.sympy_conversion.arithmetics import LogicalXor
24
27
  def sympy_to_python(
25
28
  value: Any, locals: Optional[dict[str, ExpressionValue]] = None
26
29
  ) -> ExpressionValue:
27
- if isinstance(value, Integer):
30
+ if isinstance(value, AnyClassicalValue):
31
+ pass
32
+ elif isinstance(value, Integer):
28
33
  value = int(value)
29
34
  elif isinstance(value, Float):
30
35
  value = float(value)
@@ -66,6 +71,8 @@ class SympyToQuantumExpressionTranslator(PythonCodePrinter):
66
71
  "BitwiseOr": "|",
67
72
  "BitwiseXor": "^",
68
73
  "LogicalXor": "^",
74
+ "RShift": ">>",
75
+ "LShift": "<<",
69
76
  }
70
77
  UNARY_BITWISE_OPERATORS_MAPPING = {"BitwiseNot": "~"}
71
78
 
@@ -117,6 +124,8 @@ class SympyToBoolExpressionTranslator(SympyToQuantumExpressionTranslator):
117
124
 
118
125
 
119
126
  def translate_sympy_quantum_expression(expr: Basic, preserve_bool_ops: bool) -> str:
127
+ if isinstance(expr, AnyClassicalValue):
128
+ return str(expr)
120
129
  if preserve_bool_ops:
121
130
  return SympyToBoolExpressionTranslator().doprint(expr)
122
131
  else:
@@ -2,8 +2,10 @@ import ast
2
2
  import re
3
3
  from collections.abc import Mapping, Sequence
4
4
  from dataclasses import dataclass
5
+ from functools import cmp_to_key
5
6
  from typing import TypeVar, cast
6
7
 
8
+ from classiq.interface.exceptions import ClassiqInternalExpansionError
7
9
  from classiq.interface.generator.expressions.expression import Expression
8
10
  from classiq.interface.generator.visitor import NodeType
9
11
  from classiq.interface.model.handle_binding import HandleBinding
@@ -23,6 +25,39 @@ def _replace_full_word(pattern: str, substitution: str, target: str) -> str:
23
25
  )
24
26
 
25
27
 
28
+ def _handle_contains_handle(handle: HandleBinding, other_handle: HandleBinding) -> int:
29
+ if str(other_handle) in str(handle) or other_handle.qmod_expr in handle.qmod_expr:
30
+ return 1
31
+ if str(handle) in str(other_handle) or handle.qmod_expr in other_handle.qmod_expr:
32
+ return -1
33
+ return 0
34
+
35
+
36
+ class _ExprNormalizer(ast.NodeTransformer):
37
+ def visit_Call(self, node: ast.Call) -> ast.AST:
38
+ if not isinstance(node.func, ast.Name):
39
+ return self.generic_visit(node)
40
+ if node.func.id == "get_field":
41
+ if (
42
+ len(node.args) != 2
43
+ or not isinstance(node.args[1], ast.Constant)
44
+ or not isinstance(node.args[1].value, str)
45
+ ):
46
+ raise ClassiqInternalExpansionError("Unexpected 'get_field' arguments")
47
+ return ast.Attribute(
48
+ value=self.visit(node.args[0]), attr=node.args[1].value
49
+ )
50
+ if node.func.id == "do_subscript":
51
+ if len(node.args) != 2:
52
+ raise ClassiqInternalExpansionError(
53
+ "Unexpected 'do_subscript' arguments"
54
+ )
55
+ return ast.Subscript(
56
+ value=self.visit(node.args[0]), slice=self.visit(node.args[1])
57
+ )
58
+ return self.generic_visit(node)
59
+
60
+
26
61
  @dataclass(frozen=True)
27
62
  class HandleRenaming:
28
63
  source_handle: HandleBinding
@@ -39,26 +74,29 @@ SymbolRenaming = Mapping[HandleBinding, Sequence[HandleRenaming]]
39
74
  def _rewrite_expression(
40
75
  symbol_mapping: SymbolRenaming, expression: Expression
41
76
  ) -> Expression:
77
+ normalized_expr = _ExprNormalizer().visit(ast.parse(expression.expr))
42
78
  vrc = VarRefCollector(
43
79
  ignore_duplicated_handles=True, ignore_sympy_symbols=True, unevaluated=True
44
80
  )
45
- vrc.visit(ast.parse(expression.expr))
81
+ vrc.visit(normalized_expr)
46
82
 
47
83
  handle_names = {
48
84
  part.source_handle: part.target_var_handle
49
85
  for parts in symbol_mapping.values()
50
86
  for part in parts
51
87
  }
52
- new_expr_str = expression.expr
53
- for handle in vrc.var_handles:
88
+ new_expr_str = ast.unparse(normalized_expr)
89
+ sorted_handles = sorted(
90
+ vrc.var_handles,
91
+ key=cmp_to_key( # type:ignore[misc]
92
+ lambda handle, other_handle: _handle_contains_handle(other_handle, handle)
93
+ ),
94
+ )
95
+ for handle in sorted_handles:
54
96
  new_handle = handle.collapse()
55
97
  for handle_to_replace, replacement in handle_names.items():
56
98
  new_handle = new_handle.replace_prefix(handle_to_replace, replacement)
57
99
  new_expr_str = _replace_full_word(str(handle), str(new_handle), new_expr_str)
58
- if handle.qmod_expr != str(handle):
59
- new_expr_str = _replace_full_word(
60
- str(handle.qmod_expr), str(new_handle.qmod_expr), new_expr_str
61
- )
62
100
 
63
101
  new_expr = Expression(expr=new_expr_str)
64
102
  new_expr._evaluated_expr = expression._evaluated_expr
@@ -18,7 +18,7 @@ class _HandlesCollector(Visitor):
18
18
  self.handles.append(handle)
19
19
 
20
20
  def visit_Expression(self, expression: Expression) -> None:
21
- vrc = VarRefCollector(ignore_duplicated_handles=True)
21
+ vrc = VarRefCollector(ignore_duplicated_handles=True, unevaluated=True)
22
22
  vrc.visit(ast.parse(expression.expr))
23
23
  self.handles.extend(vrc.var_handles)
24
24
 
@@ -95,16 +95,25 @@ class VarRefCollector(ast.NodeVisitor):
95
95
  def visit_Attribute(self, node: ast.Attribute) -> Optional[FieldHandleBinding]:
96
96
  return self._get_field_handle(node.value, node.attr)
97
97
 
98
- def visit_Call(self, node: ast.Call) -> Optional[FieldHandleBinding]:
99
- if (
100
- not isinstance(node.func, ast.Name)
101
- or node.func.id != "get_field"
102
- or len(node.args) != 2
103
- or not isinstance(node.args[1], ast.Constant)
104
- or not isinstance(node.args[1].value, str)
105
- ):
98
+ def visit_Call(self, node: ast.Call) -> Optional[HandleBinding]:
99
+ if not isinstance(node.func, ast.Name):
106
100
  return self.generic_visit(node)
107
- return self._get_field_handle(node.args[0], node.args[1].value)
101
+ if node.func.id == "get_field":
102
+ if (
103
+ len(node.args) != 2
104
+ or not isinstance(node.args[1], ast.Constant)
105
+ or not isinstance(node.args[1].value, str)
106
+ ):
107
+ raise ClassiqInternalExpansionError("Unexpected 'get_field' arguments")
108
+ return self._get_field_handle(node.args[0], node.args[1].value)
109
+ if node.func.id == "do_subscript":
110
+ if len(node.args) != 2:
111
+ raise ClassiqInternalExpansionError(
112
+ "Unexpected 'do_subscript' arguments"
113
+ )
114
+ self.visit(node.args[1])
115
+ return self._get_subscript_handle(node.args[0], node.args[1])
116
+ return self.generic_visit(node)
108
117
 
109
118
  def _get_field_handle(
110
119
  self, subject: ast.expr, field: str
@@ -121,6 +130,33 @@ class VarRefCollector(ast.NodeVisitor):
121
130
  self._var_handles[handle] = True
122
131
  return handle
123
132
 
133
+ def _get_subscript_handle(
134
+ self, subject: ast.expr, subscript: ast.expr
135
+ ) -> Optional[HandleBinding]:
136
+ with self.set_nested():
137
+ base_handle = self.visit(subject)
138
+ if base_handle is None:
139
+ return None
140
+ handle: HandleBinding
141
+ if isinstance(subscript, ast.Slice):
142
+ if subscript.lower is None or subscript.upper is None:
143
+ raise ClassiqExpansionError(
144
+ f"{str(base_handle)!r} slice must specify both lower and upper bounds"
145
+ )
146
+ handle = SlicedHandleBinding(
147
+ base_handle=base_handle,
148
+ start=Expression(expr=ast.unparse(subscript.lower)),
149
+ end=Expression(expr=ast.unparse(subscript.upper)),
150
+ )
151
+ else:
152
+ handle = SubscriptHandleBinding(
153
+ base_handle=base_handle,
154
+ index=Expression(expr=ast.unparse(subscript)),
155
+ )
156
+ if not self._is_nested:
157
+ self._var_handles[handle] = True
158
+ return handle
159
+
124
160
  def visit_Name(self, node: ast.Name) -> Optional[HandleBinding]:
125
161
  if not self._ignore_sympy_symbols and node.id in set(
126
162
  SYMPY_SUPPORTED_EXPRESSIONS
@@ -2,11 +2,11 @@ from typing import Literal
2
2
 
3
3
  from classiq.qmod.qfunc import qfunc
4
4
  from classiq.qmod.qmod_parameter import CArray, CReal
5
- from classiq.qmod.qmod_variable import Input, Output, QArray, QBit
5
+ from classiq.qmod.qmod_variable import Input, Output, QArray, QBit, QFree
6
6
 
7
7
 
8
8
  @qfunc(external=True)
9
- def free(in_: Input[QArray[QBit]]) -> None:
9
+ def free(in_: QFree[Input[QArray[QBit]]]) -> None:
10
10
  """
11
11
  [Qmod core-library function]
12
12
 
@@ -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 CArray, CBool, CReal
5
- from classiq.qmod.qmod_variable import Output, QArray, QBit, QNum
5
+ from classiq.qmod.qmod_variable import Const, Output, QArray, QBit, QFree, QNum
6
6
 
7
7
 
8
8
  @qfunc(external=True)
@@ -24,13 +24,15 @@ def unitary(
24
24
 
25
25
  @qfunc(external=True)
26
26
  def add(
27
- left: QNum,
28
- right: QNum,
29
- result: Output[
30
- QNum[
31
- Literal["result_size"],
32
- Literal["result_is_signed"],
33
- Literal["result_fraction_places"],
27
+ left: Const[QNum],
28
+ right: Const[QNum],
29
+ result: QFree[
30
+ Output[
31
+ QNum[
32
+ Literal["result_size"],
33
+ Literal["result_is_signed"],
34
+ Literal["result_fraction_places"],
35
+ ]
34
36
  ]
35
37
  ],
36
38
  result_size: CReal,
@@ -41,20 +43,20 @@ def add(
41
43
 
42
44
 
43
45
  @qfunc(external=True)
44
- def modular_add(left: QArray[QBit], right: QArray[QBit]) -> None:
46
+ def modular_add(left: Const[QArray[QBit]], right: QFree[QArray[QBit]]) -> None:
45
47
  pass
46
48
 
47
49
 
48
50
  @qfunc(external=True)
49
- def modular_add_constant(left: CReal, right: QNum) -> None:
51
+ def modular_add_constant(left: CReal, right: QFree[QNum]) -> None:
50
52
  pass
51
53
 
52
54
 
53
55
  @qfunc(external=True)
54
- def integer_xor(left: QArray[QBit], right: QArray[QBit]) -> None:
56
+ def integer_xor(left: Const[QArray[QBit]], right: QFree[QArray[QBit]]) -> None:
55
57
  pass
56
58
 
57
59
 
58
60
  @qfunc(external=True)
59
- def real_xor_constant(left: CReal, right: QNum) -> None:
61
+ def real_xor_constant(left: CReal, right: QFree[QNum]) -> None:
60
62
  pass
@@ -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