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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. classiq/_internals/api_wrapper.py +36 -0
  2. classiq/analyzer/show_interactive_hack.py +58 -2
  3. classiq/applications/chemistry/chemistry_model_constructor.py +8 -1
  4. classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +2 -0
  5. classiq/applications/combinatorial_optimization/combinatorial_problem.py +4 -4
  6. classiq/applications/qnn/gradients/quantum_gradient.py +3 -5
  7. classiq/applications/qnn/gradients/simple_quantum_gradient.py +2 -2
  8. classiq/applications/qnn/qlayer.py +23 -19
  9. classiq/applications/qnn/types.py +1 -4
  10. classiq/execution/__init__.py +3 -0
  11. classiq/execution/execution_session.py +3 -16
  12. classiq/execution/qnn.py +2 -2
  13. classiq/execution/user_budgets.py +38 -0
  14. classiq/executor.py +7 -19
  15. classiq/interface/_version.py +1 -1
  16. classiq/interface/debug_info/debug_info.py +18 -13
  17. classiq/interface/executor/user_budget.py +56 -0
  18. classiq/interface/generator/application_apis/finance_declarations.py +3 -0
  19. classiq/interface/generator/expressions/atomic_expression_functions.py +3 -0
  20. classiq/interface/generator/expressions/proxies/classical/any_classical_value.py +30 -124
  21. classiq/interface/generator/expressions/proxies/classical/classical_array_proxy.py +46 -22
  22. classiq/interface/generator/expressions/proxies/classical/qmod_struct_instance.py +7 -0
  23. classiq/interface/generator/expressions/proxies/classical/utils.py +14 -13
  24. classiq/interface/generator/expressions/proxies/quantum/qmod_qscalar_proxy.py +9 -2
  25. classiq/interface/generator/expressions/proxies/quantum/qmod_sized_proxy.py +4 -1
  26. classiq/interface/generator/expressions/sympy_supported_expressions.py +1 -0
  27. classiq/interface/generator/functions/classical_type.py +36 -1
  28. classiq/interface/generator/functions/type_name.py +32 -5
  29. classiq/interface/generator/functions/type_qualifier.py +15 -0
  30. classiq/interface/generator/generated_circuit_data.py +11 -25
  31. classiq/interface/generator/model/preferences/preferences.py +7 -0
  32. classiq/interface/generator/quantum_program.py +5 -19
  33. classiq/interface/generator/synthesis_metadata/synthesis_execution_data.py +10 -13
  34. classiq/interface/helpers/backward_compatibility.py +9 -0
  35. classiq/interface/helpers/datastructures.py +6 -0
  36. classiq/interface/helpers/versioned_model.py +12 -0
  37. classiq/interface/interface_version.py +1 -1
  38. classiq/interface/model/handle_binding.py +12 -0
  39. classiq/interface/model/port_declaration.py +1 -2
  40. classiq/interface/model/quantum_lambda_function.py +2 -1
  41. classiq/interface/model/statement_block.py +9 -1
  42. classiq/interface/model/within_apply_operation.py +12 -0
  43. classiq/interface/server/routes.py +6 -0
  44. classiq/model_expansions/atomic_expression_functions_defs.py +82 -23
  45. classiq/model_expansions/capturing/captured_vars.py +2 -0
  46. classiq/model_expansions/closure.py +18 -0
  47. classiq/model_expansions/evaluators/argument_types.py +6 -5
  48. classiq/model_expansions/evaluators/classical_type_inference.py +17 -6
  49. classiq/model_expansions/evaluators/parameter_types.py +26 -13
  50. classiq/model_expansions/evaluators/type_type_match.py +2 -2
  51. classiq/model_expansions/expression_evaluator.py +1 -1
  52. classiq/model_expansions/generative_functions.py +66 -33
  53. classiq/model_expansions/interpreters/base_interpreter.py +27 -19
  54. classiq/model_expansions/interpreters/frontend_generative_interpreter.py +26 -0
  55. classiq/model_expansions/interpreters/generative_interpreter.py +25 -1
  56. classiq/model_expansions/quantum_operations/allocate.py +27 -11
  57. classiq/model_expansions/quantum_operations/assignment_result_processor.py +220 -19
  58. classiq/model_expansions/quantum_operations/bind.py +54 -30
  59. classiq/model_expansions/quantum_operations/block_evaluator.py +42 -0
  60. classiq/model_expansions/quantum_operations/call_emitter.py +14 -12
  61. classiq/model_expansions/quantum_operations/composite_emitter.py +1 -1
  62. classiq/model_expansions/quantum_operations/declarative_call_emitter.py +23 -9
  63. classiq/model_expansions/quantum_operations/emitter.py +21 -8
  64. classiq/model_expansions/quantum_operations/expression_evaluator.py +1 -0
  65. classiq/model_expansions/quantum_operations/handle_evaluator.py +1 -0
  66. classiq/model_expansions/quantum_operations/quantum_function_call.py +4 -3
  67. classiq/model_expansions/scope.py +10 -7
  68. classiq/model_expansions/sympy_conversion/arithmetics.py +18 -0
  69. classiq/model_expansions/sympy_conversion/expression_to_sympy.py +2 -0
  70. classiq/model_expansions/sympy_conversion/sympy_to_python.py +10 -1
  71. classiq/model_expansions/transformers/model_renamer.py +48 -8
  72. classiq/model_expansions/utils/handles_collector.py +1 -1
  73. classiq/model_expansions/visitors/symbolic_param_inference.py +197 -0
  74. classiq/model_expansions/visitors/variable_references.py +45 -9
  75. classiq/qmod/builtins/functions/allocation.py +2 -2
  76. classiq/qmod/builtins/functions/arithmetic.py +14 -12
  77. classiq/qmod/builtins/functions/standard_gates.py +23 -23
  78. classiq/qmod/declaration_inferrer.py +19 -7
  79. classiq/qmod/generative.py +9 -1
  80. classiq/qmod/native/expression_to_qmod.py +4 -0
  81. classiq/qmod/native/pretty_printer.py +8 -3
  82. classiq/qmod/pretty_print/pretty_printer.py +1 -1
  83. classiq/qmod/python_classical_type.py +4 -5
  84. classiq/qmod/qmod_constant.py +15 -7
  85. classiq/qmod/qmod_variable.py +30 -2
  86. classiq/qmod/quantum_function.py +19 -6
  87. classiq/qmod/semantics/lambdas.py +6 -2
  88. classiq/qmod/semantics/validation/main_validation.py +17 -4
  89. classiq/qmod/symbolic.py +8 -19
  90. classiq/qmod/symbolic_expr.py +34 -2
  91. classiq/qmod/write_qmod.py +5 -1
  92. classiq/synthesis.py +17 -31
  93. classiq/visualization.py +35 -0
  94. {classiq-0.74.0.dist-info → classiq-0.76.0.dist-info}/METADATA +1 -1
  95. {classiq-0.74.0.dist-info → classiq-0.76.0.dist-info}/RECORD +96 -91
  96. {classiq-0.74.0.dist-info → classiq-0.76.0.dist-info}/WHEEL +1 -1
@@ -1,5 +1,5 @@
1
- from collections.abc import Mapping
2
- from typing import Optional, Union
1
+ from collections.abc import Mapping, Sequence
2
+ from typing import Optional, Union, cast
3
3
  from uuid import UUID
4
4
 
5
5
  from pydantic import BaseModel, Field
@@ -7,10 +7,13 @@ 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
13
+ from classiq.interface.model.handle_binding import ConcreteHandleBinding
14
+ from classiq.interface.model.port_declaration import PortDeclaration
15
+ from classiq.interface.model.quantum_function_call import ArgValue
16
+ from classiq.interface.model.quantum_function_declaration import PositionalArg
14
17
  from classiq.interface.model.statement_block import ConcreteQuantumStatement
15
18
 
16
19
  ParameterValue = Union[float, int, str, None]
@@ -18,21 +21,15 @@ ParameterValue = Union[float, int, str, None]
18
21
 
19
22
  class FunctionDebugInfo(BaseModel):
20
23
  name: str
21
- level: OperationLevel = Field(default=OperationLevel.UNKNOWN)
22
24
  statement_type: Union[StatementType, None] = None
23
- is_allocate_or_free: bool = Field(default=False)
24
25
  is_inverse: bool = Field(default=False)
25
26
  release_by_inverse: bool = Field(default=False)
26
27
  port_to_passed_variable_map: dict[str, str] = Field(default_factory=dict)
27
28
  node: Optional[ConcreteQuantumStatement] = None
28
29
 
29
30
  @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
- )
31
+ def is_allocate_or_free(self) -> bool:
32
+ return back_ref_util.is_allocate_or_free(self.node) if self.node else False
36
33
 
37
34
  def update_map_from_port_mapping(self, port_mapping: Mapping[str, str]) -> None:
38
35
  new_port_to_passed_variable_map = self.port_to_passed_variable_map.copy()
@@ -103,7 +100,15 @@ def new_function_debug_info_by_node(
103
100
  ) -> FunctionDebugInfo:
104
101
  return FunctionDebugInfo(
105
102
  name="",
106
- parameters=dict(),
107
- level=OperationLevel.QMOD_STATEMENT,
108
103
  node=node._as_back_ref(),
109
104
  )
105
+
106
+
107
+ def calculate_port_to_passed_variable_mapping(
108
+ arg_decls: Sequence[PositionalArg], args: Sequence[Union[ArgValue, None]]
109
+ ) -> dict[str, str]:
110
+ return {
111
+ arg_decl.name: str(cast(ConcreteHandleBinding, arg))
112
+ for arg_decl, arg in zip(arg_decls, args)
113
+ if isinstance(arg_decl, PortDeclaration)
114
+ }
@@ -0,0 +1,56 @@
1
+ import datetime
2
+ from collections import defaultdict
3
+ from typing import Optional
4
+
5
+ import pydantic
6
+ from pydantic import ConfigDict, Field
7
+
8
+ from classiq.interface.helpers.versioned_model import VersionedModel
9
+
10
+
11
+ class UserBudget(VersionedModel):
12
+ provider: str
13
+ currency_code: str
14
+ organization: Optional[str] = Field(default=None)
15
+ available_budget: float
16
+ used_budget: float
17
+ last_allocation_date: datetime.datetime
18
+
19
+ model_config = ConfigDict(extra="ignore")
20
+
21
+
22
+ class UserBudgets(VersionedModel):
23
+ budgets: list[UserBudget] = pydantic.Field(default=[])
24
+
25
+ def print_budgets(self) -> None:
26
+ def format_header() -> str:
27
+ return f"| {'Provider':<20} | {'Available Budget':<18} | {'Used Budget':<18} | {'Currency':<8} |"
28
+
29
+ def format_row(
30
+ provider: str, available: float, used: float, currency: str
31
+ ) -> str:
32
+ return f"| {provider:<20} | {available:<18.0f} | {used:<18.0f} | {currency:<8} |"
33
+
34
+ table_data: dict = defaultdict(
35
+ lambda: {"used": 0.0, "available": 0.0, "currency": "USD"}
36
+ )
37
+
38
+ for budget in self.budgets:
39
+ provider = budget.provider
40
+ table_data[provider]["available"] += budget.available_budget
41
+ table_data[provider]["used"] += budget.used_budget
42
+ table_data[provider]["currency"] = budget.currency_code
43
+
44
+ line = "=" * 77
45
+ print(line) # noqa: T201
46
+ print(format_header()) # noqa: T201
47
+ print(line) # noqa: T201
48
+
49
+ for provider, values in table_data.items():
50
+ print( # noqa: T201
51
+ format_row(
52
+ provider, values["available"], values["used"], values["currency"]
53
+ )
54
+ )
55
+
56
+ print(line) # noqa: T201
@@ -10,6 +10,7 @@ from classiq.interface.generator.functions.port_declaration import (
10
10
  PortDeclarationDirection,
11
11
  )
12
12
  from classiq.interface.generator.functions.type_name import Struct
13
+ from classiq.interface.generator.functions.type_qualifier import TypeQualifier
13
14
  from classiq.interface.model.classical_parameter_declaration import (
14
15
  ClassicalParameterDeclaration,
15
16
  )
@@ -55,11 +56,13 @@ def _generate_finance_function(
55
56
  )
56
57
  ),
57
58
  direction=PortDeclarationDirection.Inout,
59
+ type_qualifier=TypeQualifier.Quantum,
58
60
  ),
59
61
  PortDeclaration(
60
62
  name=OBJECTIVE_PORT_NAME,
61
63
  quantum_type=QuantumBit(),
62
64
  direction=PortDeclarationDirection.Inout,
65
+ type_qualifier=TypeQualifier.Quantum,
63
66
  ),
64
67
  ],
65
68
  )
@@ -15,6 +15,7 @@ SUPPORTED_CLASSIQ_BUILTIN_FUNCTIONS = {
15
15
  "molecule_problem_to_hamiltonian",
16
16
  "fock_hamiltonian_problem_to_hamiltonian",
17
17
  "molecule_ground_state_solution_post_process",
18
+ "mod_inverse",
18
19
  }
19
20
 
20
21
  SUPPORTED_CLASSIQ_SYMPY_WRAPPERS = {
@@ -23,6 +24,8 @@ SUPPORTED_CLASSIQ_SYMPY_WRAPPERS = {
23
24
  "BitwiseNot",
24
25
  "BitwiseOr",
25
26
  "LogicalXor",
27
+ "RShift",
28
+ "LShift",
26
29
  }
27
30
 
28
31
  SUPPORTED_ATOMIC_EXPRESSION_FUNCTIONS = {
@@ -1,135 +1,41 @@
1
+ import inspect
1
2
  from typing import Any
2
3
 
4
+ import sympy
3
5
 
4
- class AnyClassicalValue:
5
- def __init__(self, expr: str) -> None:
6
- self._expr = expr
6
+ _SYMPY_MEMBERS = [name for name, _ in inspect.getmembers(sympy.Symbol)] + ["precedence"]
7
7
 
8
- def __str__(self) -> str:
9
- return self._expr
10
8
 
11
- def __repr__(self) -> str:
12
- return str(self)
9
+ def subscript_to_str(index: Any) -> str:
10
+ if not isinstance(index, slice):
11
+ return str(index)
12
+ expr = ""
13
+ if index.start is not None:
14
+ expr += str(index.start)
15
+ expr += ":"
16
+ if index.stop is not None:
17
+ expr += str(index.stop)
18
+ if index.step is not None:
19
+ expr += f":{index.step}"
20
+ return expr
13
21
 
14
- def __getitem__(self, item: Any) -> "AnyClassicalValue":
15
- if isinstance(item, slice):
16
- subscript = ""
17
- if item.start is not None:
18
- subscript += str(item.start)
19
- subscript += ":"
20
- if item.stop is not None:
21
- subscript += str(item.stop)
22
- if item.step is not None:
23
- subscript += f":{item.stop}"
24
- item = subscript
25
- return AnyClassicalValue(f"{self}[{item}]")
26
-
27
- @staticmethod
28
- def _binary_op(lhs: Any, rhs: Any, op: str) -> "AnyClassicalValue":
29
- return AnyClassicalValue(f"{lhs} {op} {rhs}")
30
-
31
- @staticmethod
32
- def _unary_op(arg: Any, op: str) -> "AnyClassicalValue":
33
- return AnyClassicalValue(f"{op}({arg})")
34
-
35
- def __add__(self, other: Any) -> "AnyClassicalValue":
36
- return AnyClassicalValue._binary_op(self, other, "+")
37
-
38
- def __sub__(self, other: Any) -> "AnyClassicalValue":
39
- return AnyClassicalValue._binary_op(self, other, "-")
40
-
41
- def __mul__(self, other: Any) -> "AnyClassicalValue":
42
- return AnyClassicalValue._binary_op(self, other, "*")
43
-
44
- def __truediv__(self, other: Any) -> "AnyClassicalValue":
45
- return AnyClassicalValue._binary_op(self, other, "/")
46
-
47
- def __floordiv__(self, other: Any) -> "AnyClassicalValue":
48
- return AnyClassicalValue._binary_op(self, other, "//")
49
-
50
- def __mod__(self, other: Any) -> "AnyClassicalValue":
51
- return AnyClassicalValue._binary_op(self, other, "%")
52
-
53
- def __pow__(self, other: Any) -> "AnyClassicalValue":
54
- return AnyClassicalValue._binary_op(self, other, "**")
55
-
56
- def __lshift__(self, other: Any) -> "AnyClassicalValue":
57
- return AnyClassicalValue._binary_op(self, other, "<<")
58
-
59
- def __rshift__(self, other: Any) -> "AnyClassicalValue":
60
- return AnyClassicalValue._binary_op(self, other, ">>")
61
-
62
- def __and__(self, other: Any) -> "AnyClassicalValue":
63
- return AnyClassicalValue._binary_op(self, other, "&")
64
-
65
- def __xor__(self, other: Any) -> "AnyClassicalValue":
66
- return AnyClassicalValue._binary_op(self, other, "^")
67
-
68
- def __or__(self, other: Any) -> "AnyClassicalValue":
69
- return AnyClassicalValue._binary_op(self, other, "|")
70
-
71
- def __radd__(self, other: Any) -> "AnyClassicalValue":
72
- return AnyClassicalValue._binary_op(other, self, "+")
73
-
74
- def __rsub__(self, other: Any) -> "AnyClassicalValue":
75
- return AnyClassicalValue._binary_op(other, self, "-")
76
22
 
77
- def __rmul__(self, other: Any) -> "AnyClassicalValue":
78
- return AnyClassicalValue._binary_op(other, self, "*")
23
+ class AnyClassicalValue(sympy.Symbol):
79
24
 
80
- def __rtruediv__(self, other: Any) -> "AnyClassicalValue":
81
- return AnyClassicalValue._binary_op(other, self, "/")
25
+ is_commutative = None
26
+ is_infinite = None
27
+ is_finite = None
28
+ is_extended_real = None
82
29
 
83
- def __rfloordiv__(self, other: Any) -> "AnyClassicalValue":
84
- return AnyClassicalValue._binary_op(other, self, "//")
85
-
86
- def __rmod__(self, other: Any) -> "AnyClassicalValue":
87
- return AnyClassicalValue._binary_op(other, self, "%")
88
-
89
- def __rpow__(self, other: Any) -> "AnyClassicalValue":
90
- return AnyClassicalValue._binary_op(other, self, "**")
91
-
92
- def __rlshift__(self, other: Any) -> "AnyClassicalValue":
93
- return AnyClassicalValue._binary_op(other, self, "<<")
94
-
95
- def __rrshift__(self, other: Any) -> "AnyClassicalValue":
96
- return AnyClassicalValue._binary_op(other, self, ">>")
97
-
98
- def __rand__(self, other: Any) -> "AnyClassicalValue":
99
- return AnyClassicalValue._binary_op(other, self, "&")
100
-
101
- def __rxor__(self, other: Any) -> "AnyClassicalValue":
102
- return AnyClassicalValue._binary_op(other, self, "^")
103
-
104
- def __ror__(self, other: Any) -> "AnyClassicalValue":
105
- return AnyClassicalValue._binary_op(other, self, "|")
106
-
107
- def __lt__(self, other: Any) -> "AnyClassicalValue":
108
- return AnyClassicalValue._binary_op(self, other, "<")
109
-
110
- def __le__(self, other: Any) -> "AnyClassicalValue":
111
- return AnyClassicalValue._binary_op(self, other, "<=")
112
-
113
- def __eq__(self, other: Any) -> "AnyClassicalValue": # type:ignore[override]
114
- return AnyClassicalValue._binary_op(self, other, "==")
115
-
116
- def __ne__(self, other: Any) -> "AnyClassicalValue": # type: ignore[override]
117
- return AnyClassicalValue._binary_op(self, other, "!=")
118
-
119
- def __gt__(self, other: Any) -> "AnyClassicalValue":
120
- return AnyClassicalValue._binary_op(self, other, ">")
121
-
122
- def __ge__(self, other: Any) -> "AnyClassicalValue":
123
- return AnyClassicalValue._binary_op(self, other, ">=")
124
-
125
- def __neg__(self) -> "AnyClassicalValue":
126
- return AnyClassicalValue._unary_op(self, "-")
127
-
128
- def __pos__(self) -> "AnyClassicalValue":
129
- return AnyClassicalValue._unary_op(self, "+")
30
+ def __getitem__(self, item: Any) -> "AnyClassicalValue":
31
+ if isinstance(item, slice):
32
+ return AnyClassicalValue(f"{self}[{subscript_to_str(item)}]")
33
+ return AnyClassicalValue(f"do_subscript({self}, {item})")
130
34
 
131
- def __abs__(self) -> "AnyClassicalValue":
132
- return AnyClassicalValue._unary_op(self, "abs")
35
+ def __getattribute__(self, attr: str) -> Any:
36
+ if attr.startswith("_") or attr in _SYMPY_MEMBERS:
37
+ return super().__getattribute__(attr)
38
+ return AnyClassicalValue(f"get_field({self}, '{attr}')")
133
39
 
134
- def __invert__(self) -> "AnyClassicalValue":
135
- return AnyClassicalValue._unary_op(self, "~")
40
+ def __len__(self) -> "AnyClassicalValue":
41
+ return self.len
@@ -1,11 +1,16 @@
1
1
  from collections.abc import Mapping
2
- from typing import TYPE_CHECKING, Union
2
+ from typing import TYPE_CHECKING, Any, Union
3
3
 
4
+ import sympy
4
5
  from sympy import Integer
6
+ from typing_extensions import TypeGuard
5
7
 
6
- from classiq.interface.exceptions import ClassiqValueError
8
+ from classiq.interface.exceptions import ClassiqIndexError
7
9
  from classiq.interface.generator.expressions.expression import Expression
8
10
  from classiq.interface.generator.expressions.non_symbolic_expr import NonSymbolicExpr
11
+ from classiq.interface.generator.expressions.proxies.classical.any_classical_value import (
12
+ AnyClassicalValue,
13
+ )
9
14
  from classiq.interface.generator.expressions.proxies.classical.classical_proxy import (
10
15
  ClassicalProxy,
11
16
  )
@@ -22,9 +27,20 @@ if TYPE_CHECKING:
22
27
  )
23
28
 
24
29
 
30
+ def _is_int(val: Any) -> TypeGuard[Union[int, sympy.Basic]]:
31
+ if isinstance(val, AnyClassicalValue):
32
+ return False
33
+ if isinstance(val, sympy.Basic):
34
+ return val.is_Number
35
+ return isinstance(val, int)
36
+
37
+
25
38
  class ClassicalArrayProxy(NonSymbolicExpr, ClassicalProxy):
26
39
  def __init__(
27
- self, handle: HandleBinding, element_type: "ConcreteClassicalType", length: int
40
+ self,
41
+ handle: HandleBinding,
42
+ element_type: "ConcreteClassicalType",
43
+ length: "ExpressionValue",
28
44
  ) -> None:
29
45
  super().__init__(handle)
30
46
  self._element_type = element_type
@@ -39,39 +55,47 @@ class ClassicalArrayProxy(NonSymbolicExpr, ClassicalProxy):
39
55
  return "Array"
40
56
 
41
57
  @property
42
- def length(self) -> int:
58
+ def length(self) -> "ExpressionValue":
43
59
  return self._length
44
60
 
45
- def __getitem__(self, key: Union[slice, int, Integer]) -> ClassicalProxy:
61
+ def __getitem__(
62
+ self, key: Union[slice, int, Integer, ClassicalProxy]
63
+ ) -> ClassicalProxy:
46
64
  return (
47
65
  self._get_slice(key) if isinstance(key, slice) else self._get_subscript(key)
48
66
  )
49
67
 
50
68
  def _get_slice(self, slice_: slice) -> ClassicalProxy:
51
- start = int(slice_.start)
52
- stop = int(slice_.stop)
53
- if start >= stop:
54
- raise ClassiqValueError("Array slice has non-positive length")
55
- if start < 0 or stop > self._length:
56
- raise ClassiqValueError("Array slice is out of bounds")
69
+ start_ = slice_.start
70
+ stop_ = slice_.stop
71
+ if _is_int(start_) and _is_int(stop_):
72
+ start = int(start_)
73
+ stop = int(stop_)
74
+ if start >= stop:
75
+ raise ClassiqIndexError("Array slice has non-positive length")
76
+ if start < 0 or (isinstance(self._length, int) and stop > self._length):
77
+ raise ClassiqIndexError("Array slice is out of bounds")
57
78
  return ClassicalArrayProxy(
58
79
  SlicedHandleBinding(
59
80
  base_handle=self.handle,
60
- start=Expression(expr=str(start)),
61
- end=Expression(expr=str(stop)),
81
+ start=Expression(expr=str(start_)),
82
+ end=Expression(expr=str(stop_)),
62
83
  ),
63
84
  self._element_type,
64
- stop - start,
85
+ stop_ - start_,
65
86
  )
66
87
 
67
- def _get_subscript(self, index_: Union[int, Integer]) -> ClassicalProxy:
68
- index = int(index_)
69
- if index < 0:
70
- raise ClassiqValueError(
71
- "Array index is out of bounds (negative indices are not supported)"
72
- )
73
- if index >= self._length:
74
- raise ClassiqValueError("Array index is out of bounds")
88
+ def _get_subscript(
89
+ self, index_: Union[int, Integer, ClassicalProxy]
90
+ ) -> ClassicalProxy:
91
+ if _is_int(index_):
92
+ index = int(index_)
93
+ if index < 0:
94
+ raise ClassiqIndexError(
95
+ "Array index is out of bounds (negative indices are not supported)"
96
+ )
97
+ if isinstance(self._length, int) and index >= self._length:
98
+ raise ClassiqIndexError("Array index is out of bounds")
75
99
  return self._element_type.get_classical_proxy(
76
100
  SubscriptHandleBinding(
77
101
  base_handle=self._handle, index=Expression(expr=str(index_))
@@ -25,6 +25,13 @@ class QmodStructInstance:
25
25
  def fields(self) -> Mapping[str, Any]:
26
26
  return types.MappingProxyType(self._fields)
27
27
 
28
+ def __getattr__(self, item: str) -> Any:
29
+ if item == "_fields":
30
+ return super().__getattribute__(item)
31
+ if item in self._fields:
32
+ return self._fields[item]
33
+ return super().__getattribute__(item)
34
+
28
35
  def __str__(self) -> str:
29
36
  return repr(self)
30
37
 
@@ -13,22 +13,23 @@ from classiq.interface.generator.expressions.proxies.classical.classical_struct_
13
13
  )
14
14
  from classiq.interface.generator.functions.classical_type import (
15
15
  ClassicalArray,
16
+ ClassicalList,
16
17
  ClassicalType,
17
18
  )
18
- from classiq.interface.generator.functions.type_name import TypeName
19
+ from classiq.interface.generator.functions.type_name import Struct
19
20
 
20
21
 
21
22
  def get_proxy_type(proxy: ClassicalProxy) -> ClassicalType:
22
23
  if isinstance(proxy, ClassicalScalarProxy):
23
- classical_type = proxy._classical_type
24
- elif isinstance(proxy, ClassicalArrayProxy):
25
- classical_type = ClassicalArray(
26
- element_type=proxy._element_type, size=proxy.length
27
- )
28
- elif isinstance(proxy, ClassicalStructProxy):
29
- classical_type = TypeName(name=proxy._decl.name)
30
- else:
31
- raise ClassiqInternalExpansionError(
32
- f"Unrecognized classical proxy {type(proxy).__name__}"
33
- )
34
- return classical_type
24
+ return proxy._classical_type
25
+ if isinstance(proxy, ClassicalArrayProxy):
26
+ if not isinstance(proxy.length, int):
27
+ return ClassicalList(element_type=proxy._element_type)
28
+ return ClassicalArray(element_type=proxy._element_type, size=proxy.length)
29
+ if isinstance(proxy, ClassicalStructProxy):
30
+ classical_type = Struct(name=proxy._decl.name)
31
+ classical_type.set_classical_struct_decl(proxy._decl)
32
+ return classical_type
33
+ raise ClassiqInternalExpansionError(
34
+ f"Unrecognized classical proxy {type(proxy).__name__}"
35
+ )
@@ -1,7 +1,8 @@
1
1
  from collections.abc import Mapping
2
- from typing import Any
2
+ from typing import Any, Optional
3
3
 
4
4
  from sympy import Symbol
5
+ from typing_extensions import Self
5
6
 
6
7
  from classiq.interface.exceptions import ClassiqValueError
7
8
  from classiq.interface.generator.expressions.proxies.quantum.qmod_sized_proxy import (
@@ -12,11 +13,17 @@ from classiq.interface.model.handle_binding import HandleBinding
12
13
 
13
14
  class QmodQScalarProxy(Symbol, QmodSizedProxy):
14
15
  def __new__(cls, handle: HandleBinding, **assumptions: bool) -> "QmodQScalarProxy":
15
- return super().__new__(cls, str(handle), **assumptions)
16
+ return super().__new__(cls, handle.qmod_expr, **assumptions)
16
17
 
17
18
  def __init__(self, handle: HandleBinding, size: int) -> None:
18
19
  super().__init__(handle, size)
19
20
 
21
+ def __copy__(self) -> Self:
22
+ return self
23
+
24
+ def __deepcopy__(self, memo: Optional[dict]) -> Self:
25
+ return self
26
+
20
27
 
21
28
  class QmodQBitProxy(QmodQScalarProxy):
22
29
  def __init__(self, handle: HandleBinding) -> None:
@@ -15,7 +15,10 @@ class QmodSizedProxy:
15
15
  return self._size
16
16
 
17
17
  def __str__(self) -> str:
18
- return str(self.handle)
18
+ return self.handle.qmod_expr
19
+
20
+ def __repr__(self) -> str:
21
+ return str(self)
19
22
 
20
23
  @property
21
24
  def type_name(self) -> str:
@@ -33,6 +33,7 @@ MATHEMATICAL_FUNCTIONS: list[str] = [
33
33
  "Max",
34
34
  "Min",
35
35
  "mod_inverse",
36
+ "Mod",
36
37
  ]
37
38
  SPECIAL_FUNCTIONS: list[str] = [
38
39
  "erf",
@@ -5,6 +5,10 @@ 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
9
+ from classiq.interface.generator.expressions.proxies.classical.any_classical_value import (
10
+ AnyClassicalValue,
11
+ )
8
12
  from classiq.interface.generator.expressions.proxies.classical.classical_array_proxy import (
9
13
  ClassicalArrayProxy,
10
14
  )
@@ -39,9 +43,22 @@ class ClassicalType(HashableASTNode):
39
43
  def is_generative(self) -> bool:
40
44
  return self._is_generative
41
45
 
46
+ @property
47
+ def is_purely_declarative(self) -> bool:
48
+ return not self._is_generative
49
+
42
50
  def get_classical_proxy(self, handle: HandleBinding) -> ClassicalProxy:
43
51
  return ClassicalScalarProxy(handle, self)
44
52
 
53
+ @property
54
+ def expressions(self) -> list[Expression]:
55
+ return []
56
+
57
+ def clear_flags(self) -> Self:
58
+ res = self.model_copy()
59
+ res._is_generative = False
60
+ return res
61
+
45
62
 
46
63
  class Integer(ClassicalType):
47
64
  kind: Literal["int"]
@@ -80,7 +97,17 @@ class ClassicalList(ClassicalType):
80
97
  return values_with_discriminator(values, "kind", "list")
81
98
 
82
99
  def get_classical_proxy(self, handle: HandleBinding) -> ClassicalProxy:
83
- raise NotImplementedError
100
+ return ClassicalArrayProxy(
101
+ handle, self.element_type, AnyClassicalValue(f"get_field({handle}, 'len')")
102
+ )
103
+
104
+ @property
105
+ def expressions(self) -> list[Expression]:
106
+ return self.element_type.expressions
107
+
108
+ @property
109
+ def is_purely_declarative(self) -> bool:
110
+ return super().is_purely_declarative and self.element_type.is_purely_declarative
84
111
 
85
112
 
86
113
  class StructMetaType(ClassicalType):
@@ -108,6 +135,14 @@ class ClassicalArray(ClassicalType):
108
135
  def get_classical_proxy(self, handle: HandleBinding) -> ClassicalProxy:
109
136
  return ClassicalArrayProxy(handle, self.element_type, self.size)
110
137
 
138
+ @property
139
+ def expressions(self) -> list[Expression]:
140
+ return self.element_type.expressions
141
+
142
+ @property
143
+ def is_purely_declarative(self) -> bool:
144
+ return super().is_purely_declarative and self.element_type.is_purely_declarative
145
+
111
146
 
112
147
  class OpaqueHandle(ClassicalType):
113
148
  pass
@@ -9,6 +9,9 @@ from classiq.interface.generator.expressions.expression import Expression
9
9
  from classiq.interface.generator.expressions.proxies.classical.classical_proxy import (
10
10
  ClassicalProxy,
11
11
  )
12
+ from classiq.interface.generator.expressions.proxies.classical.classical_scalar_proxy import (
13
+ ClassicalScalarProxy,
14
+ )
12
15
  from classiq.interface.generator.expressions.proxies.classical.classical_struct_proxy import (
13
16
  ClassicalStructProxy,
14
17
  )
@@ -109,18 +112,42 @@ class TypeName(ClassicalType, QuantumType):
109
112
  self._classical_struct_decl = decl
110
113
 
111
114
  def get_classical_proxy(self, handle: HandleBinding) -> ClassicalProxy:
112
- if self._classical_struct_decl is None:
113
- raise ClassiqExpansionError(f"Type {self.name!r} is undefined")
115
+ if self.is_enum:
116
+ return ClassicalScalarProxy(handle, self)
117
+ if TYPE_CHECKING:
118
+ assert self._classical_struct_decl is not None
114
119
  return ClassicalStructProxy(handle, self._classical_struct_decl)
115
120
 
116
121
  @property
117
122
  def expressions(self) -> list[Expression]:
118
- return list(
119
- chain.from_iterable(
120
- field_type.expressions for field_type in self.fields.values()
123
+ if self.has_fields:
124
+ return list(
125
+ chain.from_iterable(
126
+ field_type.expressions for field_type in self.fields.values()
127
+ )
128
+ )
129
+ if self.has_classical_struct_decl:
130
+ return list(
131
+ chain.from_iterable(
132
+ field_type.expressions
133
+ for field_type in self.classical_struct_decl.variables.values()
134
+ )
121
135
  )
136
+ return []
137
+
138
+ @property
139
+ def is_purely_declarative(self) -> bool:
140
+ if self.is_enum:
141
+ return False
142
+ return super().is_purely_declarative and all(
143
+ field_type.is_purely_declarative
144
+ for field_type in self.classical_struct_decl.variables.values()
122
145
  )
123
146
 
147
+ @property
148
+ def is_enum(self) -> bool:
149
+ return not self.has_fields and not self.has_classical_struct_decl
150
+
124
151
 
125
152
  class Enum(TypeName):
126
153
  pass