classiq 0.38.0__py3-none-any.whl → 0.40.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 (154) hide show
  1. classiq/__init__.py +22 -22
  2. classiq/_internals/api_wrapper.py +13 -1
  3. classiq/_internals/client.py +12 -2
  4. classiq/analyzer/analyzer.py +3 -1
  5. classiq/applications/__init__.py +1 -8
  6. classiq/applications/chemistry/__init__.py +6 -0
  7. classiq/{applications_model_constructors → applications/chemistry}/chemistry_model_constructor.py +1 -1
  8. classiq/{applications_model_constructors → applications}/combinatorial_helpers/arithmetic/arithmetic_expression.py +1 -1
  9. classiq/{applications_model_constructors → applications}/combinatorial_helpers/combinatorial_problem_utils.py +25 -6
  10. classiq/{applications_model_constructors → applications}/combinatorial_helpers/encoding_mapping.py +1 -1
  11. classiq/{applications_model_constructors → applications}/combinatorial_helpers/encoding_utils.py +1 -1
  12. classiq/{applications_model_constructors → applications}/combinatorial_helpers/memory.py +2 -4
  13. classiq/{applications_model_constructors → applications}/combinatorial_helpers/optimization_model.py +13 -16
  14. classiq/{applications_model_constructors → applications}/combinatorial_helpers/pyomo_utils.py +4 -2
  15. classiq/{applications_model_constructors → applications}/combinatorial_helpers/transformations/encoding.py +3 -10
  16. classiq/{applications_model_constructors → applications}/combinatorial_helpers/transformations/fixed_variables.py +4 -6
  17. classiq/{applications_model_constructors → applications}/combinatorial_helpers/transformations/ising_converter.py +3 -5
  18. classiq/{applications_model_constructors → applications}/combinatorial_helpers/transformations/penalty_support.py +3 -7
  19. classiq/{applications_model_constructors → applications}/combinatorial_helpers/transformations/slack_variables.py +4 -6
  20. classiq/applications/combinatorial_optimization/__init__.py +11 -3
  21. classiq/{applications_model_constructors → applications/combinatorial_optimization}/combinatorial_optimization_model_constructor.py +9 -10
  22. classiq/applications/finance/__init__.py +3 -2
  23. classiq/{applications_model_constructors → applications/finance}/finance_model_constructor.py +24 -14
  24. classiq/applications/grover/__init__.py +11 -0
  25. classiq/applications/libraries/qmci_library.py +35 -0
  26. classiq/applications/qsvm/__init__.py +5 -1
  27. classiq/execution/all_hardware_devices.py +13 -0
  28. classiq/executor.py +2 -1
  29. classiq/interface/_version.py +1 -1
  30. classiq/interface/analyzer/result.py +1 -5
  31. classiq/interface/applications/qsvm.py +4 -2
  32. classiq/interface/ast_node.py +23 -0
  33. classiq/interface/backend/backend_preferences.py +5 -5
  34. classiq/interface/backend/quantum_backend_providers.py +7 -7
  35. classiq/interface/combinatorial_optimization/examples/mht.py +8 -3
  36. classiq/interface/executor/execution_preferences.py +4 -9
  37. classiq/interface/executor/execution_request.py +2 -37
  38. classiq/interface/executor/vqe_result.py +1 -1
  39. classiq/interface/generator/application_apis/chemistry_declarations.py +2 -4
  40. classiq/interface/generator/application_apis/finance_declarations.py +1 -1
  41. classiq/interface/generator/arith/arithmetic_expression_validator.py +2 -0
  42. classiq/interface/generator/builtin_api_builder.py +0 -5
  43. classiq/interface/generator/constant.py +2 -3
  44. classiq/interface/generator/expressions/expression.py +2 -4
  45. classiq/interface/generator/expressions/qmod_qarray_proxy.py +82 -0
  46. classiq/interface/generator/expressions/qmod_qscalar_proxy.py +22 -1
  47. classiq/interface/generator/expressions/qmod_sized_proxy.py +22 -0
  48. classiq/interface/generator/functions/__init__.py +2 -2
  49. classiq/interface/generator/functions/builtins/__init__.py +15 -0
  50. classiq/interface/generator/functions/builtins/core_library/__init__.py +14 -0
  51. classiq/interface/generator/functions/{core_lib_declarations/quantum_functions → builtins/core_library}/atomic_quantum_functions.py +8 -6
  52. classiq/interface/generator/functions/{core_lib_declarations/quantum_functions → builtins/core_library}/exponentiation_functions.py +10 -4
  53. classiq/interface/generator/functions/builtins/internal_operators.py +62 -0
  54. classiq/interface/generator/functions/{core_lib_declarations/quantum_functions/std_lib_functions.py → builtins/open_lib_functions.py} +893 -319
  55. classiq/interface/generator/functions/builtins/quantum_operators.py +37 -0
  56. classiq/interface/generator/functions/classical_type.py +31 -21
  57. classiq/interface/generator/functions/function_declaration.py +2 -2
  58. classiq/interface/generator/hartree_fock.py +10 -2
  59. classiq/interface/generator/model/classical_main_validator.py +1 -1
  60. classiq/interface/generator/model/model.py +1 -1
  61. classiq/interface/generator/model/preferences/preferences.py +4 -2
  62. classiq/interface/generator/quantum_function_call.py +1 -1
  63. classiq/interface/generator/types/struct_declaration.py +2 -4
  64. classiq/interface/model/call_synthesis_data.py +3 -3
  65. classiq/interface/model/classical_if.py +13 -0
  66. classiq/interface/model/classical_parameter_declaration.py +2 -3
  67. classiq/interface/model/{quantum_if_operation.py → control.py} +39 -21
  68. classiq/interface/model/handle_binding.py +3 -2
  69. classiq/interface/model/invert.py +10 -0
  70. classiq/interface/model/model.py +2 -1
  71. classiq/interface/model/power.py +12 -0
  72. classiq/interface/model/quantum_function_call.py +9 -4
  73. classiq/interface/model/quantum_lambda_function.py +3 -9
  74. classiq/interface/model/quantum_statement.py +3 -2
  75. classiq/interface/model/quantum_type.py +8 -9
  76. classiq/interface/model/quantum_variable_declaration.py +2 -2
  77. classiq/interface/model/repeat.py +13 -0
  78. classiq/interface/model/resolvers/function_call_resolver.py +21 -0
  79. classiq/interface/model/statement_block.py +18 -4
  80. classiq/interface/model/validations/handles_validator.py +8 -12
  81. classiq/interface/model/within_apply_operation.py +4 -4
  82. classiq/interface/server/routes.py +0 -4
  83. classiq/qmod/__init__.py +6 -2
  84. classiq/qmod/builtins/classical_functions.py +34 -39
  85. classiq/qmod/builtins/functions.py +287 -300
  86. classiq/qmod/builtins/operations.py +217 -16
  87. classiq/qmod/builtins/structs.py +50 -48
  88. classiq/qmod/declaration_inferrer.py +30 -18
  89. classiq/qmod/native/expression_to_qmod.py +5 -4
  90. classiq/qmod/native/pretty_printer.py +48 -26
  91. classiq/qmod/qmod_constant.py +29 -5
  92. classiq/qmod/qmod_parameter.py +56 -34
  93. classiq/qmod/qmod_struct.py +2 -2
  94. classiq/qmod/qmod_variable.py +87 -43
  95. classiq/qmod/quantum_callable.py +8 -4
  96. classiq/qmod/quantum_expandable.py +25 -20
  97. classiq/qmod/quantum_function.py +29 -2
  98. classiq/qmod/symbolic.py +79 -69
  99. classiq/qmod/symbolic_expr.py +1 -1
  100. classiq/qmod/symbolic_type.py +1 -4
  101. classiq/qmod/utilities.py +29 -0
  102. {classiq-0.38.0.dist-info → classiq-0.40.0.dist-info}/METADATA +1 -1
  103. {classiq-0.38.0.dist-info → classiq-0.40.0.dist-info}/RECORD +122 -141
  104. classiq/applications/benchmarking/__init__.py +0 -9
  105. classiq/applications/benchmarking/mirror_benchmarking.py +0 -70
  106. classiq/applications/numpy_utils.py +0 -37
  107. classiq/applications_model_constructors/__init__.py +0 -25
  108. classiq/applications_model_constructors/combinatorial_helpers/multiple_comp_basis_sp.py +0 -34
  109. classiq/applications_model_constructors/libraries/qmci_library.py +0 -107
  110. classiq/builtin_functions/__init__.py +0 -43
  111. classiq/builtin_functions/amplitude_loading.py +0 -3
  112. classiq/builtin_functions/binary_ops.py +0 -1
  113. classiq/builtin_functions/exponentiation.py +0 -5
  114. classiq/builtin_functions/qpe.py +0 -4
  115. classiq/builtin_functions/qsvm.py +0 -7
  116. classiq/builtin_functions/range_types.py +0 -5
  117. classiq/builtin_functions/standard_gates.py +0 -1
  118. classiq/builtin_functions/state_preparation.py +0 -6
  119. classiq/builtin_functions/suzuki_trotter.py +0 -3
  120. classiq/interface/executor/error_mitigation.py +0 -6
  121. classiq/interface/generator/functions/core_lib_declarations/quantum_functions/__init__.py +0 -18
  122. classiq/interface/generator/functions/core_lib_declarations/quantum_functions/chemistry_functions.py +0 -0
  123. classiq/interface/generator/functions/core_lib_declarations/quantum_operators.py +0 -95
  124. classiq/model/__init__.py +0 -14
  125. classiq/model/composite_function_generator.py +0 -33
  126. classiq/model/function_handler.py +0 -462
  127. classiq/model/logic_flow.py +0 -149
  128. classiq/model/logic_flow_change_handler.py +0 -71
  129. classiq/model/model.py +0 -229
  130. classiq/quantum_functions/__init__.py +0 -17
  131. classiq/quantum_functions/annotation_parser.py +0 -205
  132. classiq/quantum_functions/decorators.py +0 -22
  133. classiq/quantum_functions/function_library.py +0 -181
  134. classiq/quantum_functions/function_parser.py +0 -74
  135. classiq/quantum_functions/quantum_function.py +0 -236
  136. /classiq/{applications_model_constructors → applications}/combinatorial_helpers/__init__.py +0 -0
  137. /classiq/{applications_model_constructors → applications}/combinatorial_helpers/allowed_constraints.py +0 -0
  138. /classiq/{applications_model_constructors → applications}/combinatorial_helpers/arithmetic/__init__.py +0 -0
  139. /classiq/{applications_model_constructors → applications}/combinatorial_helpers/arithmetic/isolation.py +0 -0
  140. /classiq/{applications_model_constructors → applications}/combinatorial_helpers/pauli_helpers/__init__.py +0 -0
  141. /classiq/{applications_model_constructors → applications}/combinatorial_helpers/pauli_helpers/pauli_sparsing.py +0 -0
  142. /classiq/{applications_model_constructors → applications}/combinatorial_helpers/pauli_helpers/pauli_utils.py +0 -0
  143. /classiq/{applications_model_constructors → applications}/combinatorial_helpers/py.typed +0 -0
  144. /classiq/{applications_model_constructors/combinatorial_helpers/transformations → applications/combinatorial_helpers/solvers}/__init__.py +0 -0
  145. /classiq/{applications_model_constructors → applications}/combinatorial_helpers/sympy_utils.py +0 -0
  146. /classiq/{applications_model_constructors/libraries → applications/combinatorial_helpers/transformations}/__init__.py +0 -0
  147. /classiq/{applications_model_constructors → applications}/combinatorial_helpers/transformations/penalty.py +0 -0
  148. /classiq/{applications_model_constructors → applications}/combinatorial_helpers/transformations/sign_seperation.py +0 -0
  149. /classiq/{applications_model_constructors → applications/grover}/grover_model_constructor.py +0 -0
  150. /classiq/{interface/generator/functions/core_lib_declarations → applications/libraries}/__init__.py +0 -0
  151. /classiq/{applications_model_constructors → applications}/libraries/ampltitude_estimation_library.py +0 -0
  152. /classiq/{applications_model_constructors → applications/qsvm}/qsvm_model_constructor.py +0 -0
  153. /classiq/{quantum_register.py → interface/model/quantum_register.py} +0 -0
  154. {classiq-0.38.0.dist-info → classiq-0.40.0.dist-info}/WHEEL +0 -0
@@ -11,18 +11,22 @@ from classiq.interface.generator.functions.port_declaration import (
11
11
  )
12
12
  from classiq.interface.generator.visitor import NodeType, Visitor
13
13
  from classiq.interface.model.bind_operation import BindOperation
14
+ from classiq.interface.model.classical_if import ClassicalIf
14
15
  from classiq.interface.model.classical_parameter_declaration import (
15
16
  ClassicalParameterDeclaration,
16
17
  )
18
+ from classiq.interface.model.control import Control
17
19
  from classiq.interface.model.handle_binding import (
18
20
  HandleBinding,
19
21
  SlicedHandleBinding,
20
22
  SubscriptHandleBinding,
21
23
  )
22
24
  from classiq.interface.model.inplace_binary_operation import InplaceBinaryOperation
25
+ from classiq.interface.model.invert import Invert
23
26
  from classiq.interface.model.model import Model
24
27
  from classiq.interface.model.native_function_definition import NativeFunctionDefinition
25
28
  from classiq.interface.model.port_declaration import PortDeclaration
29
+ from classiq.interface.model.power import Power
26
30
  from classiq.interface.model.quantum_expressions.amplitude_loading_operation import (
27
31
  AmplitudeLoadingOperation,
28
32
  )
@@ -37,7 +41,6 @@ from classiq.interface.model.quantum_function_declaration import (
37
41
  QuantumFunctionDeclaration,
38
42
  QuantumOperandDeclaration,
39
43
  )
40
- from classiq.interface.model.quantum_if_operation import QuantumIfOperation
41
44
  from classiq.interface.model.quantum_lambda_function import QuantumLambdaFunction
42
45
  from classiq.interface.model.quantum_type import (
43
46
  QuantumBit,
@@ -47,11 +50,12 @@ from classiq.interface.model.quantum_type import (
47
50
  from classiq.interface.model.quantum_variable_declaration import (
48
51
  QuantumVariableDeclaration,
49
52
  )
53
+ from classiq.interface.model.repeat import Repeat
50
54
  from classiq.interface.model.statement_block import StatementBlock
51
55
  from classiq.interface.model.variable_declaration_statement import (
52
56
  VariableDeclarationStatement,
53
57
  )
54
- from classiq.interface.model.within_apply_operation import WithinApplyOperation
58
+ from classiq.interface.model.within_apply_operation import WithinApply
55
59
 
56
60
  from classiq import Bool, ClassicalList, Integer, Pauli, Real, Struct, StructDeclaration
57
61
  from classiq.qmod.native.expression_to_qmod import transform_expression
@@ -208,33 +212,51 @@ class DSLPrettyPrinter(Visitor):
208
212
  )
209
213
  return f"{self._indent}{func_call.func_name}{f'[{self.visit(func_call.function.index)}]' if isinstance(func_call.function, OperandIdentifier) else ''}{gen_time_arg_list}({quantum_args});\n"
210
214
 
211
- def visit_QuantumIfOperation(self, op: QuantumIfOperation) -> str:
212
- quantum_if = f"{self._indent}quantum_if ({self.visit(op.expression)}) {{\n"
213
- operand = op.then
214
- if not isinstance(operand, QuantumLambdaFunction):
215
- raise AssertionError(
216
- "Expected quantum_if to be implemented using a lambda function"
217
- )
218
- quantum_if += self._visit_body(operand.body)
219
- quantum_if += f"{self._indent}}}\n"
220
- return quantum_if
221
-
222
- def visit_WithinApplyOperation(self, op: WithinApplyOperation) -> str:
223
- action = op.action
224
- compute = op.compute
225
- if not isinstance(action, QuantumLambdaFunction) or not isinstance(
226
- compute, QuantumLambdaFunction
227
- ):
228
- raise AssertionError(
229
- "Expected within-apply to be implemented using a lambda functions"
230
- )
215
+ def visit_Control(self, op: Control) -> str:
216
+ control = f"{self._indent}control ({self.visit(op.expression)}) {{\n"
217
+ control += self._visit_body(op.body)
218
+ control += f"{self._indent}}}\n"
219
+ return control
220
+
221
+ def visit_ClassicalIf(self, op: ClassicalIf) -> str:
222
+ classical_if = f"{self._indent}if ({self.visit(op.condition)}) {{\n"
223
+ if not op.then:
224
+ raise AssertionError('Expected non empty "then" block')
225
+ classical_if += self._visit_body(op.then)
226
+
227
+ if op.else_:
228
+ classical_if += f"{self._indent}}} else {{\n"
229
+ classical_if += self._visit_body(op.else_)
230
+
231
+ classical_if += f"{self._indent}}}\n"
232
+ return classical_if
233
+
234
+ def visit_WithinApply(self, op: WithinApply) -> str:
231
235
  within_apply_code = f"{self._indent}within {{\n"
232
- within_apply_code += self._visit_body(compute.body)
236
+ within_apply_code += self._visit_body(op.compute)
233
237
  within_apply_code += f"{self._indent}}} apply {{\n"
234
- within_apply_code += self._visit_body(action.body)
238
+ within_apply_code += self._visit_body(op.action)
235
239
  within_apply_code += f"{self._indent}}}\n"
236
240
  return within_apply_code
237
241
 
242
+ def visit_Repeat(self, repeat: Repeat) -> str:
243
+ repeat_code = f"{self._indent}repeat ({self.visit(repeat.iter_var)}: {self.visit(repeat.count)}) {{\n"
244
+ repeat_code += self._visit_body(repeat.body)
245
+ repeat_code += f"{self._indent}}}\n"
246
+ return repeat_code
247
+
248
+ def visit_Power(self, power: Power) -> str:
249
+ power_code = f"{self._indent}power ({self.visit(power.power)}) {{\n"
250
+ power_code += self._visit_body(power.body)
251
+ power_code += f"{self._indent}}}\n"
252
+ return power_code
253
+
254
+ def visit_Invert(self, invert: Invert) -> str:
255
+ invert_code = f"{self._indent}invert {{\n"
256
+ invert_code += self._visit_body(invert.body)
257
+ invert_code += f"{self._indent}}}\n"
258
+ return invert_code
259
+
238
260
  def _visit_body(self, body: StatementBlock) -> str:
239
261
  code = ""
240
262
  self._level += 1
@@ -280,10 +302,10 @@ class DSLPrettyPrinter(Visitor):
280
302
  return var_ref.name
281
303
 
282
304
  def visit_SlicedHandleBinding(self, var_ref: SlicedHandleBinding) -> str:
283
- return str(var_ref)
305
+ return f"{var_ref.name}[{self.visit(var_ref.start)}:{self.visit(var_ref.end)}]"
284
306
 
285
307
  def visit_SubscriptHandleBinding(self, var_ref: SubscriptHandleBinding) -> str:
286
- return str(var_ref)
308
+ return f"{var_ref.name}[{self.visit(var_ref.index)}]"
287
309
 
288
310
  def visit_ArithmeticOperation(self, arith_op: ArithmeticOperation) -> str:
289
311
  op = "^=" if arith_op.inplace_result else "="
@@ -3,12 +3,16 @@ from typing import Any, Optional
3
3
 
4
4
  from classiq.interface.generator.constant import Constant
5
5
  from classiq.interface.generator.expressions.expression import Expression
6
- from classiq.interface.generator.functions.classical_type import QStructBase
6
+ from classiq.interface.generator.functions.classical_type import (
7
+ ClassicalArray,
8
+ ClassicalList,
9
+ CStructBase,
10
+ )
7
11
 
8
12
  from classiq.exceptions import ClassiqError
9
13
  from classiq.qmod.declaration_inferrer import python_type_to_qmod
10
14
  from classiq.qmod.model_state_container import ModelStateContainer
11
- from classiq.qmod.qmod_parameter import QParam, QParamStruct
15
+ from classiq.qmod.qmod_parameter import CParam, CParamList, CParamStruct
12
16
 
13
17
 
14
18
  class QConstant:
@@ -57,20 +61,40 @@ class QConstant:
57
61
  value=Expression(expr=expr),
58
62
  )
59
63
 
60
- def __getattr__(self, name: str) -> QParam:
64
+ def __getattr__(self, name: str) -> CParam:
61
65
  self.add_to_model()
62
66
 
63
67
  py_type = type(self._value)
64
68
  if (
65
69
  QConstant.CURRENT_QMODULE is None
66
70
  or not inspect.isclass(py_type)
67
- or not issubclass(py_type, QStructBase)
71
+ or not issubclass(py_type, CStructBase)
68
72
  ):
69
73
  return self.__getattribute__(name)
70
74
 
71
- return QParamStruct.get_field(
75
+ return CParamStruct.get_field(
72
76
  QConstant.CURRENT_QMODULE, self.name, py_type.__name__, name
73
77
  )
74
78
 
79
+ def __getitem__(self, item: Any) -> CParam:
80
+ self.add_to_model()
81
+
82
+ assert QConstant.CURRENT_QMODULE is not None
83
+
84
+ qmod_type = python_type_to_qmod(
85
+ self._py_type, qmodule=QConstant.CURRENT_QMODULE
86
+ )
87
+ if qmod_type is None:
88
+ raise ClassiqError("Invalid QMOD type")
89
+
90
+ if not isinstance(qmod_type, (ClassicalList, ClassicalArray)):
91
+ raise ClassiqError("Invalid subscript to non-list constant")
92
+
93
+ return CParamList(
94
+ self.name,
95
+ qmod_type,
96
+ QConstant.CURRENT_QMODULE,
97
+ )[item]
98
+
75
99
  def __str__(self) -> str:
76
100
  return self.name
@@ -26,21 +26,54 @@ _T = TypeVar("_T")
26
26
 
27
27
 
28
28
  if TYPE_CHECKING:
29
+ SymbolicSuperclass = SymbolicExpr
30
+ else:
31
+ SymbolicSuperclass = Symbolic
29
32
 
30
- class QParam(SymbolicExpr, Generic[_T]):
31
- pass
32
33
 
33
- else:
34
+ class CParam(SymbolicSuperclass):
35
+ pass
36
+
37
+
38
+ class QParam(CParam, Generic[_T]):
39
+ pass
40
+
41
+
42
+ class CInt(CParam):
43
+ pass
44
+
45
+
46
+ class CReal(CParam):
47
+ pass
48
+
49
+
50
+ class CBool(CParam):
51
+ pass
52
+
53
+
54
+ _P = ParamSpec("_P")
55
+
56
+
57
+ class ArrayBase(Generic[_P]):
58
+ # Support comma-separated generic args in older Python versions
59
+ if sys.version_info[0:2] < (3, 10):
60
+
61
+ def __class_getitem__(cls, args) -> _GenericAlias:
62
+ return _GenericAlias(cls, args)
63
+
64
+
65
+ class CArray(CParam, ArrayBase[_P]):
66
+ if TYPE_CHECKING:
34
67
 
35
- class QParam(Symbolic, Generic[_T]):
36
- pass
68
+ @property
69
+ def len(self) -> int: ...
37
70
 
38
71
 
39
- class QParamScalar(QParam, SymbolicExpr):
72
+ class CParamScalar(CParam, SymbolicExpr):
40
73
  pass
41
74
 
42
75
 
43
- class QParamList(QParam):
76
+ class CParamList(CParam):
44
77
  def __init__(
45
78
  self,
46
79
  expr: str,
@@ -51,7 +84,7 @@ class QParamList(QParam):
51
84
  self._qmodule = qmodule
52
85
  self._list_type = list_type
53
86
 
54
- def __getitem__(self, key: Any) -> QParam:
87
+ def __getitem__(self, key: Any) -> CParam:
55
88
  return create_param(
56
89
  f"({self})[{key}]",
57
90
  self._list_type.element_type,
@@ -60,14 +93,15 @@ class QParamList(QParam):
60
93
 
61
94
  def __len__(self) -> int:
62
95
  raise ClassiqValueError(
63
- "len(<expr>) is not supported for QMod lists - use <expr>.len() instead"
96
+ "len(<expr>) is not supported for QMod lists - use <expr>.len instead"
64
97
  )
65
98
 
66
- def len(self) -> QParamScalar:
67
- return QParamScalar(f"len({self})")
99
+ @property
100
+ def len(self) -> CParamScalar:
101
+ return CParamScalar(f"get_field({self}, 'len')")
68
102
 
69
103
 
70
- class QParamStruct(QParam):
104
+ class CParamStruct(CParam):
71
105
  def __init__(
72
106
  self, expr: str, struct_type: Struct, *, qmodule: ModelStateContainer
73
107
  ) -> None:
@@ -75,8 +109,8 @@ class QParamStruct(QParam):
75
109
  self._qmodule = qmodule
76
110
  self._struct_type = struct_type
77
111
 
78
- def __getattr__(self, field_name: str) -> QParam:
79
- return QParamStruct.get_field(
112
+ def __getattr__(self, field_name: str) -> CParam:
113
+ return CParamStruct.get_field(
80
114
  self._qmodule, str(self), self._struct_type.name, field_name
81
115
  )
82
116
 
@@ -86,7 +120,7 @@ class QParamStruct(QParam):
86
120
  variable_name: str,
87
121
  struct_name: str,
88
122
  field_name: str,
89
- ) -> QParam:
123
+ ) -> CParam:
90
124
  struct_decl = StructDeclaration.BUILTIN_STRUCT_DECLARATIONS.get(
91
125
  struct_name, qmodule.type_decls.get(struct_name)
92
126
  )
@@ -104,27 +138,15 @@ class QParamStruct(QParam):
104
138
  )
105
139
 
106
140
 
107
- _P = ParamSpec("_P")
108
-
109
-
110
- class ArrayBase(Generic[_P]):
111
- # Support comma-separated generic args in older Python versions
112
- if sys.version_info[0:2] < (3, 10):
113
-
114
- def __class_getitem__(cls, args) -> _GenericAlias:
115
- return _GenericAlias(cls, args)
116
-
117
-
118
- class Array(ArrayBase[_P]):
119
- pass
120
-
121
-
122
141
  def create_param(
123
142
  expr_str: str, ctype: ClassicalType, qmodule: ModelStateContainer
124
- ) -> QParam:
143
+ ) -> CParam:
125
144
  if isinstance(ctype, (ClassicalList, ClassicalArray)):
126
- return QParamList(expr_str, ctype, qmodule=qmodule)
145
+ return CParamList(expr_str, ctype, qmodule=qmodule)
127
146
  elif isinstance(ctype, Struct):
128
- return QParamStruct(expr_str, ctype, qmodule=qmodule)
147
+ return CParamStruct(expr_str, ctype, qmodule=qmodule)
129
148
  else:
130
- return QParamScalar(expr_str)
149
+ return CParamScalar(expr_str)
150
+
151
+
152
+ Array = CArray
@@ -3,7 +3,7 @@ from typing import Any, Type
3
3
 
4
4
  from typing_extensions import dataclass_transform
5
5
 
6
- from classiq.interface.generator.functions.classical_type import QStructBase
6
+ from classiq.interface.generator.functions.classical_type import CStructBase
7
7
 
8
8
 
9
9
  def _qmod_val_to_expr_str(val: Any) -> str:
@@ -30,7 +30,7 @@ def struct(user_class: Type) -> Type:
30
30
 
31
31
  user_dataclass = type(
32
32
  user_class.__name__,
33
- (QStructBase, dataclasses.dataclass(user_class)),
33
+ (CStructBase, dataclasses.dataclass(user_class)),
34
34
  dict(),
35
35
  )
36
36
  user_dataclass.__repr__ = _new_repr # type:ignore[assignment]
@@ -1,8 +1,7 @@
1
1
  import abc
2
2
  import sys
3
3
  from contextlib import contextmanager
4
- from typing import _GenericAlias # type: ignore[attr-defined]
5
- from typing import (
4
+ from typing import ( # type: ignore[attr-defined]
6
5
  TYPE_CHECKING,
7
6
  Any,
8
7
  ForwardRef,
@@ -14,6 +13,7 @@ from typing import (
14
13
  Type,
15
14
  TypeVar,
16
15
  Union,
16
+ _GenericAlias,
17
17
  get_args,
18
18
  get_origin,
19
19
  overload,
@@ -21,11 +21,16 @@ from typing import (
21
21
 
22
22
  from typing_extensions import Annotated, ParamSpec, Self, _AnnotatedAlias
23
23
 
24
+ from classiq.interface.ast_node import SourceReference
24
25
  from classiq.interface.generator.expressions.expression import Expression
25
26
  from classiq.interface.generator.functions.port_declaration import (
26
27
  PortDeclarationDirection,
27
28
  )
28
- from classiq.interface.model.handle_binding import HandleBinding, SlicedHandleBinding
29
+ from classiq.interface.model.handle_binding import (
30
+ HandleBinding,
31
+ SlicedHandleBinding,
32
+ SubscriptHandleBinding,
33
+ )
29
34
  from classiq.interface.model.port_declaration import PortDeclaration
30
35
  from classiq.interface.model.quantum_expressions.amplitude_loading_operation import (
31
36
  AmplitudeLoadingOperation,
@@ -41,15 +46,16 @@ from classiq.interface.model.quantum_type import (
41
46
  )
42
47
 
43
48
  from classiq.exceptions import ClassiqValueError
44
- from classiq.qmod.qmod_parameter import ArrayBase, QParam, QParamScalar
49
+ from classiq.qmod.qmod_parameter import ArrayBase, CBool, CInt, CParam, CParamScalar
45
50
  from classiq.qmod.quantum_callable import QCallable
46
51
  from classiq.qmod.symbolic_expr import Symbolic, SymbolicExpr
47
52
  from classiq.qmod.symbolic_type import SymbolicTypes
48
- from classiq.qmod.utilities import version_portable_get_args
53
+ from classiq.qmod.utilities import get_source_ref, version_portable_get_args
49
54
 
50
55
  ILLEGAL_SLICING_STEP_MSG = "Slicing with a step of a quantum variable is not supported"
51
56
  SLICE_OUT_OF_BOUNDS_MSG = "Slice end index out of bounds"
52
57
  UNSUPPORTED_ELEMENT_TYPE = "Only QBit is supported as element type for QArray"
58
+ QARRAY_ELEMENT_NOT_SUBSCRIPTABLE = "Subscripting an element in QArray is illegal"
53
59
 
54
60
 
55
61
  def _is_input_output_typehint(type_hint: Any) -> bool:
@@ -77,8 +83,9 @@ def _no_current_expandable() -> Iterator[None]:
77
83
  QCallable.CURRENT_EXPANDABLE = current_expandable
78
84
 
79
85
 
80
- class QVar:
86
+ class QVar(Symbolic):
81
87
  def __init__(self, name: str) -> None:
88
+ super().__init__(name)
82
89
  self._name = name
83
90
  if QCallable.CURRENT_EXPANDABLE is not None:
84
91
  QCallable.CURRENT_EXPANDABLE.add_local_handle(
@@ -115,6 +122,9 @@ class QVar:
115
122
  assert type_hint == cls or get_origin(type_hint) == cls
116
123
  return PortDeclarationDirection.Inout
117
124
 
125
+ def __str__(self) -> str:
126
+ return str(self.get_handle_binding())
127
+
118
128
 
119
129
  _Q = TypeVar("_Q", bound=QVar)
120
130
  Output = Annotated[_Q, PortDeclarationDirection.Output]
@@ -126,7 +136,9 @@ class QScalar(QVar, SymbolicExpr):
126
136
  QVar.__init__(self, name)
127
137
  SymbolicExpr.__init__(self, name)
128
138
 
129
- def _insert_arith_operation(self, expr: SymbolicTypes, inplace: bool) -> None:
139
+ def _insert_arith_operation(
140
+ self, expr: SymbolicTypes, inplace: bool, source_ref: SourceReference
141
+ ) -> None:
130
142
  # Fixme: Arithmetic operations are not yet supported on slices (see CAD-12670)
131
143
  if TYPE_CHECKING:
132
144
  assert QCallable.CURRENT_EXPANDABLE is not None
@@ -135,16 +147,20 @@ class QScalar(QVar, SymbolicExpr):
135
147
  expression=Expression(expr=str(expr)),
136
148
  result_var=self.get_handle_binding(),
137
149
  inplace_result=inplace,
150
+ source_ref=source_ref,
138
151
  )
139
152
  )
140
153
 
141
- def _insert_amplitude_loading(self, expr: SymbolicTypes) -> None:
154
+ def _insert_amplitude_loading(
155
+ self, expr: SymbolicTypes, source_ref: SourceReference
156
+ ) -> None:
142
157
  if TYPE_CHECKING:
143
158
  assert QCallable.CURRENT_EXPANDABLE is not None
144
159
  QCallable.CURRENT_EXPANDABLE.append_statement_to_body(
145
160
  AmplitudeLoadingOperation(
146
161
  expression=Expression(expr=str(expr)),
147
162
  result_var=self.get_handle_binding(),
163
+ source_ref=source_ref,
148
164
  )
149
165
  )
150
166
 
@@ -157,7 +173,7 @@ class QScalar(QVar, SymbolicExpr):
157
173
  f"Invalid argument {other!r} for out-of-place arithmetic operation"
158
174
  )
159
175
 
160
- self._insert_arith_operation(other, False)
176
+ self._insert_arith_operation(other, False, get_source_ref(sys._getframe(1)))
161
177
  return self
162
178
 
163
179
  def __ixor__(self, other: Any) -> Self:
@@ -166,7 +182,7 @@ class QScalar(QVar, SymbolicExpr):
166
182
  f"Invalid argument {other!r} for in-place arithmetic operation"
167
183
  )
168
184
 
169
- self._insert_arith_operation(other, True)
185
+ self._insert_arith_operation(other, True, get_source_ref(sys._getframe(1)))
170
186
  return self
171
187
 
172
188
  def __imul__(self, other: Any) -> Self:
@@ -175,7 +191,7 @@ class QScalar(QVar, SymbolicExpr):
175
191
  f"Invalid argument {other!r} for out of ampltiude encoding operation"
176
192
  )
177
193
 
178
- self._insert_amplitude_loading(other)
194
+ self._insert_amplitude_loading(other, get_source_ref(sys._getframe(1)))
179
195
  return self
180
196
 
181
197
 
@@ -202,18 +218,18 @@ class QNum(Generic[_P], QScalar):
202
218
  def __init__(
203
219
  self,
204
220
  name: str,
205
- size: Union[int, QParam[int]],
206
- is_signed: Union[bool, QParam[bool]],
207
- fraction_digits: Union[int, QParam[int]],
221
+ size: Union[int, CInt],
222
+ is_signed: Union[bool, CBool],
223
+ fraction_digits: Union[int, CInt],
208
224
  ):
209
225
  pass
210
226
 
211
227
  def __init__(
212
228
  self,
213
229
  name: str,
214
- size: Union[int, QParam[int], None] = None,
215
- is_signed: Union[bool, QParam[bool], None] = None,
216
- fraction_digits: Union[int, QParam[int], None] = None,
230
+ size: Union[int, CInt, None] = None,
231
+ is_signed: Union[bool, CBool, None] = None,
232
+ fraction_digits: Union[int, CInt, None] = None,
217
233
  ):
218
234
  if (
219
235
  size is None
@@ -239,8 +255,8 @@ class QNum(Generic[_P], QScalar):
239
255
  type_args = type_args[0]
240
256
  if len(type_args) != 3:
241
257
  raise ClassiqValueError(
242
- "QNum receives three type arguments: QNum[size: int | QParam[int], "
243
- "is_signed: bool | QParam[bool], fraction_digits: int | QParam[int]]"
258
+ "QNum receives three type arguments: QNum[size: int | CInt, "
259
+ "is_signed: bool | CBool, fraction_digits: int | CInt]"
244
260
  )
245
261
  return cls.QMOD_TYPE(
246
262
  size=Expression(expr=get_type_hint_expr(type_args[0])),
@@ -256,16 +272,16 @@ class QNum(Generic[_P], QScalar):
256
272
  )
257
273
 
258
274
  @property
259
- def size(self) -> QParamScalar:
260
- return QParamScalar(f"len({self._name})")
275
+ def size(self) -> CParamScalar:
276
+ return CParamScalar(f"get_field({self._name}, 'size')")
261
277
 
262
278
  @property
263
- def fraction_digits(self) -> QParamScalar:
264
- return QParamScalar(f"fraction_digits({self._name})")
279
+ def fraction_digits(self) -> CParamScalar:
280
+ return CParamScalar(f"get_field({self._name}, 'fraction_digits')")
265
281
 
266
282
  @property
267
- def is_signed(self) -> QParamScalar:
268
- return QParamScalar(f"is_signed({self._name})")
283
+ def is_signed(self) -> CParamScalar:
284
+ return CParamScalar(f"get_field({self._name}, 'is_signed')")
269
285
 
270
286
  # Support comma-separated generic args in older Python versions
271
287
  if sys.version_info[0:2] < (3, 10):
@@ -279,35 +295,53 @@ class QArray(ArrayBase[_P], QVar):
279
295
  self,
280
296
  name: str,
281
297
  element_type: _GenericAlias = QBit,
282
- length: Optional[Union[int, QParam[int]]] = None,
298
+ length: Optional[Union[int, CInt]] = None,
299
+ # TODO [CAD-18620]: improve type hints
283
300
  slice_: Optional[Tuple[int, int]] = None,
301
+ index_: Optional[Union[int, CInt]] = None,
284
302
  ) -> None:
285
303
  if element_type is not QBit:
286
304
  raise ClassiqValueError(UNSUPPORTED_ELEMENT_TYPE)
287
305
  self._element_type = element_type
288
306
  self._length = length
289
307
  self._slice = slice_
308
+ self._index = index_
290
309
  super().__init__(name)
291
310
 
292
311
  def get_handle_binding(self) -> HandleBinding:
293
- if self._slice is None:
294
- return HandleBinding(name=self._name)
295
- return SlicedHandleBinding(
296
- name=self._name,
297
- start=Expression(expr=str(self._slice[0])),
298
- end=Expression(expr=str(self._slice[1])),
299
- )
312
+ if self._index is not None:
313
+ return SubscriptHandleBinding(
314
+ name=self._name,
315
+ index=Expression(expr=str(self._index)),
316
+ )
317
+
318
+ if self._slice is not None:
319
+ return SlicedHandleBinding(
320
+ name=self._name,
321
+ start=Expression(expr=str(self._slice[0])),
322
+ end=Expression(expr=str(self._slice[1])),
323
+ )
324
+
325
+ return HandleBinding(name=self._name)
326
+
327
+ def __getitem__(self, key: Union[slice, int, CInt]) -> "QArray":
328
+ if self._index is not None:
329
+ raise ClassiqValueError(QARRAY_ELEMENT_NOT_SUBSCRIPTABLE)
330
+
331
+ # TODO [CAD-18620]: improve type hints
332
+ new_index: Optional[Any] = None
300
333
 
301
- def __getitem__(self, key: Union[slice, int, QParam]) -> "QArray":
302
- offset = self._slice[0] if self._slice is not None else 0
303
334
  if isinstance(key, slice):
304
335
  if key.step is not None:
305
336
  raise ClassiqValueError(ILLEGAL_SLICING_STEP_MSG)
306
- new_slice = (offset + key.start, offset + key.stop)
337
+ new_slice = self._get_new_slice(key.start, key.stop)
338
+
307
339
  else:
308
- if isinstance(key, QParam) and not isinstance(key, QParamScalar):
340
+ if isinstance(key, CParam) and not isinstance(key, CParamScalar):
309
341
  raise ClassiqValueError("Non-classical parameter for slicing")
310
- new_slice = (offset + key, offset + key + 1)
342
+ new_slice = self._get_new_slice(key, key + 1)
343
+ new_index = new_slice[0]
344
+
311
345
  if (
312
346
  self._slice is not None
313
347
  and not isinstance(new_slice[1], Symbolic)
@@ -322,23 +356,33 @@ class QArray(ArrayBase[_P], QVar):
322
356
  raise ClassiqValueError(SLICE_OUT_OF_BOUNDS_MSG)
323
357
  # prevent addition to local handles, since this is used for slicing existing local handles
324
358
  with _no_current_expandable():
325
- return QArray(self._name, length=self._length, slice_=new_slice)
359
+ return QArray(
360
+ self._name, length=self._length, slice_=new_slice, index_=new_index
361
+ )
362
+
363
+ # TODO [CAD-18620]: improve type hints
364
+ def _get_new_slice(self, start: Any, end: Any) -> Tuple[Any, Any]:
365
+ if self._slice is not None:
366
+ return (self._slice[0] + start, self._slice[0] + end)
367
+ return (start, end)
326
368
 
327
369
  def __len__(self) -> int:
328
370
  raise ClassiqValueError(
329
- "len(<var>) is not supported for quantum variables - use <var>.len() instead"
371
+ "len(<var>) is not supported for quantum variables - use <var>.len instead"
330
372
  )
331
373
 
332
374
  if TYPE_CHECKING:
333
375
 
376
+ @property
334
377
  def len(self) -> int: ...
335
378
 
336
379
  else:
337
380
 
338
- def len(self) -> QParamScalar:
381
+ @property
382
+ def len(self) -> CParamScalar:
339
383
  if self._length is not None:
340
- return QParamScalar(f"{self._length}")
341
- return QParamScalar(f"len({self._name})")
384
+ return CParamScalar(f"{self._length}")
385
+ return CParamScalar(f"get_field({self._name}, 'len')")
342
386
 
343
387
  @classmethod
344
388
  def to_qmod_quantum_type(cls, type_hint: Any) -> QuantumType: