classiq 0.69.0__py3-none-any.whl → 0.70.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 (60) hide show
  1. classiq/analyzer/analyzer.py +0 -18
  2. classiq/analyzer/url_utils.py +9 -4
  3. classiq/applications/combinatorial_helpers/pyomo_utils.py +2 -0
  4. classiq/interface/_version.py +1 -1
  5. classiq/interface/backend/quantum_backend_providers.py +6 -0
  6. classiq/interface/chemistry/operator.py +1 -21
  7. classiq/interface/executor/quantum_instruction_set.py +1 -0
  8. classiq/interface/generator/arith/arithmetic.py +21 -6
  9. classiq/interface/generator/circuit_code/circuit_code.py +4 -0
  10. classiq/interface/generator/circuit_code/types_and_constants.py +1 -0
  11. classiq/interface/generator/expressions/atomic_expression_functions.py +1 -2
  12. classiq/interface/generator/expressions/expression_types.py +8 -2
  13. classiq/interface/generator/expressions/proxies/__init__.py +0 -0
  14. classiq/interface/generator/expressions/proxies/classical/__init__.py +0 -0
  15. classiq/interface/generator/expressions/proxies/classical/classical_array_proxy.py +75 -0
  16. classiq/interface/generator/expressions/proxies/classical/classical_proxy.py +26 -0
  17. classiq/interface/generator/expressions/proxies/classical/classical_scalar_proxy.py +32 -0
  18. classiq/interface/generator/expressions/proxies/classical/classical_struct_proxy.py +31 -0
  19. classiq/interface/generator/expressions/proxies/quantum/__init__.py +0 -0
  20. classiq/interface/generator/expressions/{qmod_qarray_proxy.py → proxies/quantum/qmod_qarray_proxy.py} +3 -1
  21. classiq/interface/generator/expressions/{qmod_qscalar_proxy.py → proxies/quantum/qmod_qscalar_proxy.py} +3 -1
  22. classiq/interface/generator/expressions/{qmod_qstruct_proxy.py → proxies/quantum/qmod_qstruct_proxy.py} +3 -1
  23. classiq/interface/generator/functions/classical_type.py +23 -29
  24. classiq/interface/generator/functions/type_name.py +26 -2
  25. classiq/interface/generator/generated_circuit_data.py +21 -3
  26. classiq/interface/generator/model/preferences/preferences.py +1 -0
  27. classiq/interface/generator/quantum_program.py +0 -1
  28. classiq/interface/model/native_function_definition.py +0 -10
  29. classiq/interface/model/quantum_type.py +15 -3
  30. classiq/model_expansions/atomic_expression_functions_defs.py +9 -3
  31. classiq/model_expansions/evaluators/arg_type_match.py +4 -2
  32. classiq/model_expansions/evaluators/classical_expression.py +2 -2
  33. classiq/model_expansions/evaluators/control.py +1 -1
  34. classiq/model_expansions/evaluators/parameter_types.py +58 -16
  35. classiq/model_expansions/evaluators/quantum_type_utils.py +7 -57
  36. classiq/model_expansions/expression_evaluator.py +3 -1
  37. classiq/model_expansions/generative_functions.py +63 -4
  38. classiq/model_expansions/quantum_operations/arithmetic/__init__.py +0 -0
  39. classiq/model_expansions/quantum_operations/arithmetic/explicit_boolean_expressions.py +60 -0
  40. classiq/model_expansions/quantum_operations/assignment_result_processor.py +9 -0
  41. classiq/model_expansions/quantum_operations/quantum_function_call.py +0 -22
  42. classiq/model_expansions/scope.py +7 -6
  43. classiq/model_expansions/scope_initialization.py +17 -16
  44. classiq/model_expansions/transformers/model_renamer.py +13 -4
  45. classiq/model_expansions/visitors/variable_references.py +8 -4
  46. classiq/open_library/functions/__init__.py +2 -0
  47. classiq/open_library/functions/lookup_table.py +58 -0
  48. classiq/qmod/declaration_inferrer.py +3 -1
  49. classiq/qmod/qmod_parameter.py +8 -0
  50. classiq/qmod/qmod_variable.py +11 -14
  51. classiq/qmod/semantics/annotation/call_annotation.py +0 -28
  52. classiq/qmod/semantics/annotation/qstruct_annotator.py +21 -1
  53. classiq/qmod/semantics/validation/main_validation.py +1 -1
  54. classiq/qmod/semantics/validation/type_hints.py +29 -0
  55. classiq/qmod/utilities.py +38 -1
  56. {classiq-0.69.0.dist-info → classiq-0.70.0.dist-info}/METADATA +10 -12
  57. {classiq-0.69.0.dist-info → classiq-0.70.0.dist-info}/RECORD +60 -49
  58. {classiq-0.69.0.dist-info → classiq-0.70.0.dist-info}/WHEEL +1 -1
  59. /classiq/interface/generator/expressions/{qmod_struct_instance.py → proxies/classical/qmod_struct_instance.py} +0 -0
  60. /classiq/interface/generator/expressions/{qmod_sized_proxy.py → proxies/quantum/qmod_sized_proxy.py} +0 -0
@@ -4,7 +4,15 @@ from typing import TYPE_CHECKING, Any, Literal, Optional
4
4
  import pydantic
5
5
 
6
6
  from classiq.interface.exceptions import ClassiqExpansionError
7
- from classiq.interface.generator.expressions.qmod_qstruct_proxy import QmodQStructProxy
7
+ from classiq.interface.generator.expressions.proxies.classical.classical_proxy import (
8
+ ClassicalProxy,
9
+ )
10
+ from classiq.interface.generator.expressions.proxies.classical.classical_struct_proxy import (
11
+ ClassicalStructProxy,
12
+ )
13
+ from classiq.interface.generator.expressions.proxies.quantum.qmod_qstruct_proxy import (
14
+ QmodQStructProxy,
15
+ )
8
16
  from classiq.interface.generator.functions.classical_type import (
9
17
  ClassicalType,
10
18
  )
@@ -16,6 +24,7 @@ from classiq.interface.model.quantum_type import (
16
24
 
17
25
  if TYPE_CHECKING:
18
26
  from classiq.interface.generator.functions.concrete_types import ConcreteQuantumType
27
+ from classiq.interface.generator.types.struct_declaration import StructDeclaration
19
28
 
20
29
 
21
30
  class TypeName(ClassicalType, QuantumType):
@@ -24,6 +33,9 @@ class TypeName(ClassicalType, QuantumType):
24
33
  _assigned_fields: Optional[Mapping[str, "ConcreteQuantumType"]] = (
25
34
  pydantic.PrivateAttr(default=None)
26
35
  )
36
+ _classical_struct_decl: Optional["StructDeclaration"] = pydantic.PrivateAttr(
37
+ default=None
38
+ )
27
39
 
28
40
  @pydantic.model_validator(mode="before")
29
41
  @classmethod
@@ -40,7 +52,7 @@ class TypeName(ClassicalType, QuantumType):
40
52
  )
41
53
 
42
54
  def get_proxy(self, handle: "HandleBinding") -> "QmodQStructProxy":
43
- from classiq.interface.generator.expressions.qmod_qstruct_proxy import (
55
+ from classiq.interface.generator.expressions.proxies.quantum.qmod_qstruct_proxy import (
44
56
  QmodQStructProxy,
45
57
  )
46
58
 
@@ -81,6 +93,18 @@ class TypeName(ClassicalType, QuantumType):
81
93
  field_type.is_evaluated for field_type in self.fields.values()
82
94
  )
83
95
 
96
+ @property
97
+ def has_classical_struct_decl(self) -> bool:
98
+ return self._classical_struct_decl is not None
99
+
100
+ def set_classical_struct_decl(self, decl: "StructDeclaration") -> None:
101
+ self._classical_struct_decl = decl
102
+
103
+ def get_classical_proxy(self, handle: HandleBinding) -> ClassicalProxy:
104
+ if self._classical_struct_decl is None:
105
+ raise ClassiqExpansionError(f"Type {self.name!r} is undefined")
106
+ return ClassicalStructProxy(handle, self._classical_struct_decl)
107
+
84
108
 
85
109
  class Enum(TypeName):
86
110
  pass
@@ -1,4 +1,5 @@
1
1
  import logging
2
+ import re
2
3
  from typing import Literal, Optional, Union
3
4
 
4
5
  import pydantic
@@ -7,6 +8,9 @@ from typing_extensions import TypeAlias
7
8
 
8
9
  from classiq.interface.debug_info.back_ref_util import is_allocate_or_free_by_backref
9
10
  from classiq.interface.enum_utils import StrEnum
11
+ from classiq.interface.generator.compiler_keywords import (
12
+ generate_original_function_name,
13
+ )
10
14
  from classiq.interface.generator.control_state import ControlState
11
15
  from classiq.interface.generator.register_role import RegisterRole
12
16
  from classiq.interface.generator.synthesis_metadata.synthesis_execution_data import (
@@ -32,6 +36,11 @@ ParameterName = str
32
36
  IOQubitMapping: TypeAlias = dict[str, tuple[int, ...]]
33
37
 
34
38
  CLASSIQ_HIERARCHY_SEPARATOR: Literal["__"] = "__"
39
+ QASM_SEPARATOR = "_"
40
+ SPLIT_MARKER: str = "part"
41
+ PART_SUFFIX_REGEX = re.compile(
42
+ rf".+{QASM_SEPARATOR}{SPLIT_MARKER}{QASM_SEPARATOR}(\d+)$"
43
+ )
35
44
 
36
45
  VISUALIZATION_HIDE_LIST = [
37
46
  "apply_to_all",
@@ -168,9 +177,18 @@ class FunctionDebugInfoInterface(pydantic.BaseModel):
168
177
 
169
178
  @property
170
179
  def name(self) -> str:
171
- if self.generated_function is None:
172
- return ""
173
- return self.generated_function.name
180
+ generated_name = self.generated_function.name if self.generated_function else ""
181
+ # Temp fix for currently "supported" statements (same as for level_ property)
182
+ if generated_name in {StatementType.CONTROL, StatementType.POWER}:
183
+ return generated_name
184
+ if self.first_back_ref and isinstance(self.first_back_ref, QuantumFunctionCall):
185
+ name = generate_original_function_name(self.first_back_ref.func_name)
186
+ if part_match := PART_SUFFIX_REGEX.match(generated_name):
187
+ suffix = f" [{part_match.group(1)}]"
188
+ else:
189
+ suffix = ""
190
+ return f"{name}{suffix}"
191
+ return generated_name
174
192
 
175
193
  @property
176
194
  def first_back_ref(self) -> Optional[ConcreteQuantumStatement]:
@@ -47,6 +47,7 @@ class QuantumFormat(StrEnum):
47
47
  IONQ = "ionq"
48
48
  CIRQ_JSON = "cirq_json"
49
49
  QASM_CIRQ_COMPATIBLE = "qasm_cirq_compatible"
50
+ EXECUTION_SERIALIZATION = "_execution_serialization"
50
51
 
51
52
 
52
53
  _SERVICE_PROVIDER_TO_FORMAT: dict[Provider, QuantumFormat] = {
@@ -117,7 +117,6 @@ class QuantumProgram(VersionedModel, CircuitCodeInterface):
117
117
  instruction_set: Optional[QuantumInstructionSet] = None,
118
118
  ) -> quantum_code.QuantumCode:
119
119
  initial_values = initial_values or self.initial_values
120
-
121
120
  if instruction_set is not None:
122
121
  code, syntax = (
123
122
  self.program_circuit.get_code(instruction_set),
@@ -10,13 +10,6 @@ if TYPE_CHECKING:
10
10
  from classiq.interface.model.statement_block import StatementBlock
11
11
 
12
12
 
13
- class FunctionSynthesisData(pydantic.BaseModel):
14
- should_synthesize_separately: bool = pydantic.Field(
15
- default=False,
16
- description="Whether the function should be synthesized separately.",
17
- )
18
-
19
-
20
13
  class NativeFunctionDefinition(NamedParamsQuantumFunctionDeclaration):
21
14
  """
22
15
  Facilitates the creation of a user-defined composite function
@@ -28,6 +21,3 @@ class NativeFunctionDefinition(NamedParamsQuantumFunctionDeclaration):
28
21
  body: "StatementBlock" = pydantic.Field(
29
22
  default_factory=list, description="List of function calls to perform."
30
23
  )
31
- synthesis_data: FunctionSynthesisData = pydantic.Field(
32
- default_factory=FunctionSynthesisData, deprecated=True, exclude=True
33
- )
@@ -11,13 +11,17 @@ from classiq.interface.generator.arith.register_user_input import (
11
11
  RegisterUserInput,
12
12
  )
13
13
  from classiq.interface.generator.expressions.expression import Expression
14
- from classiq.interface.generator.expressions.qmod_qarray_proxy import QmodQArrayProxy
15
- from classiq.interface.generator.expressions.qmod_qscalar_proxy import (
14
+ from classiq.interface.generator.expressions.proxies.quantum.qmod_qarray_proxy import (
15
+ QmodQArrayProxy,
16
+ )
17
+ from classiq.interface.generator.expressions.proxies.quantum.qmod_qscalar_proxy import (
16
18
  QmodQBitProxy,
17
19
  QmodQNumProxy,
18
20
  QmodQScalarProxy,
19
21
  )
20
- from classiq.interface.generator.expressions.qmod_sized_proxy import QmodSizedProxy
22
+ from classiq.interface.generator.expressions.proxies.quantum.qmod_sized_proxy import (
23
+ QmodSizedProxy,
24
+ )
21
25
  from classiq.interface.helpers.pydantic_model_helpers import values_with_discriminator
22
26
  from classiq.interface.model.handle_binding import HandleBinding
23
27
 
@@ -213,6 +217,14 @@ class QuantumNumeric(QuantumScalar):
213
217
  0 if self.fraction_digits is None else self.fraction_digits.to_int_value()
214
218
  )
215
219
 
220
+ @property
221
+ def is_qbit(self) -> bool:
222
+ return (
223
+ self.size_in_bits == 1
224
+ and self.fraction_digits_value == 0
225
+ and not self.sign_value
226
+ )
227
+
216
228
  def _update_size_in_bits_from_declaration(self) -> None:
217
229
  if self.size is not None and self.size.is_evaluated():
218
230
  self._size_in_bits = self.size.to_int_value()
@@ -12,9 +12,15 @@ from classiq.interface.generator.expressions.expression_types import (
12
12
  ExpressionValue,
13
13
  QmodStructInstance,
14
14
  )
15
- from classiq.interface.generator.expressions.qmod_qscalar_proxy import QmodQNumProxy
16
- from classiq.interface.generator.expressions.qmod_qstruct_proxy import QmodQStructProxy
17
- from classiq.interface.generator.expressions.qmod_sized_proxy import QmodSizedProxy
15
+ from classiq.interface.generator.expressions.proxies.quantum.qmod_qscalar_proxy import (
16
+ QmodQNumProxy,
17
+ )
18
+ from classiq.interface.generator.expressions.proxies.quantum.qmod_qstruct_proxy import (
19
+ QmodQStructProxy,
20
+ )
21
+ from classiq.interface.generator.expressions.proxies.quantum.qmod_sized_proxy import (
22
+ QmodSizedProxy,
23
+ )
18
24
  from classiq.interface.generator.expressions.type_proxy import TypeProxy
19
25
  from classiq.interface.generator.functions.classical_function_declaration import (
20
26
  ClassicalFunctionDeclaration,
@@ -3,10 +3,12 @@ from enum import Enum
3
3
  from typing import Any
4
4
 
5
5
  from classiq.interface.exceptions import ClassiqExpansionError
6
- from classiq.interface.generator.expressions.qmod_sized_proxy import QmodSizedProxy
7
- from classiq.interface.generator.expressions.qmod_struct_instance import (
6
+ from classiq.interface.generator.expressions.proxies.classical.qmod_struct_instance import (
8
7
  QmodStructInstance,
9
8
  )
9
+ from classiq.interface.generator.expressions.proxies.quantum.qmod_sized_proxy import (
10
+ QmodSizedProxy,
11
+ )
10
12
  from classiq.interface.generator.functions.classical_type import (
11
13
  StructMetaType,
12
14
  )
@@ -23,13 +23,13 @@ def evaluate_classical_expression(expr: Expression, scope: Scope) -> Evaluated:
23
23
  )
24
24
  for name, evaluated in all_symbols
25
25
  if isinstance(evaluated.value, QuantumSymbol)
26
- and evaluated.value.quantum_type.has_size_in_bits
26
+ and evaluated.value.quantum_type.is_evaluated
27
27
  }
28
28
  uninitialized_locals = {
29
29
  name
30
30
  for name, evaluated in all_symbols
31
31
  if isinstance(evaluated.value, QuantumSymbol)
32
- and not evaluated.value.quantum_type.has_size_in_bits
32
+ and not evaluated.value.quantum_type.is_evaluated
33
33
  }
34
34
 
35
35
  ret = evaluate(expr, locals_dict, uninitialized_locals)
@@ -8,7 +8,7 @@ from classiq.interface.generator.arith.argument_utils import (
8
8
  unsigned_integer_interpretation,
9
9
  )
10
10
  from classiq.interface.generator.arith.register_user_input import RegisterArithmeticInfo
11
- from classiq.interface.generator.expressions.qmod_qscalar_proxy import (
11
+ from classiq.interface.generator.expressions.proxies.quantum.qmod_qscalar_proxy import (
12
12
  QmodQNumProxy,
13
13
  QmodQScalarProxy,
14
14
  QmodSizedProxy,
@@ -1,9 +1,10 @@
1
- from typing import Union
1
+ from typing import TypeVar, Union
2
2
 
3
3
  from classiq.interface.exceptions import (
4
4
  ClassiqExpansionError,
5
5
  ClassiqInternalExpansionError,
6
6
  )
7
+ from classiq.interface.generator.expressions.expression import Expression
7
8
  from classiq.interface.generator.functions.concrete_types import ConcreteQuantumType
8
9
  from classiq.interface.generator.functions.port_declaration import (
9
10
  PortDeclarationDirection,
@@ -31,8 +32,6 @@ from classiq.model_expansions.evaluators.classical_expression import (
31
32
  from classiq.model_expansions.evaluators.quantum_type_utils import (
32
33
  copy_type_information,
33
34
  set_element_type,
34
- set_fraction_digits,
35
- set_is_signed,
36
35
  set_length,
37
36
  set_size,
38
37
  )
@@ -162,9 +161,14 @@ def _evaluate_qarray_in_quantum_symbol(
162
161
  )
163
162
  set_element_type(type_to_update, new_element_type)
164
163
  if type_to_update.length is not None:
165
- new_length = evaluate_classical_expression(
166
- type_to_update.length, scope
167
- ).as_type(int)
164
+ new_length = _eval_expr(
165
+ type_to_update.length,
166
+ scope,
167
+ int,
168
+ type_to_update.type_name,
169
+ "length",
170
+ param_name,
171
+ )
168
172
  set_length(type_to_update, new_length)
169
173
  return type_to_update
170
174
 
@@ -173,23 +177,61 @@ def _evaluate_qnum_in_quantum_symbol(
173
177
  type_to_update: QuantumNumeric, scope: Scope, param_name: str
174
178
  ) -> QuantumNumeric:
175
179
  if type_to_update.is_signed is not None:
176
- new_is_sign = evaluate_classical_expression(
177
- type_to_update.is_signed, scope
178
- ).as_type(bool)
179
- set_is_signed(type_to_update, new_is_sign, param_name)
180
+ new_is_sign = _eval_expr(
181
+ type_to_update.is_signed,
182
+ scope,
183
+ bool,
184
+ type_to_update.type_name,
185
+ "sign",
186
+ param_name,
187
+ )
188
+ type_to_update.is_signed = Expression(expr=str(new_is_sign))
180
189
  if type_to_update.fraction_digits is not None:
181
- new_fraction_digits = evaluate_classical_expression(
182
- type_to_update.fraction_digits, scope
183
- ).as_type(int)
184
- set_fraction_digits(type_to_update, new_fraction_digits, param_name)
190
+ new_fraction_digits = _eval_expr(
191
+ type_to_update.fraction_digits,
192
+ scope,
193
+ int,
194
+ type_to_update.type_name,
195
+ "fraction digits",
196
+ param_name,
197
+ )
198
+ type_to_update.fraction_digits = Expression(expr=str(new_fraction_digits))
185
199
  if type_to_update.size is not None:
186
- new_size = evaluate_classical_expression(type_to_update.size, scope).as_type(
187
- int
200
+ new_size = _eval_expr(
201
+ type_to_update.size,
202
+ scope,
203
+ int,
204
+ type_to_update.type_name,
205
+ "size",
206
+ param_name,
188
207
  )
189
208
  set_size(type_to_update, new_size, param_name)
190
209
  return type_to_update
191
210
 
192
211
 
212
+ _EXPR_TYPE = TypeVar("_EXPR_TYPE")
213
+
214
+
215
+ def _eval_expr(
216
+ expression: Expression,
217
+ scope: Scope,
218
+ expected_type: type[_EXPR_TYPE],
219
+ type_name: str,
220
+ attr_name: str,
221
+ param_name: str,
222
+ ) -> _EXPR_TYPE:
223
+ val = evaluate_classical_expression(expression, scope).value
224
+ if expected_type is int and isinstance(val, float):
225
+ val = int(val)
226
+ if not isinstance(val, expected_type):
227
+ raise ClassiqExpansionError(
228
+ f"When inferring the type of parameter {param_name!r}: "
229
+ f"{type_name} {attr_name} must be {expected_type.__name__}, got "
230
+ f"{str(val)!r}"
231
+ )
232
+ return val
233
+
234
+
193
235
  def _evaluate_qstruct_in_quantum_symbol(
194
236
  type_to_update: TypeName, scope: Scope, param_name: str
195
237
  ) -> TypeName:
@@ -1,5 +1,4 @@
1
1
  from collections.abc import Sequence
2
- from typing import Optional
3
2
 
4
3
  from classiq.interface.exceptions import (
5
4
  ClassiqExpansionError,
@@ -33,12 +32,11 @@ def copy_type_information(
33
32
  if isinstance(to_type, QuantumBit):
34
33
  set_size(to_type, from_type.size_in_bits, to_param_name)
35
34
  elif isinstance(to_type, QuantumNumeric):
36
- set_is_signed(to_type, getattr(from_type, "sign_value", None), to_param_name)
37
- set_fraction_digits(
38
- to_type,
39
- getattr(from_type, "fraction_digits_value", None),
40
- to_param_name,
41
- )
35
+ if to_type.size is None and isinstance(from_type, QuantumNumeric):
36
+ to_type.is_signed = Expression(expr=str(from_type.sign_value))
37
+ to_type.fraction_digits = Expression(
38
+ expr=str(from_type.fraction_digits_value)
39
+ )
42
40
  set_size(to_type, from_type.size_in_bits, to_param_name)
43
41
  elif isinstance(to_type, QuantumBitvector):
44
42
  if isinstance(from_type, QuantumBitvector) and type( # noqa: E721
@@ -73,8 +71,8 @@ def set_size(quantum_type: QuantumType, size: int, param_name: str) -> None:
73
71
  if isinstance(quantum_type, QuantumNumeric):
74
72
  quantum_type.size = Expression(expr=str(size))
75
73
  if not quantum_type.has_sign or not quantum_type.has_fraction_digits:
76
- set_is_signed(quantum_type, False, param_name)
77
- set_fraction_digits(quantum_type, 0, param_name)
74
+ quantum_type.is_signed = Expression(expr="False")
75
+ quantum_type.fraction_digits = Expression(expr="0")
78
76
  elif isinstance(quantum_type, QuantumBitvector):
79
77
  if quantum_type.has_length:
80
78
  if size % quantum_type.length_value != 0:
@@ -107,54 +105,6 @@ def set_size(quantum_type: QuantumType, size: int, param_name: str) -> None:
107
105
  set_size(fields_without_size[0], size - predetermined_size_part, param_name)
108
106
 
109
107
 
110
- def set_fraction_digits(
111
- quantum_numeric: QuantumNumeric, fraction_digits: Optional[int], param_name: str
112
- ) -> None:
113
- if fraction_digits is not None and fraction_digits < 0:
114
- raise ClassiqExpansionError(
115
- f"Number of fraction digits for {param_name!r} was deduced to be negative: "
116
- f"{fraction_digits!r}"
117
- )
118
-
119
- if (
120
- fraction_digits is not None
121
- and quantum_numeric.fraction_digits is not None
122
- and quantum_numeric.fraction_digits.is_evaluated()
123
- and quantum_numeric.fraction_digits_value != fraction_digits
124
- ):
125
- raise ClassiqExpansionError(
126
- f"Fraction digits mismatch for variable {param_name!r} between declared "
127
- f"fraction digits {quantum_numeric.fraction_digits_value!r} and assigned fraction "
128
- f"digits {fraction_digits!r}"
129
- )
130
-
131
- if not (
132
- quantum_numeric.fraction_digits is not None
133
- and quantum_numeric.fraction_digits.is_evaluated()
134
- ):
135
- quantum_numeric.fraction_digits = Expression(expr=str(fraction_digits or 0))
136
-
137
-
138
- def set_is_signed(
139
- quantum_numeric: QuantumNumeric, is_signed: Optional[bool], param_name: str
140
- ) -> None:
141
- if (
142
- is_signed is not None
143
- and quantum_numeric.is_signed is not None
144
- and quantum_numeric.is_signed.is_evaluated()
145
- and quantum_numeric.sign_value != is_signed
146
- ):
147
- raise ClassiqExpansionError(
148
- f"Sign mismatch for variable {param_name!r} between declared sign {quantum_numeric.sign_value!r} and assigned sign {is_signed!r}"
149
- )
150
-
151
- if not (
152
- quantum_numeric.is_signed is not None
153
- and quantum_numeric.is_signed.is_evaluated()
154
- ):
155
- quantum_numeric.is_signed = Expression(expr=str(is_signed or False))
156
-
157
-
158
108
  def set_element_type(
159
109
  quantum_array: QuantumBitvector, element_type: ConcreteQuantumType
160
110
  ) -> None:
@@ -17,7 +17,9 @@ from classiq.interface.generator.expressions.expression_constants import (
17
17
  )
18
18
  from classiq.interface.generator.expressions.expression_types import ExpressionValue
19
19
  from classiq.interface.generator.expressions.non_symbolic_expr import NonSymbolicExpr
20
- from classiq.interface.generator.expressions.qmod_sized_proxy import QmodSizedProxy
20
+ from classiq.interface.generator.expressions.proxies.quantum.qmod_sized_proxy import (
21
+ QmodSizedProxy,
22
+ )
21
23
  from classiq.interface.generator.expressions.sympy_supported_expressions import (
22
24
  SYMPY_SUPPORTED_EXPRESSIONS,
23
25
  )
@@ -1,10 +1,29 @@
1
1
  from collections.abc import Mapping
2
+ from sys import exc_info
3
+ from types import TracebackType
2
4
  from typing import TYPE_CHECKING, Any
3
5
 
4
- from classiq.interface.generator.expressions.qmod_struct_instance import (
6
+ from classiq.interface.exceptions import (
7
+ ClassiqExpansionError,
8
+ ClassiqInternalExpansionError,
9
+ )
10
+ from classiq.interface.generator.expressions.proxies.classical.classical_array_proxy import (
11
+ ClassicalArrayProxy,
12
+ )
13
+ from classiq.interface.generator.expressions.proxies.classical.classical_proxy import (
14
+ ClassicalProxy,
15
+ )
16
+ from classiq.interface.generator.expressions.proxies.classical.classical_scalar_proxy import (
17
+ ClassicalScalarProxy,
18
+ )
19
+ from classiq.interface.generator.expressions.proxies.classical.classical_struct_proxy import (
20
+ ClassicalStructProxy,
21
+ )
22
+ from classiq.interface.generator.expressions.proxies.classical.qmod_struct_instance import (
5
23
  QmodStructInstance,
6
24
  )
7
- from classiq.interface.generator.functions.type_name import Struct
25
+ from classiq.interface.generator.functions.classical_type import ClassicalArray
26
+ from classiq.interface.generator.functions.type_name import Struct, TypeName
8
27
  from classiq.interface.helpers.pydantic_model_helpers import nameables_to_dict
9
28
  from classiq.interface.model.native_function_definition import NativeFunctionDefinition
10
29
  from classiq.interface.model.port_declaration import PortDeclaration
@@ -20,9 +39,10 @@ from classiq.model_expansions.closure import (
20
39
  GenerativeClosure,
21
40
  )
22
41
  from classiq.model_expansions.scope import Evaluated, QuantumSymbol
42
+ from classiq.qmod.cparam import CParam
23
43
  from classiq.qmod.generative import generative_mode_context, set_frontend_interpreter
24
44
  from classiq.qmod.model_state_container import QMODULE
25
- from classiq.qmod.qmod_parameter import CParamStruct
45
+ from classiq.qmod.qmod_parameter import CParamStruct, create_param
26
46
  from classiq.qmod.qmod_variable import QNum, _create_qvar_for_qtype
27
47
  from classiq.qmod.quantum_expandable import (
28
48
  QTerminalCallable,
@@ -37,6 +57,23 @@ if TYPE_CHECKING:
37
57
  )
38
58
 
39
59
 
60
+ def _unwrap_traceback_frame(e: Exception) -> Exception:
61
+ fallback_error = ClassiqExpansionError(str(e))
62
+ traceback = exc_info()[2]
63
+ if traceback is None:
64
+ return fallback_error
65
+ back_frame = traceback.tb_frame.f_back
66
+ if back_frame is None:
67
+ return fallback_error
68
+ back_tb = TracebackType(
69
+ tb_next=None,
70
+ tb_frame=back_frame,
71
+ tb_lasti=back_frame.f_lasti,
72
+ tb_lineno=back_frame.f_lineno,
73
+ )
74
+ return e.with_traceback(back_tb)
75
+
76
+
40
77
  class LenList(list):
41
78
  @property
42
79
  def len(self) -> int:
@@ -45,7 +82,10 @@ class LenList(list):
45
82
  def __getitem__(self, item: Any) -> Any:
46
83
  if isinstance(item, QNum):
47
84
  return SymbolicExpr(f"{self}[{item}]", True)
48
- return super().__getitem__(item)
85
+ try:
86
+ return super().__getitem__(item)
87
+ except (IndexError, TypeError) as e:
88
+ raise _unwrap_traceback_frame(e) from None
49
89
 
50
90
  @classmethod
51
91
  def wrap(cls, obj: Any) -> Any:
@@ -54,6 +94,22 @@ class LenList(list):
54
94
  return LenList([cls.wrap(item) for item in obj])
55
95
 
56
96
 
97
+ def _create_qmod_classical_var(proxy: ClassicalProxy) -> CParam:
98
+ if isinstance(proxy, ClassicalScalarProxy):
99
+ classical_type = proxy._classical_type
100
+ elif isinstance(proxy, ClassicalArrayProxy):
101
+ classical_type = ClassicalArray(
102
+ element_type=proxy._element_type, size=proxy._length
103
+ )
104
+ elif isinstance(proxy, ClassicalStructProxy):
105
+ classical_type = TypeName(name=proxy._decl.name)
106
+ else:
107
+ raise ClassiqInternalExpansionError(
108
+ f"Unrecognized classical proxy {type(proxy).__name__}"
109
+ )
110
+ return create_param(str(proxy.handle), classical_type, QMODULE)
111
+
112
+
57
113
  def translate_ast_arg_to_python_qmod(param: PositionalArg, evaluated: Evaluated) -> Any:
58
114
  if isinstance(param, PortDeclaration):
59
115
  quantum_symbol = evaluated.as_type(QuantumSymbol)
@@ -90,6 +146,9 @@ def translate_ast_arg_to_python_qmod(param: PositionalArg, evaluated: Evaluated)
90
146
  struct_type=Struct(name=classical_value.struct_declaration.name),
91
147
  qmodule=QMODULE,
92
148
  )
149
+ if isinstance(classical_value, ClassicalProxy):
150
+ return _create_qmod_classical_var(classical_value)
151
+
93
152
  return LenList.wrap(classical_value)
94
153
 
95
154
 
@@ -0,0 +1,60 @@
1
+ from classiq.interface.exceptions import ClassiqValueError
2
+ from classiq.interface.generator.arith.arithmetic import is_bool
3
+ from classiq.interface.generator.expressions.expression import Expression
4
+ from classiq.interface.model.quantum_expressions.arithmetic_operation import (
5
+ ArithmeticOperation,
6
+ ArithmeticOperationKind,
7
+ )
8
+ from classiq.interface.model.quantum_type import QuantumBit, QuantumNumeric
9
+
10
+ from classiq.model_expansions.scope import QuantumSymbol
11
+
12
+
13
+ def validate_assignment_bool_expression(
14
+ result_symbol: QuantumSymbol, expr: str, op_kind: ArithmeticOperationKind
15
+ ) -> None:
16
+ if not is_bool(expr):
17
+ return
18
+ _validate_target_type(result_symbol, expr, op_kind)
19
+
20
+
21
+ def _validate_target_type(
22
+ target_symbol: QuantumSymbol, expr: str, op_kind: ArithmeticOperationKind
23
+ ) -> None:
24
+ supported_types = _supported_types()
25
+ if target_symbol.quantum_type.qmod_type_name not in supported_types:
26
+ raise ClassiqValueError(
27
+ f'The expression has been evaluated to "{expr}" which is a Boolean value. '
28
+ f"Cannot perform {op_kind.value} operation of Boolean expression to result variable '{target_symbol.handle}' of type {target_symbol.quantum_type.qmod_type_name}. "
29
+ f"Boolean expressions can only be applied on {' or '.join(supported_types)}."
30
+ )
31
+
32
+
33
+ def convert_assignment_bool_expression(op: ArithmeticOperation) -> None:
34
+ if not is_bool(op.expression.expr):
35
+ return
36
+ op.expression = op.expression.model_copy(
37
+ update=dict(expr="1" if op.expression.expr == "True" else "0")
38
+ )
39
+
40
+
41
+ def convert_inplace_op_bool_expression(
42
+ op: ArithmeticOperation, target: QuantumSymbol
43
+ ) -> None:
44
+ if not is_bool(op.expression.expr):
45
+ return
46
+ _validate_target_type(target, op.expression.expr, op.operation_kind)
47
+ op.expression = op.expression.model_copy(
48
+ update=dict(expr="1" if op.expression.expr == "True" else "0")
49
+ )
50
+
51
+
52
+ def _supported_types() -> tuple[str, ...]:
53
+ return (
54
+ QuantumBit().qmod_type_name,
55
+ QuantumNumeric(
56
+ size=Expression(expr="1"),
57
+ is_signed=Expression(expr="False"),
58
+ fraction_digits=Expression(expr="0"),
59
+ ).qmod_type_name,
60
+ )
@@ -11,6 +11,10 @@ from classiq.interface.model.quantum_expressions.quantum_expression import (
11
11
  )
12
12
 
13
13
  from classiq.model_expansions.evaluators.quantum_type_utils import copy_type_information
14
+ from classiq.model_expansions.quantum_operations.arithmetic.explicit_boolean_expressions import (
15
+ convert_assignment_bool_expression,
16
+ validate_assignment_bool_expression,
17
+ )
14
18
  from classiq.model_expansions.quantum_operations.emitter import Emitter
15
19
  from classiq.model_expansions.scope import QuantumSymbol
16
20
  from classiq.model_expansions.transformers.ast_renamer import rename_variables
@@ -24,6 +28,7 @@ class AssignmentResultProcessor(Emitter[QuantumAssignmentOperation]):
24
28
  ):
25
29
  direction = PortDeclarationDirection.Output
26
30
  self._update_result_type(op)
31
+ convert_assignment_bool_expression(op)
27
32
  else:
28
33
  direction = PortDeclarationDirection.Inout
29
34
  self._capture_handle(op.result_var, direction)
@@ -47,6 +52,10 @@ class AssignmentResultProcessor(Emitter[QuantumAssignmentOperation]):
47
52
  self._machine_precision,
48
53
  )
49
54
  result_symbol = self._interpreter.evaluate(op.result_var).as_type(QuantumSymbol)
55
+
56
+ validate_assignment_bool_expression(
57
+ result_symbol, op.expression.expr, op.operation_kind
58
+ ) # must be here, otherwise copy_type_information will throw a non-indicative error
50
59
  copy_type_information(
51
60
  result_type, result_symbol.quantum_type, str(op.result_var)
52
61
  )