classiq 0.75.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 (101) 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 +15 -7
  4. classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +11 -1
  5. classiq/applications/combinatorial_optimization/combinatorial_problem.py +8 -7
  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 +14 -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 +16 -2
  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 +45 -21
  22. classiq/interface/generator/expressions/proxies/classical/qmod_struct_instance.py +7 -0
  23. classiq/interface/generator/expressions/proxies/classical/utils.py +12 -11
  24. classiq/interface/generator/expressions/proxies/quantum/qmod_qarray_proxy.py +6 -15
  25. classiq/interface/generator/expressions/proxies/quantum/qmod_qscalar_proxy.py +22 -6
  26. classiq/interface/generator/expressions/proxies/quantum/qmod_sized_proxy.py +9 -4
  27. classiq/interface/generator/expressions/sympy_supported_expressions.py +1 -0
  28. classiq/interface/generator/functions/classical_type.py +6 -1
  29. classiq/interface/generator/functions/type_name.py +7 -2
  30. classiq/interface/generator/functions/type_qualifier.py +15 -0
  31. classiq/interface/generator/model/preferences/preferences.py +7 -0
  32. classiq/interface/generator/quantum_program.py +5 -19
  33. classiq/interface/helpers/backward_compatibility.py +9 -0
  34. classiq/interface/helpers/datastructures.py +6 -0
  35. classiq/interface/model/handle_binding.py +8 -0
  36. classiq/interface/model/model.py +3 -6
  37. classiq/interface/model/port_declaration.py +1 -2
  38. classiq/interface/model/quantum_function_call.py +31 -1
  39. classiq/interface/model/quantum_lambda_function.py +2 -1
  40. classiq/interface/model/quantum_statement.py +14 -1
  41. classiq/interface/server/routes.py +6 -0
  42. classiq/interface/source_reference.py +7 -2
  43. classiq/model_expansions/atomic_expression_functions_defs.py +62 -19
  44. classiq/model_expansions/capturing/captured_vars.py +18 -6
  45. classiq/model_expansions/closure.py +5 -0
  46. classiq/model_expansions/evaluators/arg_type_match.py +2 -2
  47. classiq/model_expansions/evaluators/argument_types.py +3 -3
  48. classiq/model_expansions/evaluators/classical_expression.py +9 -9
  49. classiq/model_expansions/evaluators/classical_type_inference.py +17 -6
  50. classiq/model_expansions/evaluators/parameter_types.py +45 -24
  51. classiq/model_expansions/expression_evaluator.py +21 -12
  52. classiq/model_expansions/function_builder.py +45 -0
  53. classiq/model_expansions/generative_functions.py +62 -35
  54. classiq/model_expansions/interpreters/base_interpreter.py +32 -7
  55. classiq/model_expansions/interpreters/frontend_generative_interpreter.py +9 -3
  56. classiq/model_expansions/interpreters/generative_interpreter.py +17 -5
  57. classiq/model_expansions/quantum_operations/allocate.py +8 -3
  58. classiq/model_expansions/quantum_operations/assignment_result_processor.py +221 -20
  59. classiq/model_expansions/quantum_operations/bind.py +54 -30
  60. classiq/model_expansions/quantum_operations/block_evaluator.py +42 -0
  61. classiq/model_expansions/quantum_operations/call_emitter.py +35 -18
  62. classiq/model_expansions/quantum_operations/composite_emitter.py +1 -1
  63. classiq/model_expansions/quantum_operations/declarative_call_emitter.py +23 -9
  64. classiq/model_expansions/quantum_operations/emitter.py +21 -9
  65. classiq/model_expansions/quantum_operations/quantum_function_call.py +4 -3
  66. classiq/model_expansions/scope.py +63 -10
  67. classiq/model_expansions/sympy_conversion/arithmetics.py +18 -0
  68. classiq/model_expansions/sympy_conversion/expression_to_sympy.py +2 -0
  69. classiq/model_expansions/sympy_conversion/sympy_to_python.py +10 -1
  70. classiq/model_expansions/transformers/model_renamer.py +45 -7
  71. classiq/model_expansions/utils/handles_collector.py +1 -1
  72. classiq/model_expansions/visitors/symbolic_param_inference.py +3 -3
  73. classiq/model_expansions/visitors/variable_references.py +45 -9
  74. classiq/open_library/functions/lookup_table.py +1 -1
  75. classiq/open_library/functions/state_preparation.py +1 -1
  76. classiq/qmod/builtins/functions/allocation.py +2 -2
  77. classiq/qmod/builtins/functions/arithmetic.py +14 -12
  78. classiq/qmod/builtins/functions/standard_gates.py +23 -23
  79. classiq/qmod/create_model_function.py +21 -3
  80. classiq/qmod/declaration_inferrer.py +19 -7
  81. classiq/qmod/generative.py +9 -1
  82. classiq/qmod/global_declarative_switch.py +19 -0
  83. classiq/qmod/native/expression_to_qmod.py +4 -0
  84. classiq/qmod/native/pretty_printer.py +12 -3
  85. classiq/qmod/pretty_print/pretty_printer.py +5 -1
  86. classiq/qmod/python_classical_type.py +4 -5
  87. classiq/qmod/qfunc.py +31 -23
  88. classiq/qmod/qmod_constant.py +15 -7
  89. classiq/qmod/qmod_variable.py +7 -1
  90. classiq/qmod/quantum_expandable.py +29 -1
  91. classiq/qmod/quantum_function.py +45 -25
  92. classiq/qmod/semantics/lambdas.py +6 -2
  93. classiq/qmod/semantics/validation/main_validation.py +17 -4
  94. classiq/qmod/symbolic.py +8 -19
  95. classiq/qmod/symbolic_expr.py +26 -0
  96. classiq/qmod/write_qmod.py +36 -10
  97. classiq/synthesis.py +24 -37
  98. classiq/visualization.py +35 -0
  99. {classiq-0.75.0.dist-info → classiq-0.77.0.dist-info}/METADATA +1 -1
  100. {classiq-0.75.0.dist-info → classiq-0.77.0.dist-info}/RECORD +101 -96
  101. {classiq-0.75.0.dist-info → classiq-0.77.0.dist-info}/WHEEL +0 -0
@@ -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
@@ -10,6 +10,10 @@ from classiq.interface.generator.generated_circuit_data import (
10
10
  StatementType,
11
11
  )
12
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
13
17
  from classiq.interface.model.statement_block import ConcreteQuantumStatement
14
18
 
15
19
  ParameterValue = Union[float, int, str, None]
@@ -98,3 +102,13 @@ def new_function_debug_info_by_node(
98
102
  name="",
99
103
  node=node._as_back_ref(),
100
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
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 ClassiqIndexError("Array slice has non-positive length")
55
- if start < 0 or stop > self._length:
56
- raise ClassiqIndexError("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 ClassiqIndexError(
71
- "Array index is out of bounds (negative indices are not supported)"
72
- )
73
- if index >= self._length:
74
- raise ClassiqIndexError("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,6 +13,7 @@ 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
19
  from classiq.interface.generator.functions.type_name import Struct
@@ -20,15 +21,15 @@ from classiq.interface.generator.functions.type_name import Struct
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):
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):
29
30
  classical_type = Struct(name=proxy._decl.name)
30
- else:
31
- raise ClassiqInternalExpansionError(
32
- f"Unrecognized classical proxy {type(proxy).__name__}"
33
- )
34
- return classical_type
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,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,7 +1,9 @@
1
1
  from collections.abc import Mapping
2
- from typing import Any
2
+ from typing import Any, Optional, Union
3
3
 
4
+ import sympy
4
5
  from sympy import Symbol
6
+ from typing_extensions import Self
5
7
 
6
8
  from classiq.interface.exceptions import ClassiqValueError
7
9
  from classiq.interface.generator.expressions.proxies.quantum.qmod_sized_proxy import (
@@ -12,11 +14,17 @@ from classiq.interface.model.handle_binding import HandleBinding
12
14
 
13
15
  class QmodQScalarProxy(Symbol, QmodSizedProxy):
14
16
  def __new__(cls, handle: HandleBinding, **assumptions: bool) -> "QmodQScalarProxy":
15
- return super().__new__(cls, str(handle), **assumptions)
17
+ return super().__new__(cls, handle.qmod_expr, **assumptions)
16
18
 
17
19
  def __init__(self, handle: HandleBinding, size: int) -> None:
18
20
  super().__init__(handle, size)
19
21
 
22
+ def __copy__(self) -> Self:
23
+ return self
24
+
25
+ def __deepcopy__(self, memo: Optional[dict]) -> Self:
26
+ return self
27
+
20
28
 
21
29
  class QmodQBitProxy(QmodQScalarProxy):
22
30
  def __init__(self, handle: HandleBinding) -> None:
@@ -29,10 +37,18 @@ class QmodQBitProxy(QmodQScalarProxy):
29
37
 
30
38
  class QmodQNumProxy(QmodQScalarProxy):
31
39
  def __init__(
32
- 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],
33
45
  ) -> None:
34
46
  super().__init__(handle, size)
35
- if fraction_digits > size:
47
+ if (
48
+ isinstance(fraction_digits, int)
49
+ and isinstance(size, int)
50
+ and fraction_digits > size
51
+ ):
36
52
  raise ClassiqValueError(
37
53
  f"Quantum numeric of size {size} cannot have {fraction_digits} "
38
54
  f"fraction digits"
@@ -45,11 +61,11 @@ class QmodQNumProxy(QmodQScalarProxy):
45
61
  return "Quantum numeric"
46
62
 
47
63
  @property
48
- def fraction_digits(self) -> int:
64
+ def fraction_digits(self) -> Union[int, sympy.Basic]:
49
65
  return self._fraction_digits
50
66
 
51
67
  @property
52
- def is_signed(self) -> bool:
68
+ def is_signed(self) -> Union[bool, sympy.Basic]:
53
69
  return self._is_signed
54
70
 
55
71
  @property
@@ -1,21 +1,26 @@
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:
18
- return str(self.handle)
20
+ return self.handle.qmod_expr
21
+
22
+ def __repr__(self) -> str:
23
+ return str(self)
19
24
 
20
25
  @property
21
26
  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",
@@ -6,6 +6,9 @@ from typing_extensions import Self
6
6
 
7
7
  from classiq.interface.ast_node import HashableASTNode
8
8
  from classiq.interface.generator.expressions.expression import Expression
9
+ from classiq.interface.generator.expressions.proxies.classical.any_classical_value import (
10
+ AnyClassicalValue,
11
+ )
9
12
  from classiq.interface.generator.expressions.proxies.classical.classical_array_proxy import (
10
13
  ClassicalArrayProxy,
11
14
  )
@@ -94,7 +97,9 @@ class ClassicalList(ClassicalType):
94
97
  return values_with_discriminator(values, "kind", "list")
95
98
 
96
99
  def get_classical_proxy(self, handle: HandleBinding) -> ClassicalProxy:
97
- raise NotImplementedError
100
+ return ClassicalArrayProxy(
101
+ handle, self.element_type, AnyClassicalValue(f"get_field({handle}, 'len')")
102
+ )
98
103
 
99
104
  @property
100
105
  def expressions(self) -> list[Expression]:
@@ -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,8 +112,10 @@ 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