classiq 0.76.0__py3-none-any.whl → 0.78.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 (63) hide show
  1. classiq/applications/chemistry/chemistry_model_constructor.py +7 -6
  2. classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +9 -1
  3. classiq/applications/combinatorial_optimization/combinatorial_problem.py +4 -3
  4. classiq/applications/iqae/__init__.py +0 -0
  5. classiq/applications/iqae/iqae.py +207 -0
  6. classiq/execution/__init__.py +1 -1
  7. classiq/interface/_version.py +1 -1
  8. classiq/interface/applications/iqae/__init__.py +0 -0
  9. classiq/interface/applications/iqae/generic_iqae.py +222 -0
  10. classiq/interface/applications/iqae/iqae_result.py +45 -0
  11. classiq/interface/debug_info/debug_info.py +3 -0
  12. classiq/interface/executor/execution_result.py +1 -1
  13. classiq/interface/executor/user_budget.py +1 -1
  14. classiq/interface/generator/expressions/proxies/classical/any_classical_value.py +9 -3
  15. classiq/interface/generator/expressions/proxies/quantum/qmod_qarray_proxy.py +6 -15
  16. classiq/interface/generator/expressions/proxies/quantum/qmod_qscalar_proxy.py +14 -5
  17. classiq/interface/generator/expressions/proxies/quantum/qmod_sized_proxy.py +5 -3
  18. classiq/interface/generator/generated_circuit_data.py +18 -7
  19. classiq/interface/ide/visual_model.py +2 -0
  20. classiq/interface/model/handle_binding.py +8 -0
  21. classiq/interface/model/model.py +3 -6
  22. classiq/interface/model/quantum_function_call.py +31 -1
  23. classiq/interface/model/quantum_statement.py +14 -1
  24. classiq/interface/source_reference.py +7 -2
  25. classiq/model_expansions/capturing/captured_vars.py +16 -6
  26. classiq/model_expansions/closure.py +1 -58
  27. classiq/model_expansions/evaluators/arg_type_match.py +2 -2
  28. classiq/model_expansions/evaluators/argument_types.py +4 -5
  29. classiq/model_expansions/evaluators/classical_expression.py +9 -9
  30. classiq/model_expansions/evaluators/parameter_types.py +19 -11
  31. classiq/model_expansions/expression_evaluator.py +20 -11
  32. classiq/model_expansions/generative_functions.py +1 -1
  33. classiq/model_expansions/interpreters/base_interpreter.py +27 -15
  34. classiq/model_expansions/interpreters/frontend_generative_interpreter.py +0 -16
  35. classiq/model_expansions/interpreters/generative_interpreter.py +4 -4
  36. classiq/model_expansions/quantum_operations/allocate.py +2 -2
  37. classiq/model_expansions/quantum_operations/assignment_result_processor.py +3 -1
  38. classiq/model_expansions/quantum_operations/call_emitter.py +91 -42
  39. classiq/model_expansions/quantum_operations/emitter.py +7 -7
  40. classiq/model_expansions/quantum_operations/function_calls_cache.py +84 -0
  41. classiq/model_expansions/scope.py +73 -13
  42. classiq/model_expansions/transformers/model_renamer.py +2 -2
  43. classiq/model_expansions/transformers/type_qualifier_inference.py +183 -0
  44. classiq/model_expansions/utils/text_utils.py +4 -2
  45. classiq/model_expansions/visitors/symbolic_param_inference.py +4 -15
  46. classiq/open_library/functions/lookup_table.py +1 -1
  47. classiq/open_library/functions/state_preparation.py +1 -1
  48. classiq/qmod/builtins/classical_execution_primitives.py +1 -1
  49. classiq/qmod/create_model_function.py +21 -3
  50. classiq/qmod/global_declarative_switch.py +19 -0
  51. classiq/qmod/native/pretty_printer.py +4 -0
  52. classiq/qmod/pretty_print/pretty_printer.py +4 -0
  53. classiq/qmod/qfunc.py +31 -23
  54. classiq/qmod/qmod_variable.py +7 -4
  55. classiq/qmod/quantum_expandable.py +29 -1
  56. classiq/qmod/quantum_function.py +26 -19
  57. classiq/qmod/utilities.py +4 -0
  58. classiq/qmod/write_qmod.py +36 -10
  59. classiq/synthesis.py +7 -6
  60. {classiq-0.76.0.dist-info → classiq-0.78.0.dist-info}/METADATA +1 -1
  61. {classiq-0.76.0.dist-info → classiq-0.78.0.dist-info}/RECORD +62 -55
  62. classiq/interface/executor/iqae_result.py +0 -17
  63. {classiq-0.76.0.dist-info → classiq-0.78.0.dist-info}/WHEEL +0 -0
@@ -1,7 +1,7 @@
1
1
  from collections.abc import Mapping
2
2
  from typing import TYPE_CHECKING, Any, Union
3
3
 
4
- from sympy import Integer
4
+ import sympy
5
5
 
6
6
  from classiq.interface.exceptions import ClassiqValueError
7
7
  from classiq.interface.generator.expressions.expression import Expression
@@ -28,22 +28,20 @@ class QmodQArrayProxy(NonSymbolicExpr, QmodSizedProxy):
28
28
  self,
29
29
  handle: HandleBinding,
30
30
  element_type: "QuantumType",
31
- element_size: int,
32
- length: int,
31
+ element_size: Union[int, sympy.Basic],
32
+ length: Union[int, sympy.Basic],
33
33
  ) -> None:
34
34
  super().__init__(handle, element_size * length)
35
35
  self._length = length
36
36
  self._element_type = element_type
37
37
  self._element_size = element_size
38
38
 
39
- def __getitem__(self, key: Union[slice, int, Integer]) -> "QmodSizedProxy":
39
+ def __getitem__(self, key: Any) -> "QmodSizedProxy":
40
40
  return (
41
41
  self._get_slice(key) if isinstance(key, slice) else self._get_subscript(key)
42
42
  )
43
43
 
44
- def _get_subscript(self, index: Union[int, Integer]) -> "QmodSizedProxy":
45
- if isinstance(index, Integer):
46
- index = int(index)
44
+ def _get_subscript(self, index: Any) -> "QmodSizedProxy":
47
45
  return self._element_type.get_proxy(
48
46
  SubscriptHandleBinding(
49
47
  base_handle=self.handle,
@@ -54,13 +52,6 @@ class QmodQArrayProxy(NonSymbolicExpr, QmodSizedProxy):
54
52
  def _get_slice(self, slice_: slice) -> "QmodSizedProxy":
55
53
  if slice_.step is not None:
56
54
  raise ClassiqValueError(ILLEGAL_SLICING_STEP_MSG)
57
- if isinstance(slice_.start, Integer):
58
- slice_ = slice(int(slice_.start), slice_.stop)
59
- if isinstance(slice_.stop, Integer):
60
- slice_ = slice(slice_.start, int(slice_.stop))
61
- if not isinstance(slice_.start, int) or not isinstance(slice_.stop, int):
62
- raise ClassiqValueError(ILLEGAL_SLICE_MSG)
63
-
64
55
  return QmodQArrayProxy(
65
56
  SlicedHandleBinding(
66
57
  base_handle=self.handle,
@@ -77,7 +68,7 @@ class QmodQArrayProxy(NonSymbolicExpr, QmodSizedProxy):
77
68
  return "Quantum array"
78
69
 
79
70
  @property
80
- def len(self) -> int:
71
+ def len(self) -> Union[int, sympy.Basic]:
81
72
  return self._length
82
73
 
83
74
  @property
@@ -1,6 +1,7 @@
1
1
  from collections.abc import Mapping
2
- from typing import Any, Optional
2
+ from typing import Any, Optional, Union
3
3
 
4
+ import sympy
4
5
  from sympy import Symbol
5
6
  from typing_extensions import Self
6
7
 
@@ -36,10 +37,18 @@ class QmodQBitProxy(QmodQScalarProxy):
36
37
 
37
38
  class QmodQNumProxy(QmodQScalarProxy):
38
39
  def __init__(
39
- self, handle: HandleBinding, size: int, fraction_digits: int, is_signed: bool
40
+ self,
41
+ handle: HandleBinding,
42
+ size: Union[int, sympy.Basic],
43
+ fraction_digits: Union[int, sympy.Basic],
44
+ is_signed: Union[bool, sympy.Basic],
40
45
  ) -> None:
41
46
  super().__init__(handle, size)
42
- if fraction_digits > size:
47
+ if (
48
+ isinstance(fraction_digits, int)
49
+ and isinstance(size, int)
50
+ and fraction_digits > size
51
+ ):
43
52
  raise ClassiqValueError(
44
53
  f"Quantum numeric of size {size} cannot have {fraction_digits} "
45
54
  f"fraction digits"
@@ -52,11 +61,11 @@ class QmodQNumProxy(QmodQScalarProxy):
52
61
  return "Quantum numeric"
53
62
 
54
63
  @property
55
- def fraction_digits(self) -> int:
64
+ def fraction_digits(self) -> Union[int, sympy.Basic]:
56
65
  return self._fraction_digits
57
66
 
58
67
  @property
59
- def is_signed(self) -> bool:
68
+ def is_signed(self) -> Union[bool, sympy.Basic]:
60
69
  return self._is_signed
61
70
 
62
71
  @property
@@ -1,17 +1,19 @@
1
1
  from collections.abc import Mapping
2
- from typing import TYPE_CHECKING, Any
2
+ from typing import TYPE_CHECKING, Any, Union
3
+
4
+ import sympy
3
5
 
4
6
  if TYPE_CHECKING:
5
7
  from classiq.interface.model.handle_binding import HandleBinding
6
8
 
7
9
 
8
10
  class QmodSizedProxy:
9
- def __init__(self, handle: "HandleBinding", size: int) -> None:
11
+ def __init__(self, handle: "HandleBinding", size: Union[int, sympy.Basic]) -> None:
10
12
  self._handle = handle
11
13
  self._size = size
12
14
 
13
15
  @property
14
- def size(self) -> int:
16
+ def size(self) -> Union[int, sympy.Basic]:
15
17
  return self._size
16
18
 
17
19
  def __str__(self) -> str:
@@ -145,6 +145,8 @@ class StatementType(StrEnum):
145
145
  POWER = "power"
146
146
  INVERT = "invert"
147
147
  WITHIN_APPLY = "within apply"
148
+ WITHIN = "within"
149
+ APPLY = "apply"
148
150
  ASSIGN = "assign"
149
151
  ASSIGN_AMPLITUDE = "assign amplitude"
150
152
  PHASE = "phase"
@@ -161,6 +163,9 @@ STATEMENTS_NAME: dict[str, StatementType] = {
161
163
  "Power": StatementType.POWER,
162
164
  "Invert": StatementType.INVERT,
163
165
  "WithinApply": StatementType.WITHIN_APPLY,
166
+ "Compute": StatementType.WITHIN,
167
+ "Action": StatementType.APPLY,
168
+ "Uncompute": StatementType.WITHIN,
164
169
  ArithmeticOperationKind.Assignment.value: StatementType.ASSIGN,
165
170
  "InplaceBinaryOperation": StatementType.ASSIGN,
166
171
  "AmplitudeLoadingOperation": StatementType.ASSIGN_AMPLITUDE,
@@ -176,6 +181,7 @@ class FunctionDebugInfoInterface(pydantic.BaseModel):
176
181
  children: list["FunctionDebugInfoInterface"]
177
182
  relative_qubits: tuple[int, ...]
178
183
  absolute_qubits: Optional[tuple[int, ...]] = Field(default=None)
184
+ control_variable: Optional[str] = Field(default=None)
179
185
  is_basis_gate: Optional[bool] = Field(default=None)
180
186
  is_inverse: bool = Field(default=False)
181
187
  is_unitary: bool = Field(default=True, exclude=True)
@@ -205,14 +211,21 @@ class FunctionDebugInfoInterface(pydantic.BaseModel):
205
211
 
206
212
  if isinstance(back_ref, QuantumFunctionCall):
207
213
  name = generate_original_function_name(back_ref.func_name)
208
- if part_match := PART_SUFFIX_REGEX.match(generated_name):
209
- name += f" [{part_match.group(1)}]"
210
- return name.removeprefix(ARITH_ENGINE_PREFIX)
214
+ return self.add_suffix_from_generated_name(generated_name, name)
211
215
 
212
216
  statement_kind: str = back_ref.kind
213
217
  if isinstance(back_ref, ArithmeticOperation):
214
218
  statement_kind = back_ref.operation_kind.value
215
- return STATEMENTS_NAME[statement_kind]
219
+ return self.add_suffix_from_generated_name(
220
+ generated_name, STATEMENTS_NAME[statement_kind]
221
+ )
222
+
223
+ def add_suffix_from_generated_name(self, generated_name: str, name: str) -> str:
224
+ if part_match := PART_SUFFIX_REGEX.match(generated_name):
225
+ suffix = f" [{part_match.group(1)}]"
226
+ else:
227
+ suffix = ""
228
+ return f"{name}{suffix}"
216
229
 
217
230
  @property
218
231
  def first_back_ref(self) -> Optional[ConcreteQuantumStatement]:
@@ -347,9 +360,7 @@ class FunctionDebugInfoInterface(pydantic.BaseModel):
347
360
  if role is RegisterRole.INPUT:
348
361
  return RegisterRole.OUTPUT
349
362
  if role is RegisterRole.EXPLICIT_ZERO_INPUT or role is RegisterRole.ZERO_INPUT:
350
- if self.release_by_inverse:
351
- return RegisterRole.ZERO_OUTPUT
352
- return RegisterRole.OUTPUT
363
+ return RegisterRole.ZERO_OUTPUT
353
364
  if role is RegisterRole.AUXILIARY:
354
365
  return RegisterRole.AUXILIARY
355
366
  if role is RegisterRole.OUTPUT or role is RegisterRole.GARBAGE_OUTPUT:
@@ -15,6 +15,7 @@ from classiq.interface.helpers.versioned_model import VersionedModel
15
15
 
16
16
  class OperationType(StrEnum):
17
17
  REGULAR = "REGULAR"
18
+ INVISIBLE = "INVISIBLE"
18
19
  ALLOCATE = "ALLOCATE"
19
20
  FREE = "FREE"
20
21
  BIND = "BIND"
@@ -124,6 +125,7 @@ class Operation(pydantic.BaseModel):
124
125
  )
125
126
  is_daggered: bool = pydantic.Field(default=False)
126
127
  expanded: bool = pydantic.Field(default=False)
128
+ show_expanded_label: bool = pydantic.Field(default=False)
127
129
 
128
130
 
129
131
  class ProgramVisualModel(VersionedModel):
@@ -355,3 +355,11 @@ ConcreteHandleBinding = Union[
355
355
  SubscriptHandleBinding.model_rebuild()
356
356
  SlicedHandleBinding.model_rebuild()
357
357
  FieldHandleBinding.model_rebuild()
358
+
359
+
360
+ class HandlesList(ASTNode):
361
+ handles: list["GeneralHandle"]
362
+
363
+
364
+ GeneralHandle = Union[ConcreteHandleBinding, HandlesList]
365
+ HandlesList.model_rebuild()
@@ -1,6 +1,6 @@
1
1
  from collections import Counter
2
2
  from collections.abc import Mapping
3
- from typing import Any, Literal, NewType, Optional
3
+ from typing import Any, Literal, NewType
4
4
 
5
5
  import pydantic
6
6
 
@@ -9,7 +9,6 @@ from classiq.interface.debug_info.debug_info import DebugInfoCollection
9
9
  from classiq.interface.exceptions import ClassiqValueError
10
10
  from classiq.interface.executor.execution_preferences import ExecutionPreferences
11
11
  from classiq.interface.generator.constant import Constant
12
- from classiq.interface.generator.functions.concrete_types import ConcreteClassicalType
13
12
  from classiq.interface.generator.functions.port_declaration import (
14
13
  PortDeclarationDirection,
15
14
  )
@@ -103,9 +102,6 @@ class Model(VersionedModel, ASTNode):
103
102
  functions_compilation_metadata: dict[str, CompilationMetadata] = pydantic.Field(
104
103
  default_factory=dict
105
104
  )
106
- execution_parameters: Optional[dict[str, ConcreteClassicalType]] = pydantic.Field(
107
- default=None, exclude=True
108
- )
109
105
 
110
106
  @property
111
107
  def main_func(self) -> NativeFunctionDefinition:
@@ -184,11 +180,12 @@ class Model(VersionedModel, ASTNode):
184
180
  )
185
181
  return constants
186
182
 
187
- def dump_no_preferences_and_constraints(self) -> dict[str, Any]:
183
+ def dump_no_metadata(self) -> dict[str, Any]:
188
184
  return self.model_dump(
189
185
  exclude={
190
186
  "constraints",
191
187
  "execution_preferences",
192
188
  "preferences",
189
+ "functions_compilation_metadata",
193
190
  },
194
191
  )
@@ -1,4 +1,5 @@
1
1
  from collections.abc import Iterable, Mapping, Sequence
2
+ from itertools import chain
2
3
  from typing import (
3
4
  Literal,
4
5
  Optional,
@@ -15,7 +16,9 @@ from classiq.interface.generator.functions.port_declaration import (
15
16
  )
16
17
  from classiq.interface.model.handle_binding import (
17
18
  ConcreteHandleBinding,
19
+ GeneralHandle,
18
20
  HandleBinding,
21
+ HandlesList,
19
22
  )
20
23
  from classiq.interface.model.port_declaration import AnonPortDeclaration
21
24
  from classiq.interface.model.quantum_function_declaration import (
@@ -31,6 +34,7 @@ ArgValue = Union[
31
34
  Expression,
32
35
  QuantumOperand,
33
36
  ConcreteHandleBinding,
37
+ HandlesList,
34
38
  ]
35
39
 
36
40
 
@@ -146,6 +150,26 @@ class QuantumFunctionCall(QuantumOperation):
146
150
  )
147
151
  ]
148
152
 
153
+ @property
154
+ def handles_with_directions(
155
+ self,
156
+ ) -> Iterable[tuple[HandleBinding, PortDeclarationDirection]]:
157
+ return [(handle, param.direction) for handle, param in self.handles_with_params]
158
+
159
+ @property
160
+ def handles_with_params(
161
+ self,
162
+ ) -> Iterable[tuple[HandleBinding, AnonPortDeclaration]]:
163
+ return [
164
+ (handle, param)
165
+ for arg, param in zip(
166
+ self.positional_args, self.func_decl.positional_arg_declarations
167
+ )
168
+ if isinstance(param, AnonPortDeclaration)
169
+ and isinstance(arg, (HandleBinding, HandlesList))
170
+ for handle in _get_handles(arg)
171
+ ]
172
+
149
173
  @property
150
174
  def params(self) -> list[Expression]:
151
175
  return [
@@ -161,7 +185,7 @@ class QuantumFunctionCall(QuantumOperation):
161
185
  return [
162
186
  param
163
187
  for param in self.positional_args
164
- if not isinstance(param, (Expression, HandleBinding))
188
+ if not isinstance(param, (Expression, HandleBinding, HandlesList))
165
189
  ]
166
190
 
167
191
  @property
@@ -224,3 +248,9 @@ class QuantumFunctionCall(QuantumOperation):
224
248
  f" for parameter {param_name}" if len(self.positional_args) > 1 else ""
225
249
  )
226
250
  return f"as an argument{param_text} of function {self.func_name!r}"
251
+
252
+
253
+ def _get_handles(var: GeneralHandle) -> Iterable[HandleBinding]:
254
+ if isinstance(var, HandleBinding):
255
+ return [var]
256
+ return chain.from_iterable(_get_handles(item) for item in var.handles)
@@ -1,4 +1,4 @@
1
- from collections.abc import Mapping, Sequence
1
+ from collections.abc import Iterable, Mapping, Sequence
2
2
  from dataclasses import dataclass
3
3
  from typing import Any, Callable, Optional
4
4
  from uuid import UUID, uuid4
@@ -9,6 +9,9 @@ from typing_extensions import Self
9
9
 
10
10
  from classiq.interface.ast_node import ASTNode
11
11
  from classiq.interface.generator.expressions.expression import Expression
12
+ from classiq.interface.generator.functions.port_declaration import (
13
+ PortDeclarationDirection,
14
+ )
12
15
  from classiq.interface.helpers.pydantic_model_helpers import values_with_discriminator
13
16
  from classiq.interface.model.handle_binding import (
14
17
  ConcreteHandleBinding,
@@ -90,6 +93,16 @@ class QuantumOperation(QuantumStatement):
90
93
  def readable_outputs(self) -> Sequence[HandleMetadata]:
91
94
  return [HandleMetadata(handle=handle) for handle in self.outputs]
92
95
 
96
+ @property
97
+ def handles_with_directions(
98
+ self,
99
+ ) -> Iterable[tuple[HandleBinding, PortDeclarationDirection]]:
100
+ return (
101
+ [(handle, PortDeclarationDirection.Input) for handle in self.inputs]
102
+ + [(handle, PortDeclarationDirection.Output) for handle in self.outputs]
103
+ + [(handle, PortDeclarationDirection.Inout) for handle in self.inouts]
104
+ )
105
+
93
106
  def set_generative_block(self, block_name: str, py_callable: Callable) -> None:
94
107
  self._generative_blocks[block_name] = py_callable
95
108
 
@@ -33,11 +33,16 @@ class SourceReference(HashablePydanticBaseModel):
33
33
  file_name: Optional[str] = pydantic.Field(default=None)
34
34
 
35
35
  def __str__(self) -> str:
36
- file_string = _prepare_file_string(self.file_name) if self.file_name else ""
36
+ return f"{self.file_string()}{self.ref_inside_file()}"
37
+
38
+ def file_string(self) -> str:
39
+ return _prepare_file_string(self.file_name) if self.file_name else ""
40
+
41
+ def ref_inside_file(self) -> str:
37
42
  start_character_string = (
38
43
  f" character {self.start_column + 1}" if self.start_column > 0 else ""
39
44
  )
40
- return f"{file_string}line {self.start_line + 1}{start_character_string}"
45
+ return f"line {self.start_line + 1}{start_character_string}"
41
46
 
42
47
 
43
48
  class SourceReferencedError(pydantic.BaseModel):
@@ -1,7 +1,7 @@
1
1
  import dataclasses
2
2
  from collections.abc import Sequence
3
3
  from dataclasses import dataclass, field
4
- from typing import TYPE_CHECKING, Callable
4
+ from typing import TYPE_CHECKING, Callable, Union, cast
5
5
 
6
6
  from typing_extensions import Self
7
7
 
@@ -21,6 +21,7 @@ from classiq.interface.model.classical_parameter_declaration import (
21
21
  )
22
22
  from classiq.interface.model.handle_binding import (
23
23
  HandleBinding,
24
+ HandlesList,
24
25
  NestedHandleBinding,
25
26
  SlicedHandleBinding,
26
27
  )
@@ -254,7 +255,7 @@ class CapturedVars:
254
255
 
255
256
  def _conjugate_direction(
256
257
  self,
257
- source_direction: PortDirection | bool,
258
+ source_direction: Union[PortDirection, bool],
258
259
  target_direction: PortDirection,
259
260
  var_name: str,
260
261
  ) -> PortDirection:
@@ -653,12 +654,9 @@ def validate_args_are_not_propagated(
653
654
  for handle in captured_vars
654
655
  if isinstance(handle, HandleBinding)
655
656
  }
656
- arg_handles = {
657
- demangle_handle(arg) for arg in args if isinstance(arg, HandleBinding)
658
- }
659
657
  violating_handles = [
660
658
  f"{str(arg_handle)!r}"
661
- for arg_handle in arg_handles
659
+ for arg_handle in _get_all_handles(args)
662
660
  if any(
663
661
  arg_handle.overlaps(captured_handle) for captured_handle in captured_handles
664
662
  )
@@ -669,6 +667,18 @@ def validate_args_are_not_propagated(
669
667
  )
670
668
 
671
669
 
670
+ def _get_all_handles(args: Sequence[ArgValue]) -> set[HandleBinding]:
671
+ arg_handles: set[HandleBinding] = set()
672
+ for arg in args:
673
+ if isinstance(arg, HandleBinding):
674
+ arg_handles.add(demangle_handle(arg))
675
+ elif isinstance(arg, HandlesList):
676
+ arg_handles |= set(
677
+ map(demangle_handle, cast(list[HandleBinding], arg.handles))
678
+ )
679
+ return arg_handles
680
+
681
+
672
682
  def validate_captured_directions(
673
683
  captured_vars: CapturedVars, report_outin: bool = True
674
684
  ) -> None:
@@ -1,22 +1,11 @@
1
1
  import dataclasses
2
- import json
3
- from collections.abc import Collection, Sequence
2
+ from collections.abc import Sequence
4
3
  from dataclasses import dataclass, field
5
- from functools import singledispatch
6
4
  from typing import Any, Optional
7
5
 
8
6
  from typing_extensions import Self
9
7
 
10
8
  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
- )
20
9
  from classiq.interface.helpers.pydantic_model_helpers import nameables_to_dict
21
10
  from classiq.interface.model.quantum_function_declaration import (
22
11
  NamedParamsQuantumFunctionDeclaration,
@@ -27,10 +16,7 @@ from classiq.interface.model.quantum_statement import QuantumStatement
27
16
 
28
17
  from classiq.model_expansions.capturing.captured_vars import CapturedVars
29
18
  from classiq.model_expansions.scope import (
30
- Evaluated,
31
- QuantumSymbol,
32
19
  Scope,
33
- evaluated_to_str as evaluated_classical_param_to_str,
34
20
  )
35
21
  from classiq.qmod.builtins.functions import permute
36
22
  from classiq.qmod.quantum_function import GenerativeQFunc
@@ -67,13 +53,6 @@ class FunctionClosure(Closure):
67
53
  raise ClassiqInternalExpansionError
68
54
  return self._depth
69
55
 
70
- # creates a unique id for the function closure based on the arguments values.
71
- # The closure is changing across the interpreter flow so it's closure_id may change
72
- @property
73
- def closure_id(self) -> str:
74
- signature = _generate_closure_id(self.scope.data.values())
75
- return f"{self.name}__{signature}"
76
-
77
56
  @property
78
57
  def body(self) -> Sequence[QuantumStatement]:
79
58
  if self.name == permute.func_decl.name:
@@ -134,39 +113,3 @@ class FunctionClosure(Closure):
134
113
  @dataclass(frozen=True)
135
114
  class GenerativeFunctionClosure(GenerativeClosure, FunctionClosure):
136
115
  pass
137
-
138
-
139
- def _generate_closure_id(evaluated_args: Collection[Evaluated]) -> str:
140
- args_signature = [
141
- _evaluated_arg_to_str(eval_arg.value) for eval_arg in evaluated_args
142
- ]
143
- return json.dumps(args_signature)
144
-
145
-
146
- @singledispatch
147
- def _evaluated_arg_to_str(arg: Any) -> str:
148
- if isinstance(arg, str):
149
- return arg
150
- if isinstance(arg, QuantumSymbol):
151
- return _evaluated_quantum_symbol_to_str(arg)
152
- if isinstance(arg, FunctionClosure):
153
- return _evaluated_one_operand_to_str(arg)
154
- if isinstance(arg, list) and arg and isinstance(arg[0], FunctionClosure):
155
- return _evaluated_operands_list_to_str(arg)
156
- if isinstance(arg, ClassicalProxy):
157
- if isinstance(arg, ClassicalStructProxy):
158
- return repr(arg.struct_declaration)
159
- return repr(get_proxy_type(arg))
160
- return evaluated_classical_param_to_str(arg)
161
-
162
-
163
- def _evaluated_quantum_symbol_to_str(port: QuantumSymbol) -> str:
164
- return port.quantum_type.model_dump_json(exclude_none=True, exclude={"name"})
165
-
166
-
167
- def _evaluated_one_operand_to_str(operand: FunctionClosure) -> str:
168
- return operand.closure_id
169
-
170
-
171
- def _evaluated_operands_list_to_str(arg: list[FunctionClosure]) -> str:
172
- return json.dumps([_evaluated_one_operand_to_str(ope) for ope in arg])
@@ -27,7 +27,7 @@ from classiq.interface.model.quantum_function_declaration import (
27
27
 
28
28
  from classiq.model_expansions.closure import FunctionClosure
29
29
  from classiq.model_expansions.evaluators.type_type_match import check_signature_match
30
- from classiq.model_expansions.scope import Evaluated, QuantumSymbol
30
+ from classiq.model_expansions.scope import Evaluated, QuantumVariable
31
31
  from classiq.qmod.model_state_container import QMODULE
32
32
  from classiq.qmod.qmod_parameter import CInt, get_qmod_type
33
33
 
@@ -77,7 +77,7 @@ def check_arg_type_match(
77
77
 
78
78
 
79
79
  def _check_qvar_type_match(argument: Any, error_message: str) -> None:
80
- if not isinstance(argument, QuantumSymbol):
80
+ if not isinstance(argument, QuantumVariable):
81
81
  raise ClassiqExpansionError(error_message)
82
82
 
83
83
 
@@ -7,13 +7,13 @@ from classiq.interface.model.port_declaration import AnonPortDeclaration
7
7
  from classiq.interface.model.quantum_function_declaration import AnonPositionalArg
8
8
 
9
9
  from classiq.model_expansions.evaluators.quantum_type_utils import copy_type_information
10
- from classiq.model_expansions.scope import Evaluated, QuantumSymbol
10
+ from classiq.model_expansions.scope import Evaluated, QuantumVariable
11
11
 
12
12
 
13
13
  def add_information_from_output_arguments(
14
14
  parameters: Sequence[AnonPositionalArg],
15
15
  args: list[Evaluated],
16
- ) -> list[Evaluated]:
16
+ ) -> None:
17
17
  """
18
18
  This function propagates the quantum type information from the output arguments
19
19
  to the arguments that were passed to it.
@@ -29,7 +29,7 @@ def add_information_from_output_arguments(
29
29
  if not isinstance(parameter, AnonPortDeclaration):
30
30
  continue
31
31
 
32
- argument_as_quantum_symbol = argument.as_type(QuantumSymbol)
32
+ argument_as_quantum_symbol = argument.as_type(QuantumVariable)
33
33
 
34
34
  if parameter.direction != PortDeclarationDirection.Output:
35
35
  continue
@@ -38,6 +38,5 @@ def add_information_from_output_arguments(
38
38
  copy_type_information(
39
39
  parameter.quantum_type,
40
40
  argument_as_quantum_symbol.quantum_type,
41
- str(argument_as_quantum_symbol.handle),
41
+ str(argument_as_quantum_symbol),
42
42
  )
43
- return args
@@ -5,6 +5,9 @@ from classiq.interface.generator.expressions.evaluated_expression import (
5
5
  )
6
6
  from classiq.interface.generator.expressions.expression import Expression
7
7
  from classiq.interface.generator.expressions.expression_types import ExpressionValue
8
+ from classiq.interface.generator.expressions.proxies.classical.any_classical_value import (
9
+ AnyClassicalValue,
10
+ )
8
11
  from classiq.interface.model.handle_binding import HandleBinding
9
12
 
10
13
  from classiq.model_expansions.expression_evaluator import evaluate
@@ -19,18 +22,15 @@ def evaluate_classical_expression(expr: Expression, scope: Scope) -> Evaluated:
19
22
  if isinstance(evaluated.value, get_args(ExpressionValue))
20
23
  } | {
21
24
  name: EvaluatedExpression(
22
- value=evaluated.value.quantum_type.get_proxy(HandleBinding(name=name))
25
+ value=(
26
+ evaluated.value.quantum_type.get_proxy(HandleBinding(name=name))
27
+ if evaluated.value.quantum_type.is_evaluated
28
+ else AnyClassicalValue(name)
29
+ )
23
30
  )
24
31
  for name, evaluated in all_symbols
25
32
  if isinstance(evaluated.value, QuantumSymbol)
26
- and evaluated.value.quantum_type.is_evaluated
27
- }
28
- uninitialized_locals = {
29
- name
30
- for name, evaluated in all_symbols
31
- if isinstance(evaluated.value, QuantumSymbol)
32
- and not evaluated.value.quantum_type.is_evaluated
33
33
  }
34
34
 
35
- ret = evaluate(expr, locals_dict, uninitialized_locals)
35
+ ret = evaluate(expr, locals_dict)
36
36
  return Evaluated(value=ret.value)