classiq 0.68.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 (96) hide show
  1. classiq/_internals/api_wrapper.py +4 -8
  2. classiq/analyzer/analyzer.py +0 -18
  3. classiq/analyzer/url_utils.py +9 -4
  4. classiq/applications/combinatorial_helpers/pyomo_utils.py +2 -0
  5. classiq/applications/combinatorial_optimization/combinatorial_problem.py +8 -11
  6. classiq/applications/qnn/gradients/quantum_gradient.py +1 -1
  7. classiq/applications/qnn/gradients/simple_quantum_gradient.py +1 -1
  8. classiq/applications/qnn/torch_utils.py +1 -1
  9. classiq/execution/jobs.py +2 -5
  10. classiq/interface/_version.py +1 -1
  11. classiq/interface/backend/quantum_backend_providers.py +8 -3
  12. classiq/interface/chemistry/operator.py +12 -28
  13. classiq/interface/debug_info/back_ref_util.py +22 -0
  14. classiq/interface/debug_info/debug_info.py +11 -21
  15. classiq/interface/executor/optimizer_preferences.py +1 -0
  16. classiq/interface/executor/quantum_instruction_set.py +1 -0
  17. classiq/interface/generator/arith/arithmetic.py +21 -6
  18. classiq/interface/generator/arith/arithmetic_param_getters.py +3 -3
  19. classiq/interface/generator/circuit_code/circuit_code.py +4 -0
  20. classiq/interface/generator/circuit_code/types_and_constants.py +1 -0
  21. classiq/interface/generator/expressions/atomic_expression_functions.py +1 -2
  22. classiq/interface/generator/expressions/expression_types.py +8 -2
  23. classiq/interface/generator/expressions/proxies/__init__.py +0 -0
  24. classiq/interface/generator/expressions/proxies/classical/__init__.py +0 -0
  25. classiq/interface/generator/expressions/proxies/classical/classical_array_proxy.py +75 -0
  26. classiq/interface/generator/expressions/proxies/classical/classical_proxy.py +26 -0
  27. classiq/interface/generator/expressions/proxies/classical/classical_scalar_proxy.py +32 -0
  28. classiq/interface/generator/expressions/proxies/classical/classical_struct_proxy.py +31 -0
  29. classiq/interface/generator/expressions/proxies/quantum/__init__.py +0 -0
  30. classiq/interface/generator/expressions/{qmod_qarray_proxy.py → proxies/quantum/qmod_qarray_proxy.py} +3 -1
  31. classiq/interface/generator/expressions/{qmod_qscalar_proxy.py → proxies/quantum/qmod_qscalar_proxy.py} +3 -1
  32. classiq/interface/generator/expressions/{qmod_qstruct_proxy.py → proxies/quantum/qmod_qstruct_proxy.py} +3 -1
  33. classiq/interface/generator/functions/classical_type.py +34 -29
  34. classiq/interface/generator/functions/type_name.py +26 -2
  35. classiq/interface/generator/generated_circuit_data.py +84 -27
  36. classiq/interface/generator/model/preferences/preferences.py +1 -0
  37. classiq/interface/generator/quantum_program.py +0 -1
  38. classiq/interface/generator/types/builtin_enum_declarations.py +1 -0
  39. classiq/interface/generator/types/enum_declaration.py +12 -1
  40. classiq/interface/ide/visual_model.py +0 -2
  41. classiq/interface/model/native_function_definition.py +0 -10
  42. classiq/interface/model/quantum_statement.py +1 -1
  43. classiq/interface/model/quantum_type.py +15 -3
  44. classiq/interface/server/routes.py +0 -6
  45. classiq/model_expansions/atomic_expression_functions_defs.py +9 -3
  46. classiq/model_expansions/evaluators/arg_type_match.py +4 -2
  47. classiq/model_expansions/evaluators/classical_expression.py +2 -2
  48. classiq/model_expansions/evaluators/control.py +1 -1
  49. classiq/model_expansions/evaluators/parameter_types.py +58 -16
  50. classiq/model_expansions/evaluators/quantum_type_utils.py +7 -57
  51. classiq/model_expansions/expression_evaluator.py +3 -1
  52. classiq/model_expansions/generative_functions.py +67 -7
  53. classiq/model_expansions/quantum_operations/arithmetic/__init__.py +0 -0
  54. classiq/model_expansions/quantum_operations/arithmetic/explicit_boolean_expressions.py +60 -0
  55. classiq/model_expansions/quantum_operations/assignment_result_processor.py +9 -0
  56. classiq/model_expansions/quantum_operations/call_emitter.py +0 -13
  57. classiq/model_expansions/quantum_operations/quantum_function_call.py +0 -22
  58. classiq/model_expansions/scope.py +7 -6
  59. classiq/model_expansions/scope_initialization.py +20 -33
  60. classiq/model_expansions/transformers/model_renamer.py +13 -4
  61. classiq/model_expansions/visitors/variable_references.py +8 -4
  62. classiq/open_library/functions/__init__.py +2 -0
  63. classiq/open_library/functions/amplitude_amplification.py +3 -7
  64. classiq/open_library/functions/discrete_sine_cosine_transform.py +4 -4
  65. classiq/open_library/functions/grover.py +2 -2
  66. classiq/open_library/functions/hea.py +3 -3
  67. classiq/open_library/functions/lookup_table.py +58 -0
  68. classiq/open_library/functions/modular_exponentiation.py +10 -20
  69. classiq/open_library/functions/qft_functions.py +2 -2
  70. classiq/open_library/functions/qsvt.py +8 -8
  71. classiq/open_library/functions/utility_functions.py +2 -2
  72. classiq/qmod/builtins/classical_functions.py +24 -7
  73. classiq/qmod/builtins/enums.py +1 -0
  74. classiq/qmod/builtins/functions/__init__.py +2 -0
  75. classiq/qmod/builtins/functions/exponentiation.py +24 -0
  76. classiq/qmod/builtins/operations.py +26 -11
  77. classiq/qmod/cparam.py +32 -5
  78. classiq/qmod/declaration_inferrer.py +3 -1
  79. classiq/qmod/python_classical_type.py +10 -4
  80. classiq/qmod/qmod_parameter.py +8 -0
  81. classiq/qmod/qmod_variable.py +11 -14
  82. classiq/qmod/quantum_callable.py +2 -1
  83. classiq/qmod/quantum_function.py +3 -2
  84. classiq/qmod/semantics/annotation/call_annotation.py +0 -28
  85. classiq/qmod/semantics/annotation/qstruct_annotator.py +21 -1
  86. classiq/qmod/semantics/error_manager.py +1 -1
  87. classiq/qmod/semantics/validation/main_validation.py +1 -1
  88. classiq/qmod/semantics/validation/type_hints.py +29 -0
  89. classiq/qmod/utilities.py +67 -2
  90. classiq/synthesis.py +9 -6
  91. {classiq-0.68.0.dist-info → classiq-0.70.0.dist-info}/METADATA +10 -12
  92. {classiq-0.68.0.dist-info → classiq-0.70.0.dist-info}/RECORD +95 -84
  93. {classiq-0.68.0.dist-info → classiq-0.70.0.dist-info}/WHEEL +1 -1
  94. classiq/interface/execution/jobs.py +0 -31
  95. /classiq/interface/generator/expressions/{qmod_struct_instance.py → proxies/classical/qmod_struct_instance.py} +0 -0
  96. /classiq/interface/generator/expressions/{qmod_sized_proxy.py → proxies/quantum/qmod_sized_proxy.py} +0 -0
@@ -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()
@@ -7,9 +7,6 @@ PROVIDERS_PREFIX = "/providers"
7
7
 
8
8
  IQCC_PREFIX = PROVIDERS_PREFIX + "/iqcc"
9
9
 
10
- EXECUTION_NON_VERSIONED_PREFIX = "/execution/v1"
11
- SYNTHESIS_NON_VERSIONED_PREFIX = "/synthesis/v1"
12
-
13
10
  ANALYZER_CIRCUIT_PAGE = "circuit"
14
11
  DEFAULT_IDE_FE_APP = "https://platform.classiq.io/"
15
12
 
@@ -58,9 +55,6 @@ TASKS_GENERATE_FULL_PATH = TASKS_GENERATE_SUFFIX
58
55
 
59
56
  EXECUTION_JOBS_SUFFIX = "/jobs"
60
57
  EXECUTION_JOBS_FULL_PATH = EXECUTION_PREFIX + EXECUTION_JOBS_SUFFIX
61
- EXECUTION_JOBS_NON_VERSIONED_FULL_PATH = (
62
- EXECUTION_NON_VERSIONED_PREFIX + EXECUTION_JOBS_SUFFIX
63
- )
64
58
 
65
59
  ANALYZER_FULL_PATH = ANALYZER_PREFIX + TASKS_SUFFIX
66
60
  ANALYZER_RB_FULL_PATH = ANALYZER_PREFIX + TASK_RB_SUFFIX
@@ -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
 
@@ -148,7 +207,8 @@ def emit_generative_statements(
148
207
  with _InterpreterExpandable(interpreter):
149
208
  set_frontend_interpreter(interpreter)
150
209
  for block_name, generative_function in operation.generative_blocks.items():
151
- with interpreter._builder.block_context(
152
- block_name
153
- ), generative_mode_context(True):
210
+ with (
211
+ interpreter._builder.block_context(block_name),
212
+ generative_mode_context(True),
213
+ ):
154
214
  generative_function._py_callable(*python_qmod_args)
@@ -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
  )
@@ -13,9 +13,6 @@ from classiq.interface.generator.functions.port_declaration import (
13
13
  PortDeclarationDirection,
14
14
  )
15
15
  from classiq.interface.generator.generated_circuit_data import OperationLevel
16
- from classiq.interface.model.classical_parameter_declaration import (
17
- ClassicalParameterDeclaration,
18
- )
19
16
  from classiq.interface.model.handle_binding import HandleBinding
20
17
  from classiq.interface.model.native_function_definition import NativeFunctionDefinition
21
18
  from classiq.interface.model.port_declaration import PortDeclaration
@@ -162,11 +159,6 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
162
159
  back_ref=self._get_back_ref(propagated_debug_info),
163
160
  )
164
161
  is_allocate_or_free = new_call.func_name == free.func_decl.name
165
- parameters = {
166
- arg_decl.name: FunctionDebugInfo.param_controller(value=evaluated_arg.value)
167
- for arg_decl, evaluated_arg in zip(new_positional_arg_decls, evaluated_args)
168
- if isinstance(arg_decl, ClassicalParameterDeclaration)
169
- }
170
162
 
171
163
  port_to_passed_variable_map = {
172
164
  arg_decl.name: str(evaluated_arg.value.handle)
@@ -176,11 +168,6 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
176
168
  self._debug_info[new_call.uuid] = FunctionDebugInfo(
177
169
  name=new_call.func_name,
178
170
  level=OperationLevel.QMOD_FUNCTION_CALL,
179
- parameters=(
180
- parameters
181
- if propagated_debug_info is None or propagated_debug_info.name == ""
182
- else propagated_debug_info.parameters
183
- ),
184
171
  is_allocate_or_free=is_allocate_or_free,
185
172
  port_to_passed_variable_map=port_to_passed_variable_map,
186
173
  node=new_call._as_back_ref(),
@@ -1,9 +1,5 @@
1
1
  from typing import TYPE_CHECKING
2
2
 
3
- from classiq.interface.exceptions import ClassiqValueError
4
- from classiq.interface.generator.expressions.expression import Expression
5
- from classiq.interface.model.allocate import Allocate
6
- from classiq.interface.model.handle_binding import HandleBinding
7
3
  from classiq.interface.model.quantum_function_call import QuantumFunctionCall
8
4
 
9
5
  from classiq.model_expansions.closure import FunctionClosure
@@ -17,21 +13,12 @@ if TYPE_CHECKING:
17
13
  from classiq.model_expansions.interpreters.base_interpreter import BaseInterpreter
18
14
 
19
15
 
20
- ALLOCATE_COMPATIBILITY_ERROR_MESSAGE = (
21
- "'allocate' expects two argument: The number of qubits to allocate (integer) and "
22
- "the variable to be allocated (quantum variable)"
23
- )
24
-
25
-
26
16
  class QuantumFunctionCallEmitter(CallEmitter[QuantumFunctionCall]):
27
17
  def __init__(self, interpreter: "BaseInterpreter") -> None:
28
18
  super().__init__(interpreter)
29
19
  self._model = self._interpreter._model
30
20
 
31
21
  def emit(self, call: QuantumFunctionCall, /) -> bool:
32
- if call.function == "allocate": # FIXME: Remove compatibility (CAD-25935)
33
- self._allocate_compatibility(call)
34
- return True
35
22
  function = self._interpreter.evaluate(call.function).as_type(FunctionClosure)
36
23
  args = call.positional_args
37
24
  with ErrorManager().call(function.name):
@@ -40,15 +27,6 @@ class QuantumFunctionCallEmitter(CallEmitter[QuantumFunctionCall]):
40
27
  )
41
28
  return True
42
29
 
43
- def _allocate_compatibility(self, call: QuantumFunctionCall) -> None:
44
- if len(call.positional_args) != 2:
45
- raise ClassiqValueError(ALLOCATE_COMPATIBILITY_ERROR_MESSAGE)
46
- size, target = call.positional_args
47
- if not isinstance(size, Expression) or not isinstance(target, HandleBinding):
48
- raise ClassiqValueError(ALLOCATE_COMPATIBILITY_ERROR_MESSAGE)
49
- allocate = Allocate(size=size, target=target, source_ref=call.source_ref)
50
- self._interpreter.emit_statement(allocate)
51
-
52
30
 
53
31
  class DeclarativeQuantumFunctionCallEmitter(
54
32
  QuantumFunctionCallEmitter, DeclarativeCallEmitter
@@ -19,7 +19,7 @@ from classiq.interface.generator.expressions.expression import Expression
19
19
  from classiq.interface.generator.expressions.expression_constants import (
20
20
  CPARAM_EXECUTION_SUFFIX_PATTERN,
21
21
  )
22
- from classiq.interface.generator.expressions.qmod_struct_instance import (
22
+ from classiq.interface.generator.expressions.proxies.classical.qmod_struct_instance import (
23
23
  QmodStructInstance,
24
24
  )
25
25
  from classiq.interface.generator.functions.type_name import TypeName
@@ -72,9 +72,9 @@ class QuantumSymbol:
72
72
  array_length = self.quantum_type.length_value
73
73
  if start < 0 or end > array_length:
74
74
  raise ClassiqExpansionError(
75
- f"Slice [{start}:{end}] is out of bounds of "
76
- f"{self.quantum_type.type_name} {str(self.handle)!r} of length "
77
- f"{array_length}"
75
+ f"Slice [{start}:{end}] is out of bounds for "
76
+ f"{self.quantum_type.type_name.lower()} {str(self.handle)!r} (of "
77
+ f"length {array_length})"
78
78
  )
79
79
  return QuantumSymbol(
80
80
  handle=SlicedHandleBinding(
@@ -96,8 +96,9 @@ class QuantumSymbol:
96
96
  array_length = self.quantum_type.length_value
97
97
  if index < 0 or index >= array_length:
98
98
  raise ClassiqExpansionError(
99
- f"Subscript {index} is out of bounds of {self.quantum_type.type_name} "
100
- f"{str(self.handle)!r} of length {array_length}"
99
+ f"Index {index} is out of bounds for "
100
+ f"{self.quantum_type.type_name.lower()} {str(self.handle)!r} (of "
101
+ f"length {array_length})"
101
102
  )
102
103
  return QuantumSymbol(
103
104
  handle=SubscriptHandleBinding(