classiq 0.74.0__py3-none-any.whl → 0.75.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 (37) hide show
  1. classiq/analyzer/show_interactive_hack.py +1 -1
  2. classiq/applications/qnn/qlayer.py +9 -0
  3. classiq/interface/_version.py +1 -1
  4. classiq/interface/debug_info/debug_info.py +2 -11
  5. classiq/interface/generator/expressions/proxies/classical/classical_array_proxy.py +5 -5
  6. classiq/interface/generator/expressions/proxies/classical/utils.py +2 -2
  7. classiq/interface/generator/functions/classical_type.py +30 -0
  8. classiq/interface/generator/functions/type_name.py +25 -3
  9. classiq/interface/generator/generated_circuit_data.py +11 -25
  10. classiq/interface/generator/synthesis_metadata/synthesis_execution_data.py +10 -13
  11. classiq/interface/helpers/versioned_model.py +12 -0
  12. classiq/interface/interface_version.py +1 -1
  13. classiq/interface/model/handle_binding.py +12 -0
  14. classiq/interface/model/statement_block.py +9 -1
  15. classiq/interface/model/within_apply_operation.py +12 -0
  16. classiq/model_expansions/atomic_expression_functions_defs.py +24 -8
  17. classiq/model_expansions/closure.py +13 -0
  18. classiq/model_expansions/evaluators/argument_types.py +6 -5
  19. classiq/model_expansions/evaluators/type_type_match.py +2 -2
  20. classiq/model_expansions/generative_functions.py +14 -8
  21. classiq/model_expansions/interpreters/base_interpreter.py +10 -13
  22. classiq/model_expansions/interpreters/frontend_generative_interpreter.py +21 -0
  23. classiq/model_expansions/interpreters/generative_interpreter.py +12 -0
  24. classiq/model_expansions/quantum_operations/allocate.py +22 -11
  25. classiq/model_expansions/quantum_operations/assignment_result_processor.py +2 -0
  26. classiq/model_expansions/quantum_operations/call_emitter.py +0 -5
  27. classiq/model_expansions/quantum_operations/emitter.py +1 -5
  28. classiq/model_expansions/quantum_operations/expression_evaluator.py +1 -0
  29. classiq/model_expansions/quantum_operations/handle_evaluator.py +1 -0
  30. classiq/model_expansions/transformers/model_renamer.py +3 -1
  31. classiq/model_expansions/visitors/symbolic_param_inference.py +197 -0
  32. classiq/qmod/qmod_variable.py +23 -1
  33. classiq/qmod/symbolic_expr.py +8 -2
  34. classiq/qmod/write_qmod.py +5 -1
  35. {classiq-0.74.0.dist-info → classiq-0.75.0.dist-info}/METADATA +1 -1
  36. {classiq-0.74.0.dist-info → classiq-0.75.0.dist-info}/RECORD +37 -36
  37. {classiq-0.74.0.dist-info → classiq-0.75.0.dist-info}/WHEEL +1 -1
@@ -24,7 +24,7 @@ async def handle_remote_app(circuit: QuantumProgram, display_url: bool = True) -
24
24
  )
25
25
 
26
26
  if display_url:
27
- print(f"Opening: {app_url}") # noqa: T201
27
+ print(f"Quantum program link: {app_url}") # noqa: T201
28
28
 
29
29
  webbrowser.open_new_tab(app_url)
30
30
 
@@ -1,5 +1,6 @@
1
1
  import functools
2
2
  import inspect
3
+ import os
3
4
  import typing
4
5
  from typing import Any, Callable, Optional, Union, overload
5
6
 
@@ -247,6 +248,14 @@ class QLayer(nn.Module):
247
248
  def _make_execute(
248
249
  self, quantum_program: SerializedQuantumProgram
249
250
  ) -> ExecuteFunction:
251
+ if os.environ.get("SDK_ENV") == "Studio":
252
+ try:
253
+ import classiq_studio_simulation
254
+
255
+ return classiq_studio_simulation.make_execute_qnn(quantum_program)
256
+ except ImportError:
257
+ pass
258
+
250
259
  session = ExecutionSession(quantum_program)
251
260
  self._session = session
252
261
 
@@ -3,5 +3,5 @@ from packaging.version import Version
3
3
  # This file was generated automatically
4
4
  # Please don't track in version control (DONTTRACK)
5
5
 
6
- SEMVER_VERSION = '0.74.0'
6
+ SEMVER_VERSION = '0.75.0'
7
7
  VERSION = str(Version(SEMVER_VERSION))
@@ -7,7 +7,6 @@ from pydantic import BaseModel, Field
7
7
  from classiq.interface.debug_info import back_ref_util
8
8
  from classiq.interface.generator.generated_circuit_data import (
9
9
  FunctionDebugInfoInterface,
10
- OperationLevel,
11
10
  StatementType,
12
11
  )
13
12
  from classiq.interface.model.block import Block
@@ -18,21 +17,15 @@ ParameterValue = Union[float, int, str, None]
18
17
 
19
18
  class FunctionDebugInfo(BaseModel):
20
19
  name: str
21
- level: OperationLevel = Field(default=OperationLevel.UNKNOWN)
22
20
  statement_type: Union[StatementType, None] = None
23
- is_allocate_or_free: bool = Field(default=False)
24
21
  is_inverse: bool = Field(default=False)
25
22
  release_by_inverse: bool = Field(default=False)
26
23
  port_to_passed_variable_map: dict[str, str] = Field(default_factory=dict)
27
24
  node: Optional[ConcreteQuantumStatement] = None
28
25
 
29
26
  @property
30
- def is_allocate_or_free_(self) -> bool:
31
- return (
32
- back_ref_util.is_allocate_or_free(self.node)
33
- if self.node is not None
34
- else self.is_allocate_or_free
35
- )
27
+ def is_allocate_or_free(self) -> bool:
28
+ return back_ref_util.is_allocate_or_free(self.node) if self.node else False
36
29
 
37
30
  def update_map_from_port_mapping(self, port_mapping: Mapping[str, str]) -> None:
38
31
  new_port_to_passed_variable_map = self.port_to_passed_variable_map.copy()
@@ -103,7 +96,5 @@ def new_function_debug_info_by_node(
103
96
  ) -> FunctionDebugInfo:
104
97
  return FunctionDebugInfo(
105
98
  name="",
106
- parameters=dict(),
107
- level=OperationLevel.QMOD_STATEMENT,
108
99
  node=node._as_back_ref(),
109
100
  )
@@ -3,7 +3,7 @@ from typing import TYPE_CHECKING, Union
3
3
 
4
4
  from sympy import Integer
5
5
 
6
- from classiq.interface.exceptions import ClassiqValueError
6
+ from classiq.interface.exceptions import ClassiqIndexError
7
7
  from classiq.interface.generator.expressions.expression import Expression
8
8
  from classiq.interface.generator.expressions.non_symbolic_expr import NonSymbolicExpr
9
9
  from classiq.interface.generator.expressions.proxies.classical.classical_proxy import (
@@ -51,9 +51,9 @@ class ClassicalArrayProxy(NonSymbolicExpr, ClassicalProxy):
51
51
  start = int(slice_.start)
52
52
  stop = int(slice_.stop)
53
53
  if start >= stop:
54
- raise ClassiqValueError("Array slice has non-positive length")
54
+ raise ClassiqIndexError("Array slice has non-positive length")
55
55
  if start < 0 or stop > self._length:
56
- raise ClassiqValueError("Array slice is out of bounds")
56
+ raise ClassiqIndexError("Array slice is out of bounds")
57
57
  return ClassicalArrayProxy(
58
58
  SlicedHandleBinding(
59
59
  base_handle=self.handle,
@@ -67,11 +67,11 @@ class ClassicalArrayProxy(NonSymbolicExpr, ClassicalProxy):
67
67
  def _get_subscript(self, index_: Union[int, Integer]) -> ClassicalProxy:
68
68
  index = int(index_)
69
69
  if index < 0:
70
- raise ClassiqValueError(
70
+ raise ClassiqIndexError(
71
71
  "Array index is out of bounds (negative indices are not supported)"
72
72
  )
73
73
  if index >= self._length:
74
- raise ClassiqValueError("Array index is out of bounds")
74
+ raise ClassiqIndexError("Array index is out of bounds")
75
75
  return self._element_type.get_classical_proxy(
76
76
  SubscriptHandleBinding(
77
77
  base_handle=self._handle, index=Expression(expr=str(index_))
@@ -15,7 +15,7 @@ from classiq.interface.generator.functions.classical_type import (
15
15
  ClassicalArray,
16
16
  ClassicalType,
17
17
  )
18
- from classiq.interface.generator.functions.type_name import TypeName
18
+ from classiq.interface.generator.functions.type_name import Struct
19
19
 
20
20
 
21
21
  def get_proxy_type(proxy: ClassicalProxy) -> ClassicalType:
@@ -26,7 +26,7 @@ def get_proxy_type(proxy: ClassicalProxy) -> ClassicalType:
26
26
  element_type=proxy._element_type, size=proxy.length
27
27
  )
28
28
  elif isinstance(proxy, ClassicalStructProxy):
29
- classical_type = TypeName(name=proxy._decl.name)
29
+ classical_type = Struct(name=proxy._decl.name)
30
30
  else:
31
31
  raise ClassiqInternalExpansionError(
32
32
  f"Unrecognized classical proxy {type(proxy).__name__}"
@@ -5,6 +5,7 @@ from pydantic import ConfigDict, PrivateAttr
5
5
  from typing_extensions import Self
6
6
 
7
7
  from classiq.interface.ast_node import HashableASTNode
8
+ from classiq.interface.generator.expressions.expression import Expression
8
9
  from classiq.interface.generator.expressions.proxies.classical.classical_array_proxy import (
9
10
  ClassicalArrayProxy,
10
11
  )
@@ -39,9 +40,22 @@ class ClassicalType(HashableASTNode):
39
40
  def is_generative(self) -> bool:
40
41
  return self._is_generative
41
42
 
43
+ @property
44
+ def is_purely_declarative(self) -> bool:
45
+ return not self._is_generative
46
+
42
47
  def get_classical_proxy(self, handle: HandleBinding) -> ClassicalProxy:
43
48
  return ClassicalScalarProxy(handle, self)
44
49
 
50
+ @property
51
+ def expressions(self) -> list[Expression]:
52
+ return []
53
+
54
+ def clear_flags(self) -> Self:
55
+ res = self.model_copy()
56
+ res._is_generative = False
57
+ return res
58
+
45
59
 
46
60
  class Integer(ClassicalType):
47
61
  kind: Literal["int"]
@@ -82,6 +96,14 @@ class ClassicalList(ClassicalType):
82
96
  def get_classical_proxy(self, handle: HandleBinding) -> ClassicalProxy:
83
97
  raise NotImplementedError
84
98
 
99
+ @property
100
+ def expressions(self) -> list[Expression]:
101
+ return self.element_type.expressions
102
+
103
+ @property
104
+ def is_purely_declarative(self) -> bool:
105
+ return super().is_purely_declarative and self.element_type.is_purely_declarative
106
+
85
107
 
86
108
  class StructMetaType(ClassicalType):
87
109
  kind: Literal["type_proxy"]
@@ -108,6 +130,14 @@ class ClassicalArray(ClassicalType):
108
130
  def get_classical_proxy(self, handle: HandleBinding) -> ClassicalProxy:
109
131
  return ClassicalArrayProxy(handle, self.element_type, self.size)
110
132
 
133
+ @property
134
+ def expressions(self) -> list[Expression]:
135
+ return self.element_type.expressions
136
+
137
+ @property
138
+ def is_purely_declarative(self) -> bool:
139
+ return super().is_purely_declarative and self.element_type.is_purely_declarative
140
+
111
141
 
112
142
  class OpaqueHandle(ClassicalType):
113
143
  pass
@@ -115,12 +115,34 @@ class TypeName(ClassicalType, QuantumType):
115
115
 
116
116
  @property
117
117
  def expressions(self) -> list[Expression]:
118
- return list(
119
- chain.from_iterable(
120
- field_type.expressions for field_type in self.fields.values()
118
+ if self.has_fields:
119
+ return list(
120
+ chain.from_iterable(
121
+ field_type.expressions for field_type in self.fields.values()
122
+ )
121
123
  )
124
+ if self.has_classical_struct_decl:
125
+ return list(
126
+ chain.from_iterable(
127
+ field_type.expressions
128
+ for field_type in self.classical_struct_decl.variables.values()
129
+ )
130
+ )
131
+ return []
132
+
133
+ @property
134
+ def is_purely_declarative(self) -> bool:
135
+ if self.is_enum:
136
+ return False
137
+ return super().is_purely_declarative and all(
138
+ field_type.is_purely_declarative
139
+ for field_type in self.classical_struct_decl.variables.values()
122
140
  )
123
141
 
142
+ @property
143
+ def is_enum(self) -> bool:
144
+ return not self.has_fields and not self.has_classical_struct_decl
145
+
124
146
 
125
147
  class Enum(TypeName):
126
148
  pass
@@ -40,6 +40,7 @@ IOQubitMapping: TypeAlias = dict[str, tuple[int, ...]]
40
40
  CLASSIQ_HIERARCHY_SEPARATOR: Literal["__"] = "__"
41
41
  QASM_SEPARATOR = "_"
42
42
  SPLIT_MARKER: str = "part"
43
+ ARITH_ENGINE_PREFIX = "arith_eng__"
43
44
  PART_SUFFIX_REGEX = re.compile(
44
45
  rf".+{QASM_SEPARATOR}{SPLIT_MARKER}{QASM_SEPARATOR}(\d+)$"
45
46
  )
@@ -177,10 +178,8 @@ class FunctionDebugInfoInterface(pydantic.BaseModel):
177
178
  absolute_qubits: Optional[tuple[int, ...]] = Field(default=None)
178
179
  is_basis_gate: Optional[bool] = Field(default=None)
179
180
  is_inverse: bool = Field(default=False)
180
- is_allocate_or_free: bool = Field(default=False)
181
181
  is_unitary: bool = Field(default=True, exclude=True)
182
182
  uuid: Optional[UUID] = Field(default=None, exclude=True)
183
- level: OperationLevel = Field(default=OperationLevel.UNKNOWN)
184
183
  port_to_passed_variable_map: dict[str, str] = Field(default={})
185
184
  release_by_inverse: bool = Field(default=False)
186
185
  back_refs: StatementBlock = Field(default_factory=list)
@@ -190,16 +189,8 @@ class FunctionDebugInfoInterface(pydantic.BaseModel):
190
189
  override_debug_info: Optional["FunctionDebugInfoInterface"] = None
191
190
 
192
191
  @property
193
- def is_allocate_or_free_(self) -> bool:
194
- """
195
- temporary measure to handle the fact that in the current release we do not have
196
- the backref, and therefore fallback to the old field of is_allocate_or_free
197
- """
198
- return (
199
- is_allocate_or_free_by_backref(self.back_refs)
200
- if bool(self.back_refs)
201
- else self.is_allocate_or_free
202
- )
192
+ def is_allocate_or_free(self) -> bool:
193
+ return is_allocate_or_free_by_backref(self.back_refs)
203
194
 
204
195
  @property
205
196
  def name(self) -> str:
@@ -215,10 +206,8 @@ class FunctionDebugInfoInterface(pydantic.BaseModel):
215
206
  if isinstance(back_ref, QuantumFunctionCall):
216
207
  name = generate_original_function_name(back_ref.func_name)
217
208
  if part_match := PART_SUFFIX_REGEX.match(generated_name):
218
- suffix = f" [{part_match.group(1)}]"
219
- else:
220
- suffix = ""
221
- return f"{name}{suffix}"
209
+ name += f" [{part_match.group(1)}]"
210
+ return name.removeprefix(ARITH_ENGINE_PREFIX)
222
211
 
223
212
  statement_kind: str = back_ref.kind
224
213
  if isinstance(back_ref, ArithmeticOperation):
@@ -230,19 +219,16 @@ class FunctionDebugInfoInterface(pydantic.BaseModel):
230
219
  return self.back_refs[0] if self.back_refs else None
231
220
 
232
221
  @property
233
- def level_(self) -> OperationLevel:
222
+ def level(self) -> OperationLevel:
234
223
  # Temp fix for currently "supported" statements
235
224
  if self.name in {StatementType.CONTROL, StatementType.POWER}:
236
225
  return OperationLevel.QMOD_STATEMENT
237
226
 
238
- back_ref = self.first_back_ref
239
- if back_ref is None:
240
- # This is the expected behavior for the case where there's not back ref.
241
- # We will use it once we can rely on the existence of the back ref in
242
- # all expected cases (non-engine calls)
243
- # return OperationLevel.ENGINE_FUNCTION_CALL
244
- return self.level
245
- if isinstance(back_ref, QuantumFunctionCall):
227
+ if self.first_back_ref is None:
228
+ # we use ENGINE_FUNCTION_CALL in case where there's not back ref
229
+ return OperationLevel.ENGINE_FUNCTION_CALL
230
+
231
+ if isinstance(self.first_back_ref, QuantumFunctionCall):
246
232
  return OperationLevel.QMOD_FUNCTION_CALL
247
233
  return OperationLevel.QMOD_STATEMENT
248
234
 
@@ -1,10 +1,10 @@
1
+ from itertools import chain
1
2
  from typing import Optional
2
3
 
3
4
  import pydantic
4
5
  import sympy
5
6
 
6
7
  from classiq.interface.backend.pydantic_backend import PydanticExecutionParameter
7
- from classiq.interface.exceptions import ClassiqValueError
8
8
  from classiq.interface.generator.parameters import ParameterType
9
9
 
10
10
 
@@ -12,15 +12,10 @@ class FunctionExecutionData(pydantic.BaseModel):
12
12
  power_parameter: Optional[ParameterType] = pydantic.Field(default=None)
13
13
 
14
14
  @property
15
- def power_var(self) -> Optional[str]:
15
+ def power_vars(self) -> Optional[list[str]]:
16
16
  if self.power_parameter is None:
17
17
  return None
18
- power_vars = sympy.sympify(self.power_parameter).free_symbols
19
- if len(power_vars) != 1:
20
- raise ClassiqValueError(
21
- f"Power parameter expression: {self.power_parameter} must contain exactly one variable"
22
- )
23
- return str(list(power_vars)[0])
18
+ return list(map(str, sympy.sympify(self.power_parameter).free_symbols))
24
19
 
25
20
 
26
21
  class ExecutionData(pydantic.BaseModel):
@@ -32,8 +27,10 @@ class ExecutionData(pydantic.BaseModel):
32
27
  def execution_parameters(
33
28
  self,
34
29
  ) -> set[PydanticExecutionParameter]:
35
- return {
36
- function_execution_data.power_var
37
- for function_execution_data in self.function_execution.values()
38
- if function_execution_data.power_var is not None
39
- }
30
+ return set(
31
+ chain.from_iterable(
32
+ function_execution_data.power_vars
33
+ for function_execution_data in self.function_execution.values()
34
+ if function_execution_data.power_vars is not None
35
+ )
36
+ )
@@ -1,7 +1,19 @@
1
+ from typing import Any
2
+
1
3
  import pydantic
2
4
 
3
5
  from classiq.interface._version import VERSION
6
+ from classiq.interface.interface_version import INTERFACE_VERSION
4
7
 
5
8
 
6
9
  class VersionedModel(pydantic.BaseModel):
7
10
  version: str = pydantic.Field(default=VERSION)
11
+ interface_version: str = pydantic.Field(default="0")
12
+
13
+ @pydantic.model_validator(mode="before")
14
+ @classmethod
15
+ def set_interface_version(cls, values: dict[str, Any]) -> dict[str, Any]:
16
+ # We "override" the default value mechanism so that the schema does not depend on the version
17
+ if "interface_version" not in values:
18
+ values["interface_version"] = INTERFACE_VERSION
19
+ return values
@@ -1 +1 @@
1
- INTERFACE_VERSION = "9"
1
+ INTERFACE_VERSION = "10"
@@ -67,6 +67,9 @@ class HandleBinding(ASTNode):
67
67
  def is_constant(self) -> bool:
68
68
  return True
69
69
 
70
+ def expressions(self) -> list[Expression]:
71
+ return []
72
+
70
73
 
71
74
  class NestedHandleBinding(HandleBinding):
72
75
  base_handle: "ConcreteHandleBinding"
@@ -111,6 +114,9 @@ class NestedHandleBinding(HandleBinding):
111
114
  def is_constant(self) -> bool:
112
115
  return self.base_handle.is_constant()
113
116
 
117
+ def expressions(self) -> list[Expression]:
118
+ return self.base_handle.expressions()
119
+
114
120
 
115
121
  class SubscriptHandleBinding(NestedHandleBinding):
116
122
  index: Expression
@@ -191,6 +197,9 @@ class SubscriptHandleBinding(NestedHandleBinding):
191
197
  and self.index.is_constant()
192
198
  )
193
199
 
200
+ def expressions(self) -> list[Expression]:
201
+ return super().expressions() + [self.index]
202
+
194
203
 
195
204
  class SlicedHandleBinding(NestedHandleBinding):
196
205
  start: Expression
@@ -305,6 +314,9 @@ class SlicedHandleBinding(NestedHandleBinding):
305
314
  and self.end.is_constant()
306
315
  )
307
316
 
317
+ def expressions(self) -> list[Expression]:
318
+ return super().expressions() + [self.start, self.end]
319
+
308
320
 
309
321
  class FieldHandleBinding(NestedHandleBinding):
310
322
  field: str
@@ -24,7 +24,12 @@ from classiq.interface.model.repeat import Repeat
24
24
  from classiq.interface.model.variable_declaration_statement import (
25
25
  VariableDeclarationStatement,
26
26
  )
27
- from classiq.interface.model.within_apply_operation import WithinApply
27
+ from classiq.interface.model.within_apply_operation import (
28
+ Action,
29
+ Compute,
30
+ Uncompute,
31
+ WithinApply,
32
+ )
28
33
 
29
34
  ConcreteQuantumStatement = Annotated[
30
35
  Union[
@@ -43,6 +48,9 @@ ConcreteQuantumStatement = Annotated[
43
48
  WithinApply,
44
49
  PhaseOperation,
45
50
  Block,
51
+ Compute,
52
+ Action,
53
+ Uncompute,
46
54
  ],
47
55
  Field(..., discriminator="kind"),
48
56
  ]
@@ -15,3 +15,15 @@ class WithinApply(QuantumOperation):
15
15
 
16
16
  def _as_back_ref(self: ASTNodeType) -> ASTNodeType:
17
17
  return reset_lists(self, ["compute", "action"])
18
+
19
+
20
+ class Compute(QuantumOperation):
21
+ kind: Literal["Compute"]
22
+
23
+
24
+ class Action(QuantumOperation):
25
+ kind: Literal["Action"]
26
+
27
+
28
+ class Uncompute(QuantumOperation):
29
+ kind: Literal["Uncompute"]
@@ -15,6 +15,9 @@ from classiq.interface.generator.expressions.expression_types import (
15
15
  from classiq.interface.generator.expressions.proxies.classical.any_classical_value import (
16
16
  AnyClassicalValue,
17
17
  )
18
+ from classiq.interface.generator.expressions.proxies.classical.classical_array_proxy import (
19
+ ClassicalArrayProxy,
20
+ )
18
21
  from classiq.interface.generator.expressions.proxies.classical.classical_proxy import (
19
22
  ClassicalProxy,
20
23
  )
@@ -170,7 +173,11 @@ def get_field(
170
173
  return AnyClassicalValue(f"({proxy}).{field}")
171
174
  if isinstance(proxy, type) and issubclass(proxy, Enum):
172
175
  return getattr(proxy, field)
173
- if isinstance(proxy, Symbol) and not isinstance(proxy, QmodSizedProxy):
176
+ if (
177
+ isinstance(proxy, Symbol)
178
+ and not isinstance(proxy, QmodSizedProxy)
179
+ and not isinstance(proxy, ClassicalProxy)
180
+ ):
174
181
  raise ClassiqExpansionError(
175
182
  f"Cannot evaluate '{proxy}.{field}': Variable {str(proxy)!r} is not "
176
183
  f"initialized"
@@ -229,7 +236,9 @@ def _is_qmod_value(val: Any) -> bool:
229
236
 
230
237
 
231
238
  def do_subscript(value: Any, index: Any) -> Any:
232
- if not isinstance(value, list) or not isinstance(index, QmodQNumProxy):
239
+ if not isinstance(value, (list, ClassicalArrayProxy)) or not isinstance(
240
+ index, QmodQNumProxy
241
+ ):
233
242
  if isinstance(index, (QmodSizedProxy, QmodStructInstance)):
234
243
  raise ClassiqExpansionError(
235
244
  f"Subscript {value}[{index}] is not supported. Supported subscripts "
@@ -251,15 +260,22 @@ def do_subscript(value: Any, index: Any) -> Any:
251
260
  "Quantum numeric subscript must be an unsigned integer (is_signed=False, "
252
261
  "fraction_digits=0)"
253
262
  )
254
- if len(value) != 2**index.size:
263
+ if isinstance(value, ClassicalArrayProxy):
264
+ length = value.length
265
+ else:
266
+ length = len(value)
267
+ if length != 2**index.size:
255
268
  raise ClassiqExpansionError(
256
269
  f"Quantum numeric subscript size mismatch: The quantum numeric has "
257
- f"{index.size} qubits but the list size is {len(value)} != 2**{index.size}"
270
+ f"{index.size} qubits but the list size is {length} != 2**{index.size}"
271
+ )
272
+ if isinstance(value, ClassicalArrayProxy):
273
+ return AnyClassicalValue(f"{value}[{index}]")
274
+ else:
275
+ return Piecewise(
276
+ *[(item, Eq(index, idx)) for idx, item in enumerate(value[:-1])],
277
+ (value[-1], True),
258
278
  )
259
- return Piecewise(
260
- *[(item, Eq(index, idx)) for idx, item in enumerate(value[:-1])],
261
- (value[-1], True),
262
- )
263
279
 
264
280
 
265
281
  def do_slice(value: Any, lower: Any, upper: Any) -> Any:
@@ -8,6 +8,15 @@ from typing import Any, Optional
8
8
  from typing_extensions import Self
9
9
 
10
10
  from classiq.interface.exceptions import ClassiqInternalExpansionError
11
+ from classiq.interface.generator.expressions.proxies.classical.classical_proxy import (
12
+ ClassicalProxy,
13
+ )
14
+ from classiq.interface.generator.expressions.proxies.classical.classical_struct_proxy import (
15
+ ClassicalStructProxy,
16
+ )
17
+ from classiq.interface.generator.expressions.proxies.classical.utils import (
18
+ get_proxy_type,
19
+ )
11
20
  from classiq.interface.model.quantum_function_declaration import (
12
21
  NamedParamsQuantumFunctionDeclaration,
13
22
  PositionalArg,
@@ -139,6 +148,10 @@ def _evaluated_arg_to_str(arg: Any) -> str:
139
148
  return _evaluated_one_operand_to_str(arg)
140
149
  if isinstance(arg, list) and arg and isinstance(arg[0], FunctionClosure):
141
150
  return _evaluated_operands_list_to_str(arg)
151
+ if isinstance(arg, ClassicalProxy):
152
+ if isinstance(arg, ClassicalStructProxy):
153
+ return repr(arg.struct_declaration)
154
+ return repr(get_proxy_type(arg))
142
155
  return evaluated_classical_param_to_str(arg)
143
156
 
144
157
 
@@ -34,9 +34,10 @@ def add_information_from_output_arguments(
34
34
  if parameter.direction != PortDeclarationDirection.Output:
35
35
  continue
36
36
 
37
- copy_type_information(
38
- parameter.quantum_type,
39
- argument_as_quantum_symbol.quantum_type,
40
- str(argument_as_quantum_symbol.handle),
41
- )
37
+ if parameter.quantum_type.is_evaluated:
38
+ copy_type_information(
39
+ parameter.quantum_type,
40
+ argument_as_quantum_symbol.quantum_type,
41
+ str(argument_as_quantum_symbol.handle),
42
+ )
42
43
  return args
@@ -85,7 +85,7 @@ def _check_classical_type_match(
85
85
  ) -> None:
86
86
  if (
87
87
  not isinstance(op_param, AnonClassicalParameterDeclaration)
88
- or decl_param.classical_type.set_generative()
89
- != op_param.classical_type.set_generative()
88
+ or decl_param.classical_type.clear_flags()
89
+ != op_param.classical_type.clear_flags()
90
90
  ):
91
91
  raise ClassiqExpansionError(error_message)
@@ -81,11 +81,14 @@ def translate_ast_arg_to_python_qmod(param: PositionalArg, evaluated: Evaluated)
81
81
  ]
82
82
  classical_value = evaluated.value
83
83
  if isinstance(classical_value, QmodStructInstance):
84
- return CParamStruct(
85
- expr=param.name,
86
- struct_type=Struct(name=classical_value.struct_declaration.name),
87
- qmodule=QMODULE,
88
- )
84
+ if param.classical_type.is_purely_declarative:
85
+ return CParamStruct(
86
+ expr=param.name,
87
+ struct_type=Struct(name=classical_value.struct_declaration.name),
88
+ qmodule=QMODULE,
89
+ )
90
+ else:
91
+ return get_sdk_compatible_python_object(dict(classical_value.fields))
89
92
  if isinstance(classical_value, ClassicalProxy):
90
93
  return create_param(
91
94
  str(classical_value.handle), get_proxy_type(classical_value), QMODULE
@@ -111,15 +114,18 @@ class _InterpreterExpandable(QFunc):
111
114
  for name, func in self._qmodule.native_defs.items()
112
115
  if name not in self._interpreter._top_level_scope
113
116
  }
117
+ generative_functions = self._qmodule.generative_functions
114
118
  self._interpreter.update_declarative_functions(
115
119
  declarative_functions, self._qmodule
116
120
  )
117
- self._interpreter.update_generative_functions(
118
- self._qmodule.generative_functions
119
- )
121
+ self._interpreter.update_generative_functions(generative_functions)
120
122
  func_decls = self._get_function_declarations()
121
123
  for dec_func in declarative_functions.values():
122
124
  resolve_function_calls(dec_func, func_decls)
125
+ self._interpreter.infer_symbolic_parameters(
126
+ list(declarative_functions.values()),
127
+ [func.func_decl for func in generative_functions.values()],
128
+ )
123
129
  resolve_function_calls(dummy_function, func_decls)
124
130
  stmt = dummy_function.body[-1]
125
131
  with generative_mode_context(False):
@@ -9,14 +9,15 @@ from typing import Any, cast
9
9
  import sympy
10
10
  from pydantic import ValidationError
11
11
 
12
- from classiq.interface.debug_info.debug_info import FunctionDebugInfo
12
+ from classiq.interface.debug_info.debug_info import (
13
+ new_function_debug_info_by_node,
14
+ )
13
15
  from classiq.interface.exceptions import (
14
16
  ClassiqError,
15
17
  ClassiqExpansionError,
16
18
  ClassiqInternalExpansionError,
17
19
  )
18
20
  from classiq.interface.generator.expressions.expression import Expression
19
- from classiq.interface.generator.generated_circuit_data import OperationLevel
20
21
  from classiq.interface.generator.types.compilation_metadata import CompilationMetadata
21
22
  from classiq.interface.model.handle_binding import (
22
23
  FieldHandleBinding,
@@ -237,20 +238,16 @@ class BaseInterpreter:
237
238
  if source_ref is not None
238
239
  else nullcontext()
239
240
  )
240
- if statement.uuid not in self._model.debug_info:
241
- self._model.debug_info[statement.uuid] = FunctionDebugInfo(
242
- name="",
243
- parameters={},
244
- level=OperationLevel.QMOD_STATEMENT,
245
- statement_type=None,
246
- is_allocate_or_free=False,
247
- is_inverse=False,
248
- port_to_passed_variable_map={},
249
- node=statement._as_back_ref(),
250
- )
241
+ self.add_to_debug_info(statement)
251
242
  with error_context, self._builder.source_ref_context(source_ref):
252
243
  self.emit(statement)
253
244
 
245
+ def add_to_debug_info(self, statement: QuantumStatement) -> None:
246
+ if statement.uuid not in self._model.debug_info:
247
+ self._model.debug_info[statement.uuid] = new_function_debug_info_by_node(
248
+ statement # type: ignore[arg-type]
249
+ )
250
+
254
251
  def _expand_operation(self, operation: Closure) -> OperationContext:
255
252
  with self._builder.operation_context(operation) as context:
256
253
  if isinstance(operation, FunctionClosure) and (