classiq 0.76.0__py3-none-any.whl → 0.77.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) 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/interface/_version.py +1 -1
  5. classiq/interface/generator/expressions/proxies/quantum/qmod_qarray_proxy.py +6 -15
  6. classiq/interface/generator/expressions/proxies/quantum/qmod_qscalar_proxy.py +14 -5
  7. classiq/interface/generator/expressions/proxies/quantum/qmod_sized_proxy.py +5 -3
  8. classiq/interface/model/handle_binding.py +8 -0
  9. classiq/interface/model/model.py +3 -6
  10. classiq/interface/model/quantum_function_call.py +31 -1
  11. classiq/interface/model/quantum_statement.py +14 -1
  12. classiq/interface/source_reference.py +7 -2
  13. classiq/model_expansions/capturing/captured_vars.py +16 -6
  14. classiq/model_expansions/evaluators/arg_type_match.py +2 -2
  15. classiq/model_expansions/evaluators/argument_types.py +3 -3
  16. classiq/model_expansions/evaluators/classical_expression.py +9 -9
  17. classiq/model_expansions/evaluators/parameter_types.py +19 -11
  18. classiq/model_expansions/expression_evaluator.py +20 -11
  19. classiq/model_expansions/function_builder.py +45 -0
  20. classiq/model_expansions/generative_functions.py +1 -1
  21. classiq/model_expansions/interpreters/base_interpreter.py +15 -1
  22. classiq/model_expansions/interpreters/frontend_generative_interpreter.py +4 -3
  23. classiq/model_expansions/interpreters/generative_interpreter.py +4 -4
  24. classiq/model_expansions/quantum_operations/allocate.py +2 -2
  25. classiq/model_expansions/quantum_operations/assignment_result_processor.py +3 -1
  26. classiq/model_expansions/quantum_operations/call_emitter.py +21 -11
  27. classiq/model_expansions/quantum_operations/emitter.py +1 -6
  28. classiq/model_expansions/scope.py +53 -3
  29. classiq/model_expansions/transformers/model_renamer.py +2 -2
  30. classiq/model_expansions/visitors/symbolic_param_inference.py +3 -3
  31. classiq/open_library/functions/lookup_table.py +1 -1
  32. classiq/open_library/functions/state_preparation.py +1 -1
  33. classiq/qmod/create_model_function.py +21 -3
  34. classiq/qmod/global_declarative_switch.py +19 -0
  35. classiq/qmod/native/pretty_printer.py +4 -0
  36. classiq/qmod/pretty_print/pretty_printer.py +4 -0
  37. classiq/qmod/qfunc.py +31 -23
  38. classiq/qmod/quantum_expandable.py +29 -1
  39. classiq/qmod/quantum_function.py +26 -19
  40. classiq/qmod/write_qmod.py +36 -10
  41. classiq/synthesis.py +7 -6
  42. {classiq-0.76.0.dist-info → classiq-0.77.0.dist-info}/METADATA +1 -1
  43. {classiq-0.76.0.dist-info → classiq-0.77.0.dist-info}/RECORD +44 -43
  44. {classiq-0.76.0.dist-info → classiq-0.77.0.dist-info}/WHEEL +0 -0
@@ -53,6 +53,7 @@ from classiq.qmod.builtins.structs import (
53
53
  MoleculeProblem as QmodMoleculeProblem,
54
54
  Position as QmodPosition,
55
55
  )
56
+ from classiq.qmod.global_declarative_switch import set_global_declarative_switch
56
57
  from classiq.qmod.utilities import qmod_val_to_expr_str
57
58
 
58
59
  # isort: split
@@ -60,6 +61,11 @@ from classiq.qmod.utilities import qmod_val_to_expr_str
60
61
  # This import causes a circular import if done earlier. We use isort: split to avoid it
61
62
  from classiq.open_library.functions.hea import full_hea
62
63
 
64
+ with set_global_declarative_switch():
65
+ _FULL_HEA = cast(
66
+ NativeFunctionDefinition, full_hea.create_model().function_dict["full_hea"]
67
+ )
68
+
63
69
  _LADDER_OPERATOR_TYPE_INDICATOR_TO_QMOD_MAPPING: dict[str, str] = {
64
70
  "+": "PLUS",
65
71
  "-": "MINUS",
@@ -506,12 +512,7 @@ def construct_chemistry_model(
506
512
  )
507
513
  ]
508
514
  if isinstance(ansatz_parameters, HEAParameters):
509
- chemistry_functions.append(
510
- cast(
511
- NativeFunctionDefinition,
512
- full_hea.create_model().function_dict["full_hea"],
513
- )
514
- )
515
+ chemistry_functions.append(_FULL_HEA)
515
516
  model = Model(
516
517
  functions=chemistry_functions,
517
518
  classical_execution_code=_get_chemistry_classical_code(
@@ -36,6 +36,14 @@ from classiq.applications.combinatorial_helpers.pauli_helpers.pauli_utils import
36
36
  )
37
37
  from classiq.applications.combinatorial_optimization import OptimizerConfig, QAOAConfig
38
38
  from classiq.open_library.functions.qaoa_penalty import qaoa_penalty
39
+ from classiq.qmod.global_declarative_switch import set_global_declarative_switch
40
+
41
+ with set_global_declarative_switch():
42
+ _LIBRARY_FUNCTIONS = [
43
+ f.model_dump()
44
+ for f in qaoa_penalty.create_model().functions
45
+ if f.name != "main"
46
+ ]
39
47
 
40
48
 
41
49
  def construct_combi_opt_py_model(
@@ -107,7 +115,7 @@ def construct_combi_opt_py_model(
107
115
  ),
108
116
  ],
109
117
  ),
110
- *[f for f in qaoa_penalty.create_model().functions if f.name != "main"],
118
+ *_LIBRARY_FUNCTIONS,
111
119
  ],
112
120
  classical_execution_code=f"""
113
121
  vqe_result = vqe(
@@ -24,6 +24,7 @@ from classiq.open_library.functions.utility_functions import (
24
24
  from classiq.qmod.builtins.functions import RX
25
25
  from classiq.qmod.builtins.operations import allocate, phase, repeat
26
26
  from classiq.qmod.cparam import CReal
27
+ from classiq.qmod.create_model_function import create_model
27
28
  from classiq.qmod.qfunc import qfunc
28
29
  from classiq.qmod.qmod_parameter import CArray
29
30
  from classiq.qmod.qmod_variable import Output, QVar
@@ -80,9 +81,9 @@ class CombinatorialProblem:
80
81
  ],
81
82
  )
82
83
 
83
- self.model_ = main.create_model(
84
- constraints=constraints, preferences=preferences
85
- ).get_model() # type:ignore[assignment]
84
+ self.model_ = create_model(
85
+ main, constraints=constraints, preferences=preferences
86
+ ) # type:ignore[assignment]
86
87
  return self.model_ # type:ignore[return-value]
87
88
 
88
89
  def get_qprog(self) -> QuantumProgram:
@@ -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.76.0'
6
+ SEMVER_VERSION = '0.77.0'
7
7
  VERSION = str(Version(SEMVER_VERSION))
@@ -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:
@@ -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:
@@ -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,7 +7,7 @@ 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(
@@ -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,6 @@ 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
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)
@@ -20,6 +20,7 @@ from classiq.interface.generator.functions.type_name import (
20
20
  from classiq.interface.model.classical_parameter_declaration import (
21
21
  ClassicalParameterDeclaration,
22
22
  )
23
+ from classiq.interface.model.handle_binding import HandleBinding
23
24
  from classiq.interface.model.port_declaration import PortDeclaration
24
25
  from classiq.interface.model.quantum_function_declaration import (
25
26
  PositionalArg,
@@ -46,7 +47,12 @@ from classiq.model_expansions.evaluators.quantum_type_utils import (
46
47
  set_length,
47
48
  set_size,
48
49
  )
49
- from classiq.model_expansions.scope import Evaluated, QuantumSymbol, Scope
50
+ from classiq.model_expansions.scope import (
51
+ Evaluated,
52
+ QuantumSymbol,
53
+ QuantumVariable,
54
+ Scope,
55
+ )
50
56
 
51
57
 
52
58
  def evaluate_parameter_types_from_args(
@@ -83,10 +89,10 @@ def _update_scope(
83
89
  return
84
90
  if parameter.direction is PortDeclarationDirection.Output:
85
91
  return
86
- quantum_symbol = argument.as_type(QuantumSymbol)
92
+ quantum_var = argument.as_type(QuantumVariable)
87
93
  casted_argument = _cast(
88
94
  parameter.quantum_type,
89
- quantum_symbol,
95
+ quantum_var.quantum_type,
90
96
  parameter.name,
91
97
  )
92
98
  closure.scope[parameter.name] = Evaluated(
@@ -120,12 +126,12 @@ def _update_operand_signature_environment(
120
126
 
121
127
 
122
128
  def _cast(
123
- quantum_type: QuantumType, quantum_symbol: QuantumSymbol, param_name: str
129
+ parameter_type: QuantumType, argument_type: QuantumType, param_name: str
124
130
  ) -> QuantumSymbol:
125
- updated_quantum_type = quantum_type.model_copy()
126
- _inject_quantum_arg_info_to_type(updated_quantum_type, quantum_symbol, param_name)
131
+ updated_quantum_type = parameter_type.model_copy()
132
+ _inject_quantum_arg_info_to_type(updated_quantum_type, argument_type, param_name)
127
133
  return QuantumSymbol(
128
- handle=quantum_symbol.handle, quantum_type=updated_quantum_type
134
+ handle=HandleBinding(name=param_name), quantum_type=updated_quantum_type
129
135
  )
130
136
 
131
137
 
@@ -148,7 +154,9 @@ def _evaluate_type_from_arg(
148
154
  )
149
155
  if parameter.direction != PortDeclarationDirection.Output:
150
156
  updated_quantum_type = _inject_quantum_arg_info_to_type(
151
- updated_quantum_type, argument.as_type(QuantumSymbol), parameter.name
157
+ updated_quantum_type,
158
+ argument.as_type(QuantumVariable).quantum_type,
159
+ parameter.name,
152
160
  )
153
161
  return parameter.model_copy(update={"quantum_type": updated_quantum_type})
154
162
 
@@ -280,11 +288,11 @@ def evaluate_types_in_quantum_symbols(
280
288
 
281
289
 
282
290
  def _inject_quantum_arg_info_to_type(
283
- parameter_type: QuantumType, quantum_symbol: QuantumSymbol, param_name: str
291
+ parameter_type: QuantumType, argument_type: QuantumType, param_name: str
284
292
  ) -> QuantumType:
285
- if quantum_symbol.quantum_type.has_size_in_bits:
293
+ if argument_type.has_size_in_bits:
286
294
  copy_type_information(
287
- quantum_symbol.quantum_type,
295
+ argument_type,
288
296
  parameter_type,
289
297
  param_name,
290
298
  )
@@ -1,7 +1,7 @@
1
1
  import ast
2
2
  from collections.abc import Mapping
3
3
  from enum import EnumMeta
4
- from typing import Any, Optional
4
+ from typing import Any
5
5
 
6
6
  from sympy import SympifyError, sympify
7
7
 
@@ -50,11 +50,25 @@ def evaluate_constants_as_python(constants: list[Constant]) -> dict[str, Any]:
50
50
  }
51
51
 
52
52
 
53
+ def _quick_eval(expr: str) -> Any:
54
+ try:
55
+ return int(expr)
56
+ except ValueError:
57
+ pass
58
+ try:
59
+ return float(expr)
60
+ except ValueError:
61
+ pass
62
+ return None
63
+
64
+
53
65
  def evaluate(
54
- expr: Expression,
55
- locals_dict: Mapping[str, EvaluatedExpression],
56
- uninitialized_locals: Optional[set[str]] = None,
66
+ expr: Expression, locals_dict: Mapping[str, EvaluatedExpression]
57
67
  ) -> EvaluatedExpression:
68
+ val = _quick_eval(expr.expr)
69
+ if val is not None:
70
+ return EvaluatedExpression(value=val)
71
+
58
72
  model_locals: dict[str, ExpressionValue] = {}
59
73
  model_locals.update(ATOMIC_EXPRESSION_FUNCTIONS)
60
74
  model_locals.update(
@@ -65,9 +79,8 @@ def evaluate(
65
79
  )
66
80
  # locals override builtin-functions
67
81
  model_locals.update({name: expr.value for name, expr in locals_dict.items()})
68
- uninitialized_locals = uninitialized_locals or set()
69
82
 
70
- _validate_undefined_vars(expr.expr, model_locals, uninitialized_locals)
83
+ _validate_undefined_vars(expr.expr, model_locals)
71
84
 
72
85
  sympy_expr = translate_to_sympy(expr.expr)
73
86
  try:
@@ -95,11 +108,8 @@ def evaluate(
95
108
 
96
109
 
97
110
  def _validate_undefined_vars(
98
- expr: str,
99
- model_locals: dict[str, ExpressionValue],
100
- uninitialized_locals: Optional[set[str]],
111
+ expr: str, model_locals: dict[str, ExpressionValue]
101
112
  ) -> None:
102
- uninitialized_locals = uninitialized_locals or set()
103
113
  id_visitor = _VarsCollector()
104
114
  id_visitor.visit(ast.parse(expr))
105
115
  identifiers = id_visitor.vars
@@ -108,7 +118,6 @@ def _validate_undefined_vars(
108
118
  - model_locals.keys()
109
119
  - set(SYMPY_SUPPORTED_EXPRESSIONS)
110
120
  - set(symbolic.__all__)
111
- - uninitialized_locals
112
121
  )
113
122
 
114
123
  if len(undefined_vars) == 1: