classiq 0.42.2__py3-none-any.whl → 0.43.1__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 (113) hide show
  1. classiq/__init__.py +2 -6
  2. classiq/_internals/api_wrapper.py +6 -12
  3. classiq/_internals/authentication/token_manager.py +5 -2
  4. classiq/_internals/jobs.py +5 -10
  5. classiq/analyzer/rb.py +3 -3
  6. classiq/applications/chemistry/chemistry_model_constructor.py +12 -8
  7. classiq/applications/combinatorial_helpers/pauli_helpers/pauli_utils.py +2 -2
  8. classiq/applications/finance/finance_model_constructor.py +16 -13
  9. classiq/applications/qsvm/__init__.py +1 -3
  10. classiq/applications/qsvm/qsvm_model_constructor.py +7 -6
  11. classiq/exceptions.py +9 -4
  12. classiq/execution/execution_session.py +5 -2
  13. classiq/execution/qnn.py +1 -1
  14. classiq/executor.py +0 -2
  15. classiq/interface/_version.py +1 -1
  16. classiq/interface/chemistry/operator.py +19 -5
  17. classiq/interface/executor/constants.py +1 -0
  18. classiq/interface/finance/function_input.py +16 -10
  19. classiq/interface/generator/application_apis/chemistry_declarations.py +2 -2
  20. classiq/interface/generator/application_apis/qsvm_declarations.py +4 -2
  21. classiq/interface/generator/arith/argument_utils.py +20 -3
  22. classiq/interface/generator/arith/arithmetic_expression_validator.py +3 -26
  23. classiq/interface/generator/arith/binary_ops.py +8 -14
  24. classiq/interface/generator/arith/extremum_operations.py +30 -0
  25. classiq/interface/generator/arith/number_utils.py +1 -1
  26. classiq/interface/generator/arith/unary_ops.py +1 -3
  27. classiq/interface/generator/compiler_keywords.py +1 -1
  28. classiq/interface/generator/expressions/atomic_expression_functions.py +13 -3
  29. classiq/interface/generator/expressions/enums/__init__.py +0 -20
  30. classiq/interface/generator/expressions/enums/finance_functions.py +11 -18
  31. classiq/interface/generator/expressions/non_symbolic_expr.py +119 -0
  32. classiq/interface/generator/expressions/qmod_qarray_proxy.py +52 -37
  33. classiq/interface/generator/expressions/qmod_qscalar_proxy.py +16 -11
  34. classiq/interface/generator/expressions/qmod_sized_proxy.py +5 -5
  35. classiq/interface/generator/function_param_list_without_self_reference.py +0 -10
  36. classiq/interface/generator/function_params.py +0 -4
  37. classiq/interface/generator/functions/__init__.py +0 -20
  38. classiq/interface/generator/functions/builtins/core_library/exponentiation_functions.py +2 -2
  39. classiq/interface/generator/functions/builtins/open_lib_functions.py +530 -1
  40. classiq/interface/generator/functions/classical_type.py +22 -69
  41. classiq/interface/generator/functions/port_declaration.py +0 -11
  42. classiq/interface/generator/model/__init__.py +0 -1
  43. classiq/interface/generator/model/model.py +9 -185
  44. classiq/interface/generator/synthesis_metadata/synthesis_execution_data.py +3 -1
  45. classiq/interface/generator/types/builtin_enum_declarations.py +69 -0
  46. classiq/interface/generator/types/builtin_struct_declarations/pauli_struct_declarations.py +2 -2
  47. classiq/interface/generator/types/enum_declaration.py +57 -0
  48. classiq/interface/jobs.py +36 -65
  49. classiq/interface/model/bind_operation.py +3 -0
  50. classiq/interface/model/classical_parameter_declaration.py +3 -0
  51. classiq/interface/model/handle_binding.py +7 -0
  52. classiq/interface/model/inplace_binary_operation.py +13 -15
  53. classiq/interface/model/model.py +8 -20
  54. classiq/interface/model/native_function_definition.py +0 -17
  55. classiq/interface/model/quantum_function_call.py +63 -182
  56. classiq/interface/model/quantum_type.py +71 -10
  57. classiq/interface/server/routes.py +0 -6
  58. classiq/qmod/__init__.py +3 -3
  59. classiq/qmod/builtins/__init__.py +10 -1
  60. classiq/qmod/builtins/classical_execution_primitives.py +4 -2
  61. classiq/qmod/builtins/enums.py +177 -0
  62. classiq/qmod/builtins/functions.py +1 -2
  63. classiq/qmod/builtins/operations.py +2 -4
  64. classiq/qmod/builtins/structs.py +16 -17
  65. classiq/qmod/declaration_inferrer.py +23 -20
  66. classiq/qmod/model_state_container.py +2 -0
  67. classiq/qmod/native/pretty_printer.py +31 -13
  68. classiq/qmod/pretty_print/pretty_printer.py +52 -27
  69. classiq/qmod/qmod_constant.py +7 -3
  70. classiq/qmod/qmod_parameter.py +2 -1
  71. classiq/qmod/qmod_struct.py +9 -33
  72. classiq/qmod/qmod_variable.py +55 -22
  73. classiq/qmod/quantum_callable.py +6 -1
  74. classiq/qmod/quantum_expandable.py +29 -11
  75. classiq/qmod/quantum_function.py +8 -4
  76. classiq/qmod/semantics/annotation.py +38 -0
  77. classiq/qmod/semantics/error_manager.py +49 -0
  78. classiq/qmod/semantics/static_semantics_visitor.py +308 -0
  79. classiq/qmod/semantics/validation/func_call_validation.py +149 -0
  80. classiq/qmod/semantics/validation/types_validation.py +21 -0
  81. classiq/qmod/symbolic.py +6 -6
  82. classiq/qmod/symbolic_expr.py +26 -11
  83. classiq/qmod/utilities.py +23 -1
  84. {classiq-0.42.2.dist-info → classiq-0.43.1.dist-info}/METADATA +2 -2
  85. {classiq-0.42.2.dist-info → classiq-0.43.1.dist-info}/RECORD +88 -103
  86. classiq/_internals/_qfunc_ext.py +0 -6
  87. classiq/applications/libraries/ampltitude_estimation_library.py +0 -11
  88. classiq/interface/generator/credit_risk_example/linear_gci.py +0 -122
  89. classiq/interface/generator/credit_risk_example/weighted_adder.py +0 -69
  90. classiq/interface/generator/expressions/enums/chemistry.py +0 -28
  91. classiq/interface/generator/expressions/enums/classical_enum.py +0 -20
  92. classiq/interface/generator/expressions/enums/ladder_operator.py +0 -6
  93. classiq/interface/generator/expressions/enums/optimizers.py +0 -9
  94. classiq/interface/generator/expressions/enums/pauli.py +0 -8
  95. classiq/interface/generator/expressions/enums/qsvm_feature_map_entanglement.py +0 -9
  96. classiq/interface/generator/functions/foreign_function_definition.py +0 -114
  97. classiq/interface/generator/functions/function_implementation.py +0 -107
  98. classiq/interface/generator/functions/native_function_definition.py +0 -155
  99. classiq/interface/generator/functions/quantum_function_declaration.py +0 -69
  100. classiq/interface/generator/functions/register.py +0 -44
  101. classiq/interface/generator/functions/register_mapping_data.py +0 -106
  102. classiq/interface/generator/inequality_mixer.py +0 -51
  103. classiq/interface/generator/model/classical_main_validator.py +0 -106
  104. classiq/interface/generator/range_mixer.py +0 -56
  105. classiq/interface/generator/state_propagator.py +0 -74
  106. classiq/interface/model/resolvers/function_call_resolver.py +0 -64
  107. classiq/interface/model/validations/__init__.py +0 -0
  108. classiq/interface/model/validations/handle_validation_base.py +0 -55
  109. classiq/interface/model/validations/handles_validator.py +0 -153
  110. classiq/interface/model/validations/port_to_wire_name_generator.py +0 -12
  111. /classiq/{interface/generator/credit_risk_example → qmod/semantics}/__init__.py +0 -0
  112. /classiq/{interface/model/resolvers → qmod/semantics/validation}/__init__.py +0 -0
  113. {classiq-0.42.2.dist-info → classiq-0.43.1.dist-info}/WHEEL +0 -0
@@ -1,10 +1,9 @@
1
1
  import ast
2
- import builtins
3
2
  import re
4
3
  from _ast import AST
5
- from typing import Any, Optional, Set, Tuple, Type, Union
4
+ from typing import Any, Dict, Optional, Set, Tuple, Type, Union
6
5
 
7
- from typing_extensions import get_args
6
+ from typing_extensions import TypeAlias, get_args
8
7
 
9
8
  from classiq.interface.generator.arith.ast_node_rewrite import AstNodeRewrite
10
9
  from classiq.interface.generator.expressions.sympy_supported_expressions import (
@@ -21,6 +20,7 @@ IDENITIFIER_REGEX = re.compile(r"[a-zA-Z_][a-zA-Z0-9_]*")
21
20
  _REPEATED_VARIABLES_ERROR_MESSAGE: str = (
22
21
  "Repeated variables in the beginning of an arithmetic expression are not allowed."
23
22
  )
23
+ ValidKeyValuePairs: TypeAlias = Dict[str, Set[str]]
24
24
 
25
25
  SupportedNodesTypes = Union[
26
26
  ast.Expression,
@@ -64,14 +64,12 @@ class ExpressionValidator(ast.NodeVisitor):
64
64
  supported_nodes: Tuple[Type[AST], ...],
65
65
  expression_type: str = DEFAULT_EXPRESSION_TYPE,
66
66
  supported_functions: Optional[Set[str]] = None,
67
- supported_attr_values: Optional[Set[str]] = None,
68
67
  mode: str = "eval",
69
68
  ) -> None:
70
69
  super().__init__()
71
70
  self.supported_nodes = supported_nodes
72
71
  self._expression_type = expression_type
73
72
  self._supported_functions = supported_functions or DEFAULT_SUPPORTED_FUNC_NAMES
74
- self._supported_attr_values = supported_attr_values or set()
75
73
  self._mode = mode
76
74
  self._ast_obj: Optional[ast.AST] = None
77
75
 
@@ -170,26 +168,7 @@ class ExpressionValidator(ast.NodeVisitor):
170
168
  self.validate_Constant(node)
171
169
  self.generic_visit(node)
172
170
 
173
- def validate_Attribute(self, node: ast.Attribute) -> None: # noqa: N802
174
- if not (
175
- isinstance(node.value, ast.Name)
176
- and node.value.id in self._supported_attr_values
177
- ):
178
- raise ClassiqValueError(
179
- f"Attribute is not supported for value {node.value}"
180
- )
181
-
182
171
  def visit_Attribute(self, node: ast.Attribute) -> None:
183
- self.validate_Attribute(node)
184
- self.generic_visit(node)
185
-
186
- def visit_FunctionDef(self, node: ast.FunctionDef) -> Any:
187
- if self._mode == "exec":
188
- if hasattr(builtins, node.name):
189
- raise ClassiqValueError(
190
- f"Defining a function named {node.name} is forbidden"
191
- )
192
- self._supported_functions.add(node.name)
193
172
  self.generic_visit(node)
194
173
 
195
174
  @classmethod
@@ -203,7 +182,6 @@ def validate_expression(
203
182
  supported_nodes: Tuple[Type[AST], ...] = DEFAULT_SUPPORTED_NODE_TYPES,
204
183
  expression_type: str = DEFAULT_EXPRESSION_TYPE,
205
184
  supported_functions: Optional[Set[str]] = None,
206
- supported_attr_values: Optional[Set[str]] = None,
207
185
  mode: str = "eval",
208
186
  ) -> ast.AST:
209
187
  supported_functions = supported_functions or set(SYMPY_SUPPORTED_EXPRESSIONS).union(
@@ -213,7 +191,6 @@ def validate_expression(
213
191
  supported_nodes,
214
192
  expression_type,
215
193
  supported_functions,
216
- supported_attr_values,
217
194
  mode,
218
195
  )
219
196
  validator.validate(expression)
@@ -264,10 +264,10 @@ class Adder(InplacableBinaryOpParams[RegisterOrConst, RegisterOrConst]):
264
264
 
265
265
  def _get_result_register(self) -> RegisterArithmeticInfo:
266
266
  left_arg = argument_utils.limit_fraction_places(
267
- self.left_arg, machine_precision=self.machine_precision
267
+ self.left_arg, self.machine_precision
268
268
  )
269
269
  right_arg = argument_utils.limit_fraction_places(
270
- self.right_arg, machine_precision=self.machine_precision
270
+ self.right_arg, self.machine_precision
271
271
  )
272
272
  lb = argument_utils.lower_bound(left_arg) + argument_utils.lower_bound(
273
273
  right_arg
@@ -305,9 +305,7 @@ class Subtractor(InplacableBinaryOpParams[RegisterOrConst, RegisterOrConst]):
305
305
  def _get_effective_arg(
306
306
  arg: RegisterOrConst, machine_precision: int
307
307
  ) -> RegisterOrConst:
308
- return argument_utils.limit_fraction_places(
309
- arg, machine_precision=machine_precision
310
- )
308
+ return argument_utils.limit_fraction_places(arg, machine_precision)
311
309
 
312
310
  @property
313
311
  def effective_left_arg(self) -> RegisterOrConst:
@@ -453,13 +451,9 @@ class Multiplier(BinaryOpWithFloatInputs):
453
451
 
454
452
  def expected_fraction_places(self) -> int:
455
453
  return argument_utils.fraction_places(
456
- argument_utils.limit_fraction_places(
457
- self.left_arg, machine_precision=self.machine_precision
458
- )
454
+ argument_utils.limit_fraction_places(self.left_arg, self.machine_precision)
459
455
  ) + argument_utils.fraction_places(
460
- argument_utils.limit_fraction_places(
461
- self.right_arg, machine_precision=self.machine_precision
462
- )
456
+ argument_utils.limit_fraction_places(self.right_arg, self.machine_precision)
463
457
  )
464
458
 
465
459
  @staticmethod
@@ -483,10 +477,10 @@ class Multiplier(BinaryOpWithFloatInputs):
483
477
  def _get_result_register(self) -> RegisterArithmeticInfo:
484
478
  fraction_places = min(self.machine_precision, self.expected_fraction_places())
485
479
  left_arg = argument_utils.limit_fraction_places(
486
- self.left_arg, machine_precision=self.machine_precision
480
+ self.left_arg, self.machine_precision
487
481
  )
488
482
  right_arg = argument_utils.limit_fraction_places(
489
- self.right_arg, machine_precision=self.machine_precision
483
+ self.right_arg, self.machine_precision
490
484
  )
491
485
  bounds = self._get_bounds((left_arg, right_arg), self.machine_precision)
492
486
 
@@ -572,7 +566,7 @@ class Power(BinaryOpParams[RegisterArithmeticInfo, pydantic.PositiveInt]):
572
566
  return (
573
567
  argument_utils.fraction_places(
574
568
  argument_utils.limit_fraction_places(
575
- self.left_arg, machine_precision=self.machine_precision
569
+ self.left_arg, self.machine_precision
576
570
  )
577
571
  )
578
572
  * self.right_arg
@@ -57,7 +57,21 @@ class Extremum(ArithmeticOperationParams):
57
57
  def _bound_calculator(cls, arg1: float, arg2: float) -> float:
58
58
  pass
59
59
 
60
+ @classmethod
61
+ @abc.abstractmethod
62
+ def _preferred_arg(
63
+ cls, arg1: RegisterOrConst, arg2: RegisterOrConst
64
+ ) -> RegisterOrConst:
65
+ pass
66
+
60
67
  def _get_result_register(self) -> RegisterArithmeticInfo:
68
+ if argument_utils.arg_bounds_overlap((self.left_arg, self.right_arg)):
69
+ return self._get_general_case_result_register()
70
+ return argument_utils.as_arithmetic_info(
71
+ self._preferred_arg(self.left_arg, self.right_arg)
72
+ )
73
+
74
+ def _get_general_case_result_register(self) -> RegisterArithmeticInfo:
61
75
  integer_part_size = max(
62
76
  argument_utils.integer_part_size(self.left_arg),
63
77
  argument_utils.integer_part_size(self.right_arg),
@@ -92,6 +106,14 @@ class Min(Extremum):
92
106
  def _bound_calculator(cls, arg1: float, arg2: float) -> float:
93
107
  return min(arg1, arg2)
94
108
 
109
+ @classmethod
110
+ def _preferred_arg(
111
+ cls, arg1: RegisterOrConst, arg2: RegisterOrConst
112
+ ) -> RegisterOrConst:
113
+ if min(argument_utils.bounds(arg1)) < min(argument_utils.bounds(arg2)):
114
+ return arg1
115
+ return arg2
116
+
95
117
 
96
118
  class Max(Extremum):
97
119
  output_name = "max_value"
@@ -99,3 +121,11 @@ class Max(Extremum):
99
121
  @classmethod
100
122
  def _bound_calculator(cls, arg1: float, arg2: float) -> float:
101
123
  return max(arg1, arg2)
124
+
125
+ @classmethod
126
+ def _preferred_arg(
127
+ cls, arg1: RegisterOrConst, arg2: RegisterOrConst
128
+ ) -> RegisterOrConst:
129
+ if max(argument_utils.bounds(arg1)) > max(argument_utils.bounds(arg2)):
130
+ return arg1
131
+ return arg2
@@ -100,7 +100,7 @@ def bounds_to_integer_part_size(lb: float, ub: float) -> int:
100
100
  )
101
101
 
102
102
 
103
- def limit_fraction_places(number: float, *, machine_precision: int) -> float:
103
+ def limit_fraction_places(number: float, machine_precision: int) -> float:
104
104
  orig_bin_rep = binary_string(number)[::-1]
105
105
  orig_fractions = fraction_places(number)
106
106
 
@@ -64,9 +64,7 @@ class BitwiseInvert(UnaryOpParams):
64
64
  output_name = "inverted"
65
65
 
66
66
  def _get_result_register(self) -> RegisterArithmeticInfo:
67
- eff_arg = argument_utils.limit_fraction_places(
68
- self.arg, machine_precision=self.machine_precision
69
- )
67
+ eff_arg = argument_utils.limit_fraction_places(self.arg, self.machine_precision)
70
68
  if TYPE_CHECKING:
71
69
  assert isinstance(eff_arg, RegisterArithmeticInfo)
72
70
  return RegisterArithmeticInfo(
@@ -1,4 +1,4 @@
1
1
  EXPANDED_KEYWORD = "expanded__"
2
2
  CAPTURE_SUFFIX = "_captured__"
3
3
  LAMBDA_KEYWORD = "lambda__"
4
- GENERAL_INPLACE_ARITHMETIC_AUXILIARY_VARIABLE = "result__temp__"
4
+ INPLACE_ARITH_AUX_VAR_PREFIX = "result__temp__"
@@ -1,8 +1,10 @@
1
1
  from classiq.interface.generator.functions.classical_type import CLASSICAL_ATTRIBUTES
2
2
 
3
- SUPPORTED_BUILTIN_FUNCTIONS = ["len", "sum", "print"]
3
+ SUPPORTED_PYTHON_BUILTIN_FUNCTIONS = {"len", "sum", "print"}
4
4
 
5
- SUPPORTED_ATOMIC_EXPRESSION_FUNCTIONS = {
5
+ SUPPORTED_CLASSIQ_BUILTIN_FUNCTIONS = {
6
+ "do_div",
7
+ "do_slice",
6
8
  "do_subscript",
7
9
  "hypercube_entangler_graph",
8
10
  "grid_entangler_graph",
@@ -15,11 +17,19 @@ SUPPORTED_ATOMIC_EXPRESSION_FUNCTIONS = {
15
17
  "molecule_problem_to_hamiltonian",
16
18
  "fock_hamiltonian_problem_to_hamiltonian",
17
19
  "molecule_ground_state_solution_post_process",
20
+ }
21
+
22
+ SUPPORTED_CLASSIQ_SYMPY_WRAPPERS = {
18
23
  "BitwiseAnd",
19
24
  "BitwiseXor",
20
25
  "BitwiseNot",
21
26
  "BitwiseOr",
22
- *SUPPORTED_BUILTIN_FUNCTIONS,
27
+ }
28
+
29
+ SUPPORTED_ATOMIC_EXPRESSION_FUNCTIONS = {
30
+ *SUPPORTED_CLASSIQ_BUILTIN_FUNCTIONS,
31
+ *SUPPORTED_CLASSIQ_SYMPY_WRAPPERS,
32
+ *SUPPORTED_PYTHON_BUILTIN_FUNCTIONS,
23
33
  }
24
34
 
25
35
  SUPPORTED_ATOMIC_EXPRESSION_FUNCTIONS_QMOD = (
@@ -1,20 +0,0 @@
1
- from enum import EnumMeta
2
-
3
- from .chemistry import Element, FermionMapping
4
- from .finance_functions import FinanceFunctionType
5
- from .ladder_operator import LadderOperator
6
- from .optimizers import Optimizer
7
- from .pauli import Pauli
8
- from .qsvm_feature_map_entanglement import QSVMFeatureMapEntanglement
9
-
10
- BUILTIN_ENUMS = dict(filter(lambda pair: isinstance(pair[1], EnumMeta), vars().items()))
11
-
12
- __all__ = [
13
- "Element",
14
- "FermionMapping",
15
- "FinanceFunctionType",
16
- "LadderOperator",
17
- "Optimizer",
18
- "Pauli",
19
- "QSVMFeatureMapEntanglement",
20
- ]
@@ -1,22 +1,15 @@
1
- from typing import Dict
1
+ from typing import TYPE_CHECKING, Dict
2
2
 
3
- from classiq.interface.generator.expressions.enums.classical_enum import ClassicalEnum
3
+ if TYPE_CHECKING:
4
+ from classiq.qmod.builtins.enums import FinanceFunctionType
4
5
 
5
6
 
6
- class FinanceFunctionType(ClassicalEnum):
7
- VAR = 0
8
- SHORTFALL = 1
9
- X_SQUARE = 2
10
- EUROPEAN_CALL_OPTION = 3
7
+ def get_finance_function_dict() -> Dict[str, "FinanceFunctionType"]:
8
+ from classiq.qmod.builtins.enums import FinanceFunctionType
11
9
 
12
- @staticmethod
13
- def from_string(func_str: str) -> "FinanceFunctionType":
14
- return FINANCE_FUNCTION_STRING[func_str]
15
-
16
-
17
- FINANCE_FUNCTION_STRING: Dict[str, FinanceFunctionType] = {
18
- "var": FinanceFunctionType.VAR,
19
- "expected shortfall": FinanceFunctionType.SHORTFALL,
20
- "x**2": FinanceFunctionType.X_SQUARE,
21
- "european call option": FinanceFunctionType.EUROPEAN_CALL_OPTION,
22
- }
10
+ return {
11
+ "var": FinanceFunctionType.VAR,
12
+ "expected shortfall": FinanceFunctionType.SHORTFALL,
13
+ "x**2": FinanceFunctionType.X_SQUARE,
14
+ "european call option": FinanceFunctionType.EUROPEAN_CALL_OPTION,
15
+ }
@@ -0,0 +1,119 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, NoReturn
4
+
5
+ from classiq.exceptions import ClassiqArithmeticError
6
+
7
+
8
+ class NonSymbolicExpr:
9
+ @property
10
+ def type_name(self) -> str:
11
+ raise NotImplementedError
12
+
13
+ @staticmethod
14
+ def _raise_error(type_name: str, op: str) -> NoReturn:
15
+ raise ClassiqArithmeticError(
16
+ f"Unsupported operand type for {op!r}: {type_name}"
17
+ )
18
+
19
+ def __add__(self, other: Any) -> NoReturn:
20
+ NonSymbolicExpr._raise_error(self.type_name, "+")
21
+
22
+ def __sub__(self, other: Any) -> NoReturn:
23
+ NonSymbolicExpr._raise_error(self.type_name, "-")
24
+
25
+ def __mul__(self, other: Any) -> NoReturn:
26
+ NonSymbolicExpr._raise_error(self.type_name, "*")
27
+
28
+ def __truediv__(self, other: Any) -> NoReturn:
29
+ NonSymbolicExpr._raise_error(self.type_name, "/")
30
+
31
+ def __floordiv__(self, other: Any) -> NoReturn:
32
+ NonSymbolicExpr._raise_error(self.type_name, "//")
33
+
34
+ def __mod__(self, other: Any) -> NoReturn:
35
+ NonSymbolicExpr._raise_error(self.type_name, "%")
36
+
37
+ def __pow__(self, other: Any) -> NoReturn:
38
+ NonSymbolicExpr._raise_error(self.type_name, "**")
39
+
40
+ def __lshift__(self, other: Any) -> NoReturn:
41
+ NonSymbolicExpr._raise_error(self.type_name, "<<")
42
+
43
+ def __rshift__(self, other: Any) -> NoReturn:
44
+ NonSymbolicExpr._raise_error(self.type_name, ">>")
45
+
46
+ def __and__(self, other: Any) -> NoReturn:
47
+ NonSymbolicExpr._raise_error(self.type_name, "&")
48
+
49
+ def __xor__(self, other: Any) -> NoReturn:
50
+ NonSymbolicExpr._raise_error(self.type_name, "^")
51
+
52
+ def __or__(self, other: Any) -> NoReturn:
53
+ NonSymbolicExpr._raise_error(self.type_name, "|")
54
+
55
+ def __radd__(self, other: Any) -> NoReturn:
56
+ NonSymbolicExpr._raise_error(self.type_name, "+")
57
+
58
+ def __rsub__(self, other: Any) -> NoReturn:
59
+ NonSymbolicExpr._raise_error(self.type_name, "-")
60
+
61
+ def __rmul__(self, other: Any) -> NoReturn:
62
+ NonSymbolicExpr._raise_error(self.type_name, "*")
63
+
64
+ def __rtruediv__(self, other: Any) -> NoReturn:
65
+ NonSymbolicExpr._raise_error(self.type_name, "/")
66
+
67
+ def __rfloordiv__(self, other: Any) -> NoReturn:
68
+ NonSymbolicExpr._raise_error(self.type_name, "//")
69
+
70
+ def __rmod__(self, other: Any) -> NoReturn:
71
+ NonSymbolicExpr._raise_error(self.type_name, "%")
72
+
73
+ def __rpow__(self, other: Any) -> NoReturn:
74
+ NonSymbolicExpr._raise_error(self.type_name, "**")
75
+
76
+ def __rlshift__(self, other: Any) -> NoReturn:
77
+ NonSymbolicExpr._raise_error(self.type_name, "<<")
78
+
79
+ def __rrshift__(self, other: Any) -> NoReturn:
80
+ NonSymbolicExpr._raise_error(self.type_name, ">>")
81
+
82
+ def __rand__(self, other: Any) -> NoReturn:
83
+ NonSymbolicExpr._raise_error(self.type_name, "&")
84
+
85
+ def __rxor__(self, other: Any) -> NoReturn:
86
+ NonSymbolicExpr._raise_error(self.type_name, "^")
87
+
88
+ def __ror__(self, other: Any) -> NoReturn:
89
+ NonSymbolicExpr._raise_error(self.type_name, "|")
90
+
91
+ def __lt__(self, other: Any) -> NoReturn:
92
+ NonSymbolicExpr._raise_error(self.type_name, "<")
93
+
94
+ def __le__(self, other: Any) -> NoReturn:
95
+ NonSymbolicExpr._raise_error(self.type_name, "<=")
96
+
97
+ def __eq__(self, other: Any) -> NoReturn:
98
+ NonSymbolicExpr._raise_error(self.type_name, "==")
99
+
100
+ def __ne__(self, other: Any) -> NoReturn:
101
+ NonSymbolicExpr._raise_error(self.type_name, "!=")
102
+
103
+ def __gt__(self, other: Any) -> NoReturn:
104
+ NonSymbolicExpr._raise_error(self.type_name, ">")
105
+
106
+ def __ge__(self, other: Any) -> NoReturn:
107
+ NonSymbolicExpr._raise_error(self.type_name, ">=")
108
+
109
+ def __neg__(self) -> NoReturn:
110
+ NonSymbolicExpr._raise_error(self.type_name, "-")
111
+
112
+ def __pos__(self) -> NoReturn:
113
+ NonSymbolicExpr._raise_error(self.type_name, "+")
114
+
115
+ def __abs__(self) -> NoReturn:
116
+ NonSymbolicExpr._raise_error(self.type_name, "abs")
117
+
118
+ def __invert__(self) -> NoReturn:
119
+ NonSymbolicExpr._raise_error(self.type_name, "~")
@@ -1,6 +1,7 @@
1
- from typing import Any, Mapping, Optional, Tuple, Union
1
+ from typing import Any, Callable, Mapping, Optional, Tuple, Union
2
2
 
3
3
  from classiq.interface.generator.expressions.expression import Expression
4
+ from classiq.interface.generator.expressions.non_symbolic_expr import NonSymbolicExpr
4
5
  from classiq.interface.generator.expressions.qmod_sized_proxy import QmodSizedProxy
5
6
  from classiq.interface.model.handle_binding import (
6
7
  HandleBinding,
@@ -15,22 +16,22 @@ SLICE_OUT_OF_BOUNDS_MSG = "Slice end index out of bounds"
15
16
  QARRAY_ELEMENT_NOT_SUBSCRIPTABLE = "Subscripting an element in QArray is illegal"
16
17
 
17
18
 
18
- class QmodQArrayProxy(QmodSizedProxy):
19
+ class QmodQArrayProxy(NonSymbolicExpr, QmodSizedProxy):
19
20
  def __init__(
20
21
  self,
21
- name: str,
22
- size: int,
23
- slice_: Optional[Tuple[int, int]] = None,
24
- index_: Optional[int] = None,
22
+ handle: HandleBinding,
23
+ element_proxy: Callable[[HandleBinding], QmodSizedProxy],
24
+ element_size: int,
25
+ length: int,
25
26
  ) -> None:
26
- super().__init__(size)
27
- self._name = name
28
- self._slice = slice_
29
- self._index = index_
27
+ super().__init__(handle, element_size * length)
28
+ self._length = length
29
+ self._element_proxy = element_proxy
30
+ self._element_size = element_size
30
31
 
31
- def __getitem__(self, key: Union[slice, int]) -> "QmodQArrayProxy":
32
+ def __getitem__(self, key: Union[slice, int]) -> "QmodSizedProxy":
32
33
  if self._index is not None:
33
- raise ClassiqValueError(QARRAY_ELEMENT_NOT_SUBSCRIPTABLE)
34
+ raise TypeError(QARRAY_ELEMENT_NOT_SUBSCRIPTABLE)
34
35
 
35
36
  new_index: Optional[int] = None
36
37
 
@@ -47,8 +48,14 @@ class QmodQArrayProxy(QmodSizedProxy):
47
48
  ] > self._size:
48
49
  raise ClassiqValueError(SLICE_OUT_OF_BOUNDS_MSG)
49
50
 
51
+ new_handle = self._get_new_handle(new_index, new_slice)
52
+ if new_index is not None:
53
+ return self._element_proxy(new_handle)
50
54
  return QmodQArrayProxy(
51
- self._name, self._size, slice_=new_slice, index_=new_index
55
+ new_handle,
56
+ self._element_proxy,
57
+ self._element_size,
58
+ self._length,
52
59
  )
53
60
 
54
61
  def _get_new_slice(self, start: int, end: int) -> Tuple[int, int]:
@@ -61,29 +68,34 @@ class QmodQArrayProxy(QmodSizedProxy):
61
68
  return "Quantum array"
62
69
 
63
70
  @property
64
- def index(self) -> Optional[int]:
65
- return self._index
71
+ def _index(self) -> Optional[int]:
72
+ if not isinstance(self._handle, SubscriptHandleBinding):
73
+ return None
74
+ return self._handle.index.to_int_value()
66
75
 
67
76
  @property
68
- def slice(self) -> Optional[Tuple[int, int]]:
69
- return self._slice
70
-
71
- @property
72
- def handle(self) -> HandleBinding:
73
- if self._index is not None:
77
+ def _slice(self) -> Optional[Tuple[int, int]]:
78
+ if not isinstance(self._handle, SlicedHandleBinding):
79
+ return None
80
+ return self._handle.start.to_int_value(), self._handle.end.to_int_value()
81
+
82
+ def _get_new_handle(
83
+ self, new_index: Optional[int], new_slice: Tuple[int, int]
84
+ ) -> HandleBinding:
85
+ if new_index is not None:
74
86
  return SubscriptHandleBinding(
75
- name=self._name,
76
- index=Expression(expr=str(self._index)),
77
- )
78
-
79
- if self._slice is not None:
80
- return SlicedHandleBinding(
81
- name=self._name,
82
- start=Expression(expr=str(self._slice[0])),
83
- end=Expression(expr=str(self._slice[1])),
87
+ name=self.handle.name,
88
+ index=Expression(expr=str(new_index)),
84
89
  )
90
+ return SlicedHandleBinding(
91
+ name=self.handle.name,
92
+ start=Expression(expr=str(new_slice[0])),
93
+ end=Expression(expr=str(new_slice[1])),
94
+ )
85
95
 
86
- return HandleBinding(name=self._name)
96
+ @property
97
+ def len(self) -> int:
98
+ return self._length
87
99
 
88
100
  @property
89
101
  def fields(self) -> Mapping[str, Any]:
@@ -91,12 +103,15 @@ class QmodQArrayProxy(QmodSizedProxy):
91
103
  "len": self.len,
92
104
  }
93
105
 
94
- def __len__(self) -> int:
95
- if (slice_ := self.slice) is not None:
96
- return slice_[1] - slice_[0]
97
- elif self.index is not None:
98
- return 1
99
- return self._size
106
+ @property
107
+ def size(self) -> int:
108
+ if (slice_ := self._slice) is not None:
109
+ length = slice_[1] - slice_[0]
110
+ elif self._index is not None:
111
+ length = 1
112
+ else:
113
+ length = self._length
114
+ return length * self._element_size
100
115
 
101
116
  def __str__(self) -> str:
102
117
  return str(self.handle)
@@ -5,21 +5,21 @@ from sympy import Symbol
5
5
  from classiq.interface.generator.expressions.qmod_sized_proxy import QmodSizedProxy
6
6
  from classiq.interface.model.handle_binding import HandleBinding
7
7
 
8
+ from classiq.exceptions import ClassiqValueError
8
9
 
9
- class QmodQScalarProxy(Symbol, QmodSizedProxy):
10
- def __new__(cls, name: str, **assumptions: bool) -> "QmodQScalarProxy":
11
- return super().__new__(cls, name, **assumptions)
12
10
 
13
- def __init__(self, name: str, size: int) -> None:
14
- super().__init__(size)
15
- self.name = name
11
+ class QmodQScalarProxy(Symbol, QmodSizedProxy):
12
+ def __new__(cls, handle: HandleBinding, **assumptions: bool) -> "QmodQScalarProxy":
13
+ return super().__new__(cls, str(handle), **assumptions)
16
14
 
17
- @property
18
- def handle(self) -> HandleBinding:
19
- return HandleBinding(name=self.name)
15
+ def __init__(self, handle: HandleBinding, size: int) -> None:
16
+ super().__init__(handle, size)
20
17
 
21
18
 
22
19
  class QmodQBitProxy(QmodQScalarProxy):
20
+ def __init__(self, handle: HandleBinding) -> None:
21
+ super().__init__(handle, 1)
22
+
23
23
  @property
24
24
  def type_name(self) -> str:
25
25
  return "Quantum bit"
@@ -27,9 +27,14 @@ class QmodQBitProxy(QmodQScalarProxy):
27
27
 
28
28
  class QmodQNumProxy(QmodQScalarProxy):
29
29
  def __init__(
30
- self, name: str, size: int, fraction_digits: int, is_signed: bool
30
+ self, handle: HandleBinding, size: int, fraction_digits: int, is_signed: bool
31
31
  ) -> None:
32
- super().__init__(name, size)
32
+ super().__init__(handle, size)
33
+ if fraction_digits + is_signed > size:
34
+ raise ClassiqValueError(
35
+ f"{'Signed' if is_signed else 'Unsigned'} quantum numeric of size "
36
+ f"{size} cannot have {fraction_digits} fraction digits"
37
+ )
33
38
  self._fraction_digits = fraction_digits
34
39
  self._is_signed = is_signed
35
40
 
@@ -1,16 +1,16 @@
1
1
  from typing import TYPE_CHECKING, Any, Mapping
2
2
 
3
- from classiq.exceptions import ClassiqNotImplementedError
4
-
5
3
  if TYPE_CHECKING:
6
4
  from classiq.interface.model.handle_binding import HandleBinding
7
5
 
8
6
 
9
7
  class QmodSizedProxy:
10
- def __init__(self, size: int) -> None:
8
+ def __init__(self, handle: "HandleBinding", size: int) -> None:
9
+ self._handle = handle
11
10
  self._size = size
12
11
 
13
- def __len__(self) -> int:
12
+ @property
13
+ def size(self) -> int:
14
14
  return self._size
15
15
 
16
16
  def __str__(self) -> str:
@@ -22,7 +22,7 @@ class QmodSizedProxy:
22
22
 
23
23
  @property
24
24
  def handle(self) -> "HandleBinding":
25
- raise ClassiqNotImplementedError("cannot compute handle")
25
+ return self._handle
26
26
 
27
27
  @property
28
28
  def len(self) -> int: