classiq 0.70.0__py3-none-any.whl → 0.72.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 (98) hide show
  1. classiq/__init__.py +0 -6
  2. classiq/_internals/client.py +11 -1
  3. classiq/applications/chemistry/chemistry_model_constructor.py +18 -16
  4. classiq/applications/combinatorial_helpers/optimization_model.py +9 -2
  5. classiq/applications/combinatorial_helpers/pyomo_utils.py +6 -1
  6. classiq/applications/finance/__init__.py +0 -3
  7. classiq/applications/qsvm/__init__.py +0 -2
  8. classiq/interface/_version.py +1 -1
  9. classiq/interface/backend/backend_preferences.py +22 -0
  10. classiq/interface/backend/quantum_backend_providers.py +2 -0
  11. classiq/interface/debug_info/debug_info.py +4 -0
  12. classiq/interface/generator/expressions/expression_constants.py +0 -3
  13. classiq/interface/generator/expressions/expression_types.py +8 -3
  14. classiq/interface/generator/expressions/proxies/classical/any_classical_value.py +135 -0
  15. classiq/interface/generator/expressions/proxies/classical/classical_array_proxy.py +4 -0
  16. classiq/interface/generator/expressions/proxies/classical/classical_struct_proxy.py +5 -1
  17. classiq/interface/generator/expressions/proxies/classical/utils.py +34 -0
  18. classiq/interface/generator/functions/builtins/internal_operators.py +1 -0
  19. classiq/interface/generator/functions/classical_type.py +1 -1
  20. classiq/interface/generator/functions/type_name.py +16 -0
  21. classiq/interface/generator/functions/type_qualifier.py +7 -0
  22. classiq/interface/generator/generated_circuit_data.py +14 -1
  23. classiq/interface/generator/hardware/hardware_data.py +3 -1
  24. classiq/interface/generator/quantum_function_call.py +8 -1
  25. classiq/interface/generator/synthesis_execution_parameter.py +1 -0
  26. classiq/interface/generator/transpiler_basis_gates.py +3 -1
  27. classiq/interface/generator/types/compilation_metadata.py +1 -0
  28. classiq/interface/hardware.py +1 -0
  29. classiq/interface/ide/visual_model.py +1 -0
  30. classiq/interface/interface_version.py +1 -1
  31. classiq/interface/model/allocate.py +7 -0
  32. classiq/interface/model/block.py +12 -0
  33. classiq/interface/model/classical_if.py +4 -0
  34. classiq/interface/model/handle_binding.py +21 -0
  35. classiq/interface/model/inplace_binary_operation.py +4 -0
  36. classiq/interface/model/model.py +3 -1
  37. classiq/interface/model/phase_operation.py +4 -0
  38. classiq/interface/model/port_declaration.py +3 -0
  39. classiq/interface/model/power.py +4 -0
  40. classiq/interface/model/quantum_expressions/quantum_expression.py +4 -0
  41. classiq/interface/model/quantum_function_call.py +4 -0
  42. classiq/interface/model/quantum_function_declaration.py +1 -1
  43. classiq/interface/model/quantum_statement.py +5 -0
  44. classiq/interface/model/quantum_type.py +22 -0
  45. classiq/interface/model/repeat.py +4 -0
  46. classiq/interface/model/statement_block.py +3 -0
  47. classiq/interface/model/variable_declaration_statement.py +5 -0
  48. classiq/interface/server/routes.py +0 -2
  49. classiq/model_expansions/atomic_expression_functions_defs.py +35 -13
  50. classiq/model_expansions/capturing/captured_vars.py +156 -34
  51. classiq/model_expansions/closure.py +0 -9
  52. classiq/model_expansions/evaluators/classical_type_inference.py +70 -0
  53. classiq/model_expansions/evaluators/parameter_types.py +20 -10
  54. classiq/model_expansions/expression_evaluator.py +0 -11
  55. classiq/model_expansions/function_builder.py +2 -8
  56. classiq/model_expansions/generative_functions.py +7 -30
  57. classiq/model_expansions/interpreters/base_interpreter.py +7 -8
  58. classiq/model_expansions/interpreters/generative_interpreter.py +33 -5
  59. classiq/model_expansions/quantum_operations/__init__.py +0 -2
  60. classiq/model_expansions/quantum_operations/block_evaluator.py +16 -2
  61. classiq/model_expansions/quantum_operations/call_emitter.py +49 -6
  62. classiq/model_expansions/quantum_operations/emitter.py +64 -6
  63. classiq/model_expansions/quantum_operations/expression_evaluator.py +4 -0
  64. classiq/model_expansions/quantum_operations/handle_evaluator.py +1 -1
  65. classiq/model_expansions/quantum_operations/quantum_function_call.py +49 -0
  66. classiq/model_expansions/quantum_operations/repeat_block_evaluator.py +34 -0
  67. classiq/model_expansions/scope.py +33 -21
  68. classiq/model_expansions/scope_initialization.py +28 -32
  69. classiq/model_expansions/transformers/model_renamer.py +69 -63
  70. classiq/model_expansions/utils/sympy_utils.py +24 -0
  71. classiq/model_expansions/visitors/variable_references.py +1 -0
  72. classiq/qmod/__init__.py +3 -1
  73. classiq/qmod/builtins/functions/__init__.py +8 -0
  74. classiq/qmod/builtins/functions/allocation.py +36 -0
  75. classiq/qmod/builtins/functions/arithmetic.py +10 -5
  76. classiq/qmod/builtins/functions/mid_circuit_measurement.py +3 -0
  77. classiq/qmod/builtins/operations.py +2 -2
  78. classiq/qmod/declaration_inferrer.py +52 -24
  79. classiq/qmod/model_state_container.py +9 -0
  80. classiq/qmod/native/pretty_printer.py +25 -3
  81. classiq/qmod/pretty_print/pretty_printer.py +31 -14
  82. classiq/qmod/python_classical_type.py +12 -1
  83. classiq/qmod/qfunc.py +33 -8
  84. classiq/qmod/qmod_variable.py +188 -147
  85. classiq/qmod/quantum_function.py +3 -4
  86. classiq/qmod/semantics/validation/type_hints.py +19 -10
  87. classiq/qmod/symbolic.py +16 -3
  88. {classiq-0.70.0.dist-info → classiq-0.72.0.dist-info}/METADATA +1 -1
  89. {classiq-0.70.0.dist-info → classiq-0.72.0.dist-info}/RECORD +90 -91
  90. classiq/applications/finance/finance_model_constructor.py +0 -137
  91. classiq/applications/grover/__init__.py +0 -9
  92. classiq/applications/grover/grover_model_constructor.py +0 -167
  93. classiq/applications/libraries/__init__.py +0 -0
  94. classiq/applications/libraries/qmci_library.py +0 -22
  95. classiq/applications/qsvm/qsvm_model_constructor.py +0 -131
  96. classiq/model_expansions/quantum_operations/classicalif.py +0 -57
  97. classiq/model_expansions/quantum_operations/repeat.py +0 -62
  98. {classiq-0.70.0.dist-info → classiq-0.72.0.dist-info}/WHEEL +0 -0
@@ -7,6 +7,9 @@ from classiq.interface.generator.functions.classical_type import (
7
7
  ClassicalList,
8
8
  )
9
9
  from classiq.interface.generator.functions.concrete_types import ConcreteClassicalType
10
+ from classiq.interface.model.classical_parameter_declaration import (
11
+ ClassicalParameterDeclaration,
12
+ )
10
13
  from classiq.interface.model.handle_binding import HandleBinding
11
14
  from classiq.interface.model.model import MAIN_FUNCTION_NAME, Model
12
15
  from classiq.interface.model.native_function_definition import NativeFunctionDefinition
@@ -55,14 +58,18 @@ def add_functions_to_scope(
55
58
 
56
59
 
57
60
  def add_generative_functions_to_scope(
58
- functions: Sequence[GenerativeQFunc], scope: Scope
61
+ functions: Sequence[GenerativeQFunc], scope: Scope, override_atomic: bool = False
59
62
  ) -> None:
60
63
  for function in functions:
61
64
  name = function.func_decl.name
62
- if name == MAIN_FUNCTION_NAME or name not in scope:
63
- scope[function.func_decl.name] = Evaluated(
65
+ if (
66
+ name == MAIN_FUNCTION_NAME
67
+ or name not in scope
68
+ or (override_atomic and scope[name].value.is_atomic)
69
+ ):
70
+ scope[name] = Evaluated(
64
71
  value=GenerativeFunctionClosure.create(
65
- name=function.func_decl.name,
72
+ name=name,
66
73
  positional_arg_declarations=function.func_decl.positional_arg_declarations,
67
74
  scope=Scope(parent=scope),
68
75
  generative_blocks={"body": function},
@@ -102,17 +109,24 @@ def add_entry_point_params_to_scope(
102
109
  parameters: Sequence[PositionalArg], main_closure: FunctionClosure
103
110
  ) -> None:
104
111
  for parameter in parameters:
105
- if not isinstance(parameter, PortDeclaration):
106
- continue
107
- main_closure.scope[parameter.name] = Evaluated(
108
- value=QuantumSymbol(
109
- handle=HandleBinding(name=parameter.name),
110
- quantum_type=evaluate_type_in_quantum_symbol(
111
- parameter.quantum_type, main_closure.scope, parameter.name
112
+ if isinstance(parameter, PortDeclaration):
113
+ main_closure.scope[parameter.name] = Evaluated(
114
+ value=QuantumSymbol(
115
+ handle=HandleBinding(name=parameter.name),
116
+ quantum_type=evaluate_type_in_quantum_symbol(
117
+ parameter.quantum_type, main_closure.scope, parameter.name
118
+ ),
112
119
  ),
113
- ),
114
- defining_function=main_closure,
115
- )
120
+ defining_function=main_closure,
121
+ )
122
+ elif isinstance(parameter, ClassicalParameterDeclaration):
123
+ param_val = parameter.classical_type.get_classical_proxy(
124
+ handle=HandleBinding(name=parameter.name)
125
+ )
126
+ main_closure.scope[parameter.name] = Evaluated(
127
+ value=param_val,
128
+ defining_function=main_closure,
129
+ )
116
130
 
117
131
 
118
132
  def init_top_level_scope(model: Model, scope: Scope) -> None:
@@ -126,24 +140,6 @@ def init_builtin_types() -> None:
126
140
  QMODULE.type_decls |= BUILTIN_STRUCT_DECLARATIONS
127
141
 
128
142
 
129
- def init_exec_params(model: Model, scope: Scope) -> dict[str, ConcreteClassicalType]:
130
- if model.execution_parameters is not None:
131
- exec_params = model.execution_parameters
132
- else:
133
- exec_params = {
134
- param.name: param.classical_type
135
- for param in model.function_dict.get(
136
- "_dec_main", model.main_func
137
- ).param_decls
138
- }
139
- for param_name, param_type in exec_params.items():
140
- param_val = param_type.get_classical_proxy(
141
- handle=HandleBinding(name=param_name)
142
- )
143
- scope[param_name] = Evaluated(value=param_val)
144
- return exec_params
145
-
146
-
147
143
  def _get_shape(classical_type: ConcreteClassicalType) -> tuple[int, ...]:
148
144
  if isinstance(classical_type, ClassicalList):
149
145
  raise ClassiqInternalExpansionError("Unexpected classical list")
@@ -36,72 +36,78 @@ class HandleRenaming:
36
36
  SymbolRenaming = Mapping[HandleBinding, Sequence[HandleRenaming]]
37
37
 
38
38
 
39
- class ModelRenamer:
40
- def rewrite(self, subject: AST_NODE, symbol_mapping: SymbolRenaming) -> AST_NODE:
41
- if len(symbol_mapping) == 0:
42
- return subject
43
- handle_replacements = {
44
- part.source_handle: part.target_var_handle
45
- for parts in symbol_mapping.values()
46
- for part in parts
47
- }
39
+ def _rewrite_expression(
40
+ symbol_mapping: SymbolRenaming, expression: Expression
41
+ ) -> Expression:
42
+ vrc = VarRefCollector(
43
+ ignore_duplicated_handles=True, ignore_sympy_symbols=True, unevaluated=True
44
+ )
45
+ vrc.visit(ast.parse(expression.expr))
46
+
47
+ handle_names = {
48
+ part.source_handle: part.target_var_handle
49
+ for parts in symbol_mapping.values()
50
+ for part in parts
51
+ }
52
+ new_expr_str = expression.expr
53
+ for handle in vrc.var_handles:
54
+ new_handle = handle.collapse()
55
+ for handle_to_replace, replacement in handle_names.items():
56
+ new_handle = new_handle.replace_prefix(handle_to_replace, replacement)
57
+ 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
+
63
+ new_expr = Expression(expr=new_expr_str)
64
+ new_expr._evaluated_expr = expression._evaluated_expr
65
+ return new_expr
48
66
 
49
- class ReplaceSplitVars(ModelTransformer):
50
- @staticmethod
51
- def visit_HandleBinding(handle: HandleBinding) -> HandleBinding:
52
- handle = handle.collapse()
53
- for handle_to_replace, replacement in handle_replacements.items():
54
- handle = handle.replace_prefix(handle_to_replace, replacement)
55
- return handle
56
-
57
- @staticmethod
58
- def visit_Expression(expr: Expression) -> Expression:
59
- return self._rewrite_expression(symbol_mapping, expr)
60
-
61
- def visit_QuantumExpressionOperation(
62
- self, op: QuantumExpressionOperation
63
- ) -> QuantumExpressionOperation:
64
- op = cast(QuantumExpressionOperation, self.generic_visit(op))
65
- previous_var_handles = list(op._var_handles)
66
- op._var_handles = self.visit(op._var_handles)
67
- op._var_types = {
68
- new_handle.name: op._var_types.get(
69
- new_handle.name, op._var_types[previous_handle.name]
70
- )
71
- for previous_handle, new_handle in zip(
72
- previous_var_handles, op._var_handles
73
- )
74
- }
75
- return op
76
-
77
- return ReplaceSplitVars().visit(subject)
78
-
79
- def _rewrite_expression(
80
- self,
81
- symbol_mapping: SymbolRenaming,
82
- expression: Expression,
83
- ) -> Expression:
84
- vrc = VarRefCollector(ignore_duplicated_handles=True, ignore_sympy_symbols=True)
85
- vrc.visit(ast.parse(expression.expr))
86
-
87
- handle_names = {
67
+
68
+ class _ReplaceSplitVarsHandles(ModelTransformer):
69
+ def __init__(self, symbol_mapping: SymbolRenaming) -> None:
70
+ self._handle_replacements = {
88
71
  part.source_handle: part.target_var_handle
89
72
  for parts in symbol_mapping.values()
90
73
  for part in parts
91
74
  }
92
- new_expr_str = expression.expr
93
- for handle in vrc.var_handles:
94
- new_handle = handle.collapse()
95
- for handle_to_replace, replacement in handle_names.items():
96
- new_handle = new_handle.replace_prefix(handle_to_replace, replacement)
97
- new_expr_str = _replace_full_word(
98
- str(handle), str(new_handle), new_expr_str
75
+
76
+ def visit_HandleBinding(self, handle: HandleBinding) -> HandleBinding:
77
+ handle = handle.collapse()
78
+ for handle_to_replace, replacement in self._handle_replacements.items():
79
+ handle = handle.replace_prefix(handle_to_replace, replacement)
80
+ return handle
81
+
82
+
83
+ class _ReplaceSplitVarsExpressions(ModelTransformer):
84
+ def __init__(self, symbol_mapping: SymbolRenaming) -> None:
85
+ self._symbol_mapping = symbol_mapping
86
+
87
+ def visit_Expression(self, expr: Expression) -> Expression:
88
+ return _rewrite_expression(self._symbol_mapping, expr)
89
+
90
+ def visit_QuantumExpressionOperation(
91
+ self, op: QuantumExpressionOperation
92
+ ) -> QuantumExpressionOperation:
93
+ op = cast(QuantumExpressionOperation, self.generic_visit(op))
94
+ previous_var_handles = list(op._var_handles)
95
+ op._var_handles = self.visit(op._var_handles)
96
+ op._var_types = {
97
+ new_handle.name: op._var_types.get(
98
+ new_handle.name, op._var_types[previous_handle.name]
99
99
  )
100
- if handle.qmod_expr != str(handle):
101
- new_expr_str = _replace_full_word(
102
- str(handle.qmod_expr), str(new_handle.qmod_expr), new_expr_str
103
- )
104
-
105
- new_expr = Expression(expr=new_expr_str)
106
- new_expr._evaluated_expr = expression._evaluated_expr
107
- return new_expr
100
+ for previous_handle, new_handle in zip(
101
+ previous_var_handles, op._var_handles
102
+ )
103
+ }
104
+ return op
105
+
106
+
107
+ class ModelRenamer:
108
+ def rewrite(self, subject: AST_NODE, symbol_mapping: SymbolRenaming) -> AST_NODE:
109
+ if len(symbol_mapping) == 0:
110
+ return subject
111
+ subject = _ReplaceSplitVarsHandles(symbol_mapping).visit(subject)
112
+ subject = _ReplaceSplitVarsExpressions(symbol_mapping).visit(subject)
113
+ return subject
@@ -0,0 +1,24 @@
1
+ from typing import Any
2
+
3
+ from sympy import Number
4
+
5
+
6
+ def unwrap_sympy_numeric(n: Any) -> Any:
7
+ if not isinstance(n, Number) or not n.is_constant():
8
+ return n
9
+ if n.is_Integer:
10
+ return int(n)
11
+ return float(n)
12
+
13
+
14
+ def is_constant_subscript(index: Any) -> bool:
15
+ if not isinstance(index, slice):
16
+ return isinstance(unwrap_sympy_numeric(index), int)
17
+ start = unwrap_sympy_numeric(index.start)
18
+ stop = unwrap_sympy_numeric(index.stop)
19
+ step = unwrap_sympy_numeric(index.step)
20
+ return (
21
+ (start is None or isinstance(start, int))
22
+ and (stop is None or isinstance(stop, int))
23
+ and (step is None or isinstance(step, int))
24
+ )
@@ -58,6 +58,7 @@ class VarRefCollector(ast.NodeVisitor):
58
58
  def visit_Subscript(
59
59
  self, node: ast.Subscript
60
60
  ) -> Union[SubscriptHandleBinding, SlicedHandleBinding, None]:
61
+ self.visit(node.slice)
61
62
  with self.set_nested():
62
63
  base_handle = self.visit(node.value)
63
64
  if base_handle is None:
classiq/qmod/__init__.py CHANGED
@@ -6,7 +6,7 @@ from .expression_query import get_expression_numeric_attributes
6
6
  from .qfunc import qfunc
7
7
  from .qmod_constant import QConstant
8
8
  from .qmod_parameter import Array, CArray, CBool, CInt, CReal
9
- from .qmod_variable import Input, Output, QArray, QBit, QNum, QStruct
9
+ from .qmod_variable import Const, Input, Output, QArray, QBit, QFree, QNum, QStruct
10
10
  from .quantum_callable import QCallable, QCallableList
11
11
  from .write_qmod import write_qmod
12
12
 
@@ -18,6 +18,8 @@ __all__ = [
18
18
  "CReal",
19
19
  "Input",
20
20
  "Output",
21
+ "Const",
22
+ "QFree",
21
23
  "QArray",
22
24
  "QBit",
23
25
  "QNum",
@@ -50,7 +50,9 @@ CORE_LIB_DECLS = [
50
50
  SWAP,
51
51
  IDENTITY,
52
52
  prepare_state,
53
+ prepare_state_approx,
53
54
  prepare_amplitudes,
55
+ prepare_amplitudes_approx,
54
56
  unitary,
55
57
  add,
56
58
  modular_add,
@@ -62,7 +64,9 @@ CORE_LIB_DECLS = [
62
64
  free,
63
65
  randomized_benchmarking,
64
66
  inplace_prepare_state,
67
+ inplace_prepare_state_approx,
65
68
  inplace_prepare_amplitudes,
69
+ inplace_prepare_amplitudes_approx,
66
70
  single_pauli_exponent,
67
71
  commuting_paulis_exponent,
68
72
  suzuki_trotter,
@@ -114,7 +118,9 @@ __all__ = [ # noqa: RUF022
114
118
  "free",
115
119
  "gaussian_finance",
116
120
  "inplace_prepare_amplitudes",
121
+ "inplace_prepare_amplitudes_approx",
117
122
  "inplace_prepare_state",
123
+ "inplace_prepare_state_approx",
118
124
  "integer_xor",
119
125
  "log_normal_finance",
120
126
  "modular_add",
@@ -125,7 +131,9 @@ __all__ = [ # noqa: RUF022
125
131
  "pauli_feature_map",
126
132
  "permute",
127
133
  "prepare_amplitudes",
134
+ "prepare_amplitudes_approx",
128
135
  "prepare_state",
136
+ "prepare_state_approx",
129
137
  "qdrift",
130
138
  "randomized_benchmarking",
131
139
  "real_xor_constant",
@@ -123,3 +123,39 @@ def inplace_prepare_amplitudes(
123
123
 
124
124
  """
125
125
  pass
126
+
127
+
128
+ @qfunc(external=True)
129
+ def inplace_prepare_amplitudes_approx(
130
+ amplitudes: CArray[CReal],
131
+ bound: CReal,
132
+ target: QArray[QBit, Literal["log(get_field(amplitudes, 'len'), 2)"]],
133
+ ) -> None:
134
+ pass
135
+
136
+
137
+ @qfunc(external=True)
138
+ def prepare_amplitudes_approx(
139
+ amplitudes: CArray[CReal],
140
+ bound: CReal,
141
+ out: Output[QArray[QBit, Literal["log(get_field(amplitudes, 'len'), 2)"]]],
142
+ ) -> None:
143
+ pass
144
+
145
+
146
+ @qfunc(external=True)
147
+ def inplace_prepare_state_approx(
148
+ probabilities: CArray[CReal],
149
+ bound: CReal,
150
+ target: QArray[QBit, Literal["log(get_field(probabilities, 'len'), 2)"]],
151
+ ) -> None:
152
+ pass
153
+
154
+
155
+ @qfunc(external=True)
156
+ def prepare_state_approx(
157
+ probabilities: CArray[CReal],
158
+ bound: CReal,
159
+ out: Output[QArray[QBit, Literal["log(get_field(probabilities, 'len'), 2)"]]],
160
+ ) -> None:
161
+ pass
@@ -1,7 +1,7 @@
1
1
  from typing import Literal
2
2
 
3
3
  from classiq.qmod.qfunc import qfunc
4
- from classiq.qmod.qmod_parameter import CArray, CReal
4
+ from classiq.qmod.qmod_parameter import CArray, CBool, CReal
5
5
  from classiq.qmod.qmod_variable import Output, QArray, QBit, QNum
6
6
 
7
7
 
@@ -24,13 +24,18 @@ def unitary(
24
24
 
25
25
  @qfunc(external=True)
26
26
  def add(
27
- left: QArray[QBit],
28
- right: QArray[QBit],
27
+ left: QNum,
28
+ right: QNum,
29
29
  result: Output[
30
- QArray[
31
- QBit, Literal["Max(get_field(left, 'len'), get_field(right, 'len')) + 1"]
30
+ QNum[
31
+ Literal["result_size"],
32
+ Literal["result_is_signed"],
33
+ Literal["result_fraction_places"],
32
34
  ]
33
35
  ],
36
+ result_size: CReal,
37
+ result_is_signed: CBool,
38
+ result_fraction_places: CReal,
34
39
  ) -> None:
35
40
  pass
36
41
 
@@ -11,5 +11,8 @@ def RESET(target: QBit) -> None:
11
11
 
12
12
  Args:
13
13
  target: the qubit to reset
14
+
15
+ Notes:
16
+ We approximate the depth of a single RESET gate to 1000 X gates.
14
17
  """
15
18
  pass
@@ -142,13 +142,13 @@ def if_(
142
142
  if_stmt = ClassicalIf(
143
143
  condition=Expression(expr=str(condition)),
144
144
  then=_operand_to_body(then, "then"),
145
- else_=_operand_to_body(else_, "else") if else_ != _MISSING_VALUE else [], # type: ignore[arg-type]
145
+ else_=_operand_to_body(else_, "else_") if else_ != _MISSING_VALUE else [], # type: ignore[arg-type]
146
146
  source_ref=source_ref,
147
147
  )
148
148
  if is_generative_mode():
149
149
  if_stmt.set_generative_block("then", then)
150
150
  if callable(else_):
151
- if_stmt.set_generative_block("else", else_)
151
+ if_stmt.set_generative_block("else_", else_)
152
152
  QCallable.CURRENT_EXPANDABLE.append_statement_to_body(if_stmt)
153
153
 
154
154
 
@@ -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_qualifier import TypeQualifier
22
23
  from classiq.interface.generator.types.enum_declaration import declaration_from_enum
23
24
  from classiq.interface.generator.types.struct_declaration import StructDeclaration
24
25
  from classiq.interface.model.classical_parameter_declaration import (
@@ -36,7 +37,7 @@ from classiq.qmod.builtins.enums import BUILTIN_ENUM_DECLARATIONS
36
37
  from classiq.qmod.builtins.structs import BUILTIN_STRUCT_DECLARATIONS
37
38
  from classiq.qmod.model_state_container import ModelStateContainer
38
39
  from classiq.qmod.python_classical_type import PythonClassicalType
39
- from classiq.qmod.qmod_variable import QVar
40
+ from classiq.qmod.qmod_variable import QVar, get_port_from_type_hint
40
41
  from classiq.qmod.quantum_callable import QCallableList
41
42
  from classiq.qmod.semantics.validation.type_hints import validate_annotation
42
43
  from classiq.qmod.utilities import unmangle_keyword, version_portable_get_args
@@ -85,15 +86,12 @@ def python_type_to_qmod(
85
86
 
86
87
 
87
88
  def _extract_port_decl(name: Optional[str], py_type: Any) -> AnonPortDeclaration:
88
- # FIXME: CAD-13409
89
- qtype: type[QVar] = QVar.from_type_hint(py_type) # type:ignore[assignment]
90
- direction = qtype.port_direction(py_type)
91
- if isinstance(py_type, _AnnotatedAlias):
92
- py_type = py_type.__args__[0]
89
+ quantum_type, direction, qualifier = get_port_from_type_hint(py_type)
93
90
  param = AnonPortDeclaration(
94
91
  name=None,
95
92
  direction=direction,
96
- quantum_type=qtype.to_qmod_quantum_type(py_type),
93
+ quantum_type=quantum_type,
94
+ type_qualifier=qualifier,
97
95
  )
98
96
  if name is not None:
99
97
  param = param.rename(name)
@@ -126,23 +124,45 @@ def _extract_operand_decl(
126
124
  def _extract_operand_param(py_type: Any) -> tuple[Optional[str], Any]:
127
125
  if get_origin(py_type) is not Annotated:
128
126
  return None, py_type
127
+
129
128
  args = get_args(py_type)
130
- if len(args) == 2:
131
- if isinstance(args[1], PortDeclarationDirection):
132
- return None, py_type
133
- elif isinstance(args[1], str):
134
- return args[1], args[0]
135
- elif get_origin(args[1]) is Literal:
136
- return version_portable_get_args(args[1])[0], args[0]
137
- elif len(args) == 3 and isinstance(args[1], PortDeclarationDirection):
138
- if isinstance(args[2], str):
139
- return args[2], Annotated[args[0], args[1]]
140
- elif get_origin(args[2]) is Literal:
141
- return version_portable_get_args(args[2])[0], Annotated[args[0], args[1]]
142
- raise ClassiqValueError(
143
- f"Operand parameter declaration must be of the form <param-type> or "
144
- f"Annotated[<param-type>, <param-name>]. Got {py_type}"
145
- )
129
+ _validate_annotations(args, py_type)
130
+ param_name = _get_param_name(args)
131
+
132
+ if param_name is None:
133
+ if len(args) > 1:
134
+ return None, _unpacked_annotated(args[0], args[1:])
135
+ return None, args[0]
136
+
137
+ if len(args) > 2:
138
+ return param_name, _unpacked_annotated(args[0], args[1:-1])
139
+ return param_name, args[0]
140
+
141
+
142
+ def _unpacked_annotated(arg_0: Any, args: Any) -> _AnnotatedAlias:
143
+ return Annotated.__class_getitem__((arg_0, *args)) # type:ignore[attr-defined]
144
+
145
+
146
+ def _get_param_name(py_type_args: Any) -> Optional[str]:
147
+ if isinstance(py_type_args[-1], str) and not isinstance(
148
+ py_type_args[-1], (PortDeclarationDirection, TypeQualifier)
149
+ ):
150
+ return py_type_args[-1]
151
+ elif py_type_args[-1] is Literal:
152
+ return str(version_portable_get_args(py_type_args[-1])[0]) # type: ignore[arg-type]
153
+ else:
154
+ return None
155
+
156
+
157
+ def _validate_annotations(py_type_args: Any, py_type: Any) -> None:
158
+ for arg in py_type_args[1:-1]:
159
+ if (
160
+ isinstance(arg, str) and not isinstance(arg, PortDeclarationDirection)
161
+ ) or arg is Literal:
162
+ raise ClassiqValueError(
163
+ f"Operand parameter declaration must be of the form <param-type> or "
164
+ f"Annotated[<param-type>, <param-name>]. Got {py_type}"
165
+ )
146
166
 
147
167
 
148
168
  @overload
@@ -177,7 +197,7 @@ def _extract_positional_args(
177
197
  if name is not None:
178
198
  param = param.rename(name)
179
199
  result.append(param)
180
- elif QVar.from_type_hint(py_type) is not None:
200
+ elif is_qvar(py_type):
181
201
  result.append(_extract_port_decl(name, py_type))
182
202
  else:
183
203
  result.append(_extract_operand_decl(name, py_type, qmodule=qmodule))
@@ -193,3 +213,11 @@ def infer_func_decl(
193
213
  list(py_func.__annotations__.items()), qmodule=qmodule
194
214
  ),
195
215
  )
216
+
217
+
218
+ def is_qvar(type_hint: Any) -> Any:
219
+ non_annotated_type = (
220
+ type_hint.__origin__ if isinstance(type_hint, _AnnotatedAlias) else type_hint
221
+ )
222
+ type_ = get_origin(non_annotated_type) or non_annotated_type
223
+ return issubclass(type_, QVar)
@@ -1,3 +1,4 @@
1
+ from collections import defaultdict
1
2
  from typing import TYPE_CHECKING
2
3
 
3
4
  from classiq.interface.generator.constant import Constant
@@ -25,3 +26,11 @@ class ModelStateContainer:
25
26
 
26
27
 
27
28
  QMODULE = ModelStateContainer()
29
+ QMODULE.enum_decls = {}
30
+ QMODULE.type_decls = {}
31
+ QMODULE.qstruct_decls = {}
32
+ QMODULE.native_defs = {}
33
+ QMODULE.constants = {}
34
+ QMODULE.functions_compilation_metadata = {}
35
+ QMODULE.generative_functions = {}
36
+ QMODULE.function_dependencies = defaultdict(list)
@@ -18,6 +18,8 @@ from classiq.interface.generator.functions.port_declaration import (
18
18
  PortDeclarationDirection,
19
19
  )
20
20
  from classiq.interface.generator.functions.type_name import TypeName
21
+ from classiq.interface.generator.functions.type_qualifier import TypeQualifier
22
+ from classiq.interface.generator.types.compilation_metadata import CompilationMetadata
21
23
  from classiq.interface.generator.types.enum_declaration import EnumDeclaration
22
24
  from classiq.interface.generator.types.qstruct_declaration import QStructDeclaration
23
25
  from classiq.interface.generator.types.struct_declaration import StructDeclaration
@@ -95,6 +97,7 @@ class DSLPrettyPrinter(ModelVisitor):
95
97
  self._level = 0
96
98
  self._decimal_precision = decimal_precision
97
99
  self._emit_open_lib_functions = emit_open_lib_functions
100
+ self._compilation_metadata: dict[str, CompilationMetadata] = {}
98
101
 
99
102
  def visit(self, node: NodeType) -> str:
100
103
  res = super().visit(node)
@@ -105,6 +108,8 @@ class DSLPrettyPrinter(ModelVisitor):
105
108
  def visit_Model(self, model: Model) -> str:
106
109
  # FIXME - CAD-20149: Remove this line once the froggies are removed, and the visit of lambdas can be done without accessing the func_decl property (with rename_params values only).
107
110
  resolve_function_calls(model, model.function_dict)
111
+ self._compilation_metadata = model.functions_compilation_metadata
112
+
108
113
  enum_decls = [self.visit(enum_decl) for enum_decl in model.enums]
109
114
  struct_decls = [self.visit(struct_decl) for struct_decl in model.types]
110
115
  qstruct_decls = [self.visit(qstruct_decl) for qstruct_decl in model.qstructs]
@@ -135,10 +140,20 @@ class DSLPrettyPrinter(ModelVisitor):
135
140
  )
136
141
  return f"({positional_args})"
137
142
 
143
+ def _get_decl_string(self, func_decl: QuantumFunctionDeclaration) -> str:
144
+ no_qualifiers_decl = "qfunc"
145
+ if func_decl.name not in self._compilation_metadata:
146
+ return no_qualifiers_decl
147
+ atomic_qualifiers = self._compilation_metadata[func_decl.name].atomic_qualifiers
148
+ if len(atomic_qualifiers) == 0:
149
+ return no_qualifiers_decl
150
+ return f"atomic_qualifiers ({', '.join(atomic_qualifiers)})\n" f"qfunc"
151
+
138
152
  def visit_QuantumFunctionDeclaration(
139
153
  self, func_decl: QuantumFunctionDeclaration
140
154
  ) -> str:
141
- return f"qfunc {func_decl.name}{self._visit_arg_decls(func_decl)}"
155
+ qfunc_decl = self._get_decl_string(func_decl)
156
+ return f"{qfunc_decl} {func_decl.name}{self._visit_arg_decls(func_decl)}"
142
157
 
143
158
  def visit_EnumDeclaration(self, enum_decl: EnumDeclaration) -> str:
144
159
  return f"enum {enum_decl.name} {{\n{self._visit_members(enum_decl.members)}}}\n"
@@ -175,13 +190,20 @@ class DSLPrettyPrinter(ModelVisitor):
175
190
  return f"{var_decl.name}: {self.visit(var_decl.quantum_type)}"
176
191
 
177
192
  def visit_AnonPortDeclaration(self, port_decl: AnonPortDeclaration) -> str:
193
+ qualifier_str = (
194
+ f"{port_decl.type_qualifier} "
195
+ if port_decl.type_qualifier is not TypeQualifier.Quantum
196
+ else ""
197
+ )
178
198
  dir_str = (
179
199
  f"{port_decl.direction} "
180
- if port_decl.direction != PortDeclarationDirection.Inout
200
+ if port_decl.direction is not PortDeclarationDirection.Inout
181
201
  else ""
182
202
  )
183
203
  param_name = f"{port_decl.name}: " if port_decl.name is not None else ""
184
- return f"{dir_str}{param_name}{self.visit(port_decl.quantum_type)}"
204
+ return (
205
+ f"{qualifier_str}{dir_str}{param_name}{self.visit(port_decl.quantum_type)}"
206
+ )
185
207
 
186
208
  def visit_QuantumBit(self, qtype: QuantumBit) -> str:
187
209
  return "qbit"