classiq 0.54.0__py3-none-any.whl → 0.56.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 (56) hide show
  1. classiq/interface/_version.py +1 -1
  2. classiq/interface/debug_info/debug_info.py +11 -0
  3. classiq/interface/executor/result.py +0 -3
  4. classiq/interface/generator/functions/builtins/internal_operators.py +9 -1
  5. classiq/interface/generator/generated_circuit_data.py +0 -1
  6. classiq/interface/generator/model/preferences/preferences.py +4 -0
  7. classiq/interface/generator/types/compilation_metadata.py +5 -0
  8. classiq/interface/generator/visitor.py +13 -1
  9. classiq/interface/ide/visual_model.py +5 -1
  10. classiq/interface/interface_version.py +1 -1
  11. classiq/interface/model/control.py +22 -1
  12. classiq/interface/model/handle_binding.py +28 -0
  13. classiq/interface/model/model.py +4 -0
  14. classiq/interface/model/native_function_definition.py +1 -1
  15. classiq/interface/model/quantum_expressions/arithmetic_operation.py +2 -26
  16. classiq/interface/model/quantum_statement.py +6 -0
  17. classiq/model_expansions/capturing/mangling_utils.py +22 -0
  18. classiq/model_expansions/capturing/propagated_var_stack.py +36 -25
  19. classiq/model_expansions/closure.py +77 -12
  20. classiq/model_expansions/function_builder.py +9 -10
  21. classiq/model_expansions/generative_functions.py +2 -2
  22. classiq/model_expansions/interpreter.py +29 -26
  23. classiq/model_expansions/quantum_operations/control.py +114 -29
  24. classiq/model_expansions/quantum_operations/emitter.py +37 -11
  25. classiq/model_expansions/quantum_operations/expression_operation.py +80 -18
  26. classiq/model_expansions/quantum_operations/power.py +5 -0
  27. classiq/model_expansions/quantum_operations/quantum_assignment_operation.py +7 -37
  28. classiq/model_expansions/quantum_operations/quantum_function_call.py +2 -32
  29. classiq/model_expansions/quantum_operations/repeat.py +5 -0
  30. classiq/model_expansions/quantum_operations/within_apply.py +0 -16
  31. classiq/model_expansions/scope_initialization.py +2 -3
  32. classiq/qmod/builtins/functions/arithmetic.py +0 -2
  33. classiq/qmod/builtins/functions/discrete_sine_cosine_transform.py +0 -12
  34. classiq/qmod/builtins/functions/exponentiation.py +0 -6
  35. classiq/qmod/builtins/functions/grover.py +0 -17
  36. classiq/qmod/builtins/functions/linear_pauli_rotation.py +0 -5
  37. classiq/qmod/builtins/functions/modular_exponentiation.py +0 -3
  38. classiq/qmod/builtins/functions/qaoa_penalty.py +0 -8
  39. classiq/qmod/builtins/functions/qft_functions.py +0 -3
  40. classiq/qmod/builtins/functions/qpe.py +0 -6
  41. classiq/qmod/builtins/functions/qsvt.py +0 -12
  42. classiq/qmod/builtins/functions/standard_gates.py +0 -88
  43. classiq/qmod/builtins/functions/state_preparation.py +7 -15
  44. classiq/qmod/builtins/functions/swap_test.py +0 -3
  45. classiq/qmod/builtins/operations.py +152 -17
  46. classiq/qmod/create_model_function.py +10 -12
  47. classiq/qmod/model_state_container.py +5 -1
  48. classiq/qmod/native/pretty_printer.py +6 -1
  49. classiq/qmod/pretty_print/pretty_printer.py +25 -11
  50. classiq/qmod/qmod_constant.py +31 -3
  51. classiq/qmod/quantum_function.py +25 -19
  52. classiq/qmod/synthesize_separately.py +1 -2
  53. {classiq-0.54.0.dist-info → classiq-0.56.0.dist-info}/METADATA +2 -3
  54. {classiq-0.54.0.dist-info → classiq-0.56.0.dist-info}/RECORD +55 -55
  55. classiq/model_expansions/call_to_model_converter.py +0 -190
  56. {classiq-0.54.0.dist-info → classiq-0.56.0.dist-info}/WHEEL +0 -0
@@ -1,12 +1,16 @@
1
1
  import inspect
2
2
  import sys
3
+ import warnings
3
4
  from collections.abc import Mapping
4
5
  from types import FrameType
5
6
  from typing import (
7
+ TYPE_CHECKING,
6
8
  Any,
7
9
  Callable,
8
10
  Final,
11
+ Optional,
9
12
  Union,
13
+ overload,
10
14
  )
11
15
 
12
16
  from classiq.interface.exceptions import ClassiqValueError
@@ -21,13 +25,16 @@ from classiq.interface.model.classical_parameter_declaration import (
21
25
  ClassicalParameterDeclaration,
22
26
  )
23
27
  from classiq.interface.model.control import Control
24
- from classiq.interface.model.inplace_binary_operation import (
25
- BinaryOperation,
26
- InplaceBinaryOperation,
27
- )
28
28
  from classiq.interface.model.invert import Invert
29
29
  from classiq.interface.model.phase_operation import PhaseOperation
30
30
  from classiq.interface.model.power import Power
31
+ from classiq.interface.model.quantum_expressions.amplitude_loading_operation import (
32
+ AmplitudeLoadingOperation,
33
+ )
34
+ from classiq.interface.model.quantum_expressions.arithmetic_operation import (
35
+ ArithmeticOperation,
36
+ ArithmeticOperationKind,
37
+ )
31
38
  from classiq.interface.model.quantum_function_call import QuantumFunctionCall
32
39
  from classiq.interface.model.quantum_function_declaration import (
33
40
  QuantumOperandDeclaration,
@@ -37,7 +44,7 @@ from classiq.interface.model.repeat import Repeat
37
44
  from classiq.interface.model.statement_block import StatementBlock
38
45
  from classiq.interface.model.within_apply_operation import WithinApply
39
46
 
40
- from classiq.qmod.qmod_variable import Input, Output, QArray, QBit, QNum, QVar
47
+ from classiq.qmod.qmod_variable import Input, Output, QArray, QBit, QNum, QScalar, QVar
41
48
  from classiq.qmod.quantum_callable import QCallable
42
49
  from classiq.qmod.quantum_expandable import prepare_arg
43
50
  from classiq.qmod.symbolic_expr import SymbolicExpr
@@ -91,6 +98,7 @@ def if_(
91
98
  def control(
92
99
  ctrl: Union[SymbolicExpr, QBit, QArray[QBit]],
93
100
  stmt_block: Union[QCallable, Callable[[], None]],
101
+ else_block: Union[QCallable, Callable[[], None], None] = None,
94
102
  ) -> None:
95
103
  _validate_operand(stmt_block)
96
104
  assert QCallable.CURRENT_EXPANDABLE is not None
@@ -98,39 +106,164 @@ def control(
98
106
  control_stmt = Control(
99
107
  expression=Expression(expr=str(ctrl)),
100
108
  body=_operand_to_body(stmt_block, "stmt_block"),
109
+ else_block=_operand_to_body(else_block, "else_block") if else_block else None,
101
110
  source_ref=source_ref,
102
111
  )
103
112
  control_stmt.set_generative_block("body", stmt_block)
113
+ if else_block is not None:
114
+ control_stmt.set_generative_block("else_block", else_block)
104
115
  QCallable.CURRENT_EXPANDABLE.append_statement_to_body(control_stmt)
105
116
 
106
117
 
118
+ def assign(expression: SymbolicExpr, target_var: QScalar) -> None:
119
+ """
120
+ Initialize a scalar quantum variable using an arithmetic expression.
121
+ If specified, the variable numeric properties (size, signedness, and fraction
122
+ digits) must match the expression properties.
123
+
124
+ Equivalent to `<target_var> |= <expression>`.
125
+
126
+ Args:
127
+ expression: A classical or quantum arithmetic expression
128
+ target_var: An uninitialized scalar quantum variable
129
+ """
130
+ assert QCallable.CURRENT_EXPANDABLE is not None
131
+ source_ref = get_source_ref(sys._getframe(1))
132
+ QCallable.CURRENT_EXPANDABLE.append_statement_to_body(
133
+ ArithmeticOperation(
134
+ expression=Expression(expr=str(expression)),
135
+ result_var=target_var.get_handle_binding(),
136
+ operation_kind=ArithmeticOperationKind.Assignment,
137
+ source_ref=source_ref,
138
+ )
139
+ )
140
+
141
+
142
+ def assign_amplitude(expression: SymbolicExpr, target_var: QScalar) -> None:
143
+ """
144
+ Perform an amplitude-encoding assignment operation on a quantum variable and a
145
+ quantum expression.
146
+
147
+ Equivalent to `<target_var> *= <expression>`.
148
+
149
+ Args:
150
+ expression: A quantum arithmetic expression
151
+ target_var: A scalar quantum variable
152
+ """
153
+ assert QCallable.CURRENT_EXPANDABLE is not None
154
+ source_ref = get_source_ref(sys._getframe(1))
155
+ QCallable.CURRENT_EXPANDABLE.append_statement_to_body(
156
+ AmplitudeLoadingOperation(
157
+ expression=Expression(expr=str(expression)),
158
+ result_var=target_var.get_handle_binding(),
159
+ source_ref=source_ref,
160
+ )
161
+ )
162
+
163
+
164
+ @overload
165
+ def inplace_add(expression: SymbolicExpr, target_var: QScalar) -> None:
166
+ pass
167
+
168
+
169
+ @overload
170
+ def inplace_add(*, value: QNum, target: QNum) -> None:
171
+ pass
172
+
173
+
107
174
  def inplace_add(
108
- value: QNum,
109
- target: QNum,
175
+ expression: Optional[SymbolicExpr] = None,
176
+ target_var: Optional[QScalar] = None,
177
+ value: Optional[QNum] = None,
178
+ target: Optional[QNum] = None,
110
179
  ) -> None:
180
+ """
181
+ Add an arithmetic expression to a quantum variable.
182
+
183
+ Equivalent to `<target_var> += <expression>`.
184
+
185
+ Args:
186
+ expression: A classical or quantum arithmetic expression
187
+ target_var: A scalar quantum variable
188
+ """
111
189
  assert QCallable.CURRENT_EXPANDABLE is not None
190
+ if value is not None or target is not None:
191
+ warnings.warn(
192
+ "Parameters 'value' and 'target of function 'inplace_add' have "
193
+ "been renamed to 'expression' and 'target_var' respectively. Parameters "
194
+ "'value' and 'target' will no longer be supported starting on 02/12/24 at "
195
+ "the earliest.\nHint: Change `inplace_add(value=..., target=...)` to "
196
+ "`inplace_add(expression=..., target_var=...)` or `inplace_add(..., ...)`.",
197
+ category=DeprecationWarning,
198
+ stacklevel=2,
199
+ )
200
+ if value is not None:
201
+ expression = value
202
+ if target is not None:
203
+ target_var = target
204
+ if TYPE_CHECKING:
205
+ assert expression is not None
206
+ assert target_var is not None
112
207
  source_ref = get_source_ref(sys._getframe(1))
113
208
  QCallable.CURRENT_EXPANDABLE.append_statement_to_body(
114
- InplaceBinaryOperation(
115
- target=target.get_handle_binding(),
116
- value=value.get_handle_binding(),
117
- operation=BinaryOperation.Addition,
209
+ ArithmeticOperation(
210
+ expression=Expression(expr=str(expression)),
211
+ result_var=target_var.get_handle_binding(),
212
+ operation_kind=ArithmeticOperationKind.InplaceAdd,
118
213
  source_ref=source_ref,
119
214
  )
120
215
  )
121
216
 
122
217
 
218
+ @overload
219
+ def inplace_xor(expression: SymbolicExpr, target_var: QScalar) -> None:
220
+ pass
221
+
222
+
223
+ @overload
224
+ def inplace_xor(*, value: QNum, target: QNum) -> None:
225
+ pass
226
+
227
+
123
228
  def inplace_xor(
124
- value: QNum,
125
- target: QNum,
229
+ expression: Optional[SymbolicExpr] = None,
230
+ target_var: Optional[QScalar] = None,
231
+ value: Optional[QNum] = None,
232
+ target: Optional[QNum] = None,
126
233
  ) -> None:
234
+ """
235
+ Bitwise-XOR a quantum variable with an arithmetic expression.
236
+
237
+ Equivalent to `<target_var> ^= <expression>`.
238
+
239
+ Args:
240
+ expression: A classical or quantum arithmetic expression
241
+ target_var: A scalar quantum variable
242
+ """
127
243
  assert QCallable.CURRENT_EXPANDABLE is not None
244
+ if value is not None or target is not None:
245
+ warnings.warn(
246
+ "Parameters 'value' and 'target of function 'inplace_xor' have "
247
+ "been renamed to 'expression' and 'target_var' respectively. Parameters "
248
+ "'value' and 'target' will no longer be supported starting on 02/12/24 at "
249
+ "the earliest.\nHint: Change `inplace_xor(value=..., target=...)` to "
250
+ "`inplace_xor(expression=..., target_var=...)` or `inplace_xor(..., ...)`.",
251
+ category=DeprecationWarning,
252
+ stacklevel=2,
253
+ )
254
+ if value is not None:
255
+ expression = value
256
+ if target is not None:
257
+ target_var = target
258
+ if TYPE_CHECKING:
259
+ assert expression is not None
260
+ assert target_var is not None
128
261
  source_ref = get_source_ref(sys._getframe(1))
129
262
  QCallable.CURRENT_EXPANDABLE.append_statement_to_body(
130
- InplaceBinaryOperation(
131
- target=target.get_handle_binding(),
132
- value=value.get_handle_binding(),
133
- operation=BinaryOperation.Xor,
263
+ ArithmeticOperation(
264
+ expression=Expression(expr=str(expression)),
265
+ result_var=target_var.get_handle_binding(),
266
+ operation_kind=ArithmeticOperationKind.InplaceXor,
134
267
  source_ref=source_ref,
135
268
  )
136
269
  )
@@ -297,6 +430,8 @@ def _operand_to_body(
297
430
 
298
431
 
299
432
  __all__ = [
433
+ "assign",
434
+ "assign_amplitude",
300
435
  "bind",
301
436
  "control",
302
437
  "invert",
@@ -60,6 +60,7 @@ def create_model(
60
60
  user_gen_functions = {
61
61
  gen_func._py_callable.__name__ for gen_func in GEN_QFUNCS
62
62
  } - set(BUILTIN_FUNCTION_DECLARATIONS.keys())
63
+
63
64
  if len(user_gen_functions) > 0 and is_generative_expansion_enabled():
64
65
  model = _expand_generative_model(
65
66
  (
@@ -100,11 +101,17 @@ def _expand_generative_model(
100
101
  def _dummy() -> None:
101
102
  pass
102
103
 
104
+ functions_compilation_metadata = {
105
+ dec_func._py_callable.__name__: dec_func.compilation_metadata
106
+ for dec_func in DEC_QFUNCS
107
+ if dec_func.compilation_metadata is not None
108
+ }
103
109
  model = _dummy.create_model(
104
110
  constraints,
105
111
  execution_preferences,
106
112
  preferences,
107
113
  classical_execution_function,
114
+ functions_compilation_metadata,
108
115
  )
109
116
  generative_functions = _get_generative_functions(gen_main, preferences)
110
117
  model.functions = generative_functions
@@ -157,7 +164,8 @@ def _get_wrapper_main(gen_main: QFunc, preferences: Optional[Preferences]) -> Mo
157
164
  def _get_all_model_functions_as_generative_functions() -> list[GenerativeQFunc]:
158
165
 
159
166
  gen_functions = list(GEN_QFUNCS) + [
160
- _get_gen_from_dec(dec_func) for dec_func in DEC_QFUNCS
167
+ GenerativeQFunc(dec_func._py_callable, dec_func.func_decl)
168
+ for dec_func in DEC_QFUNCS
161
169
  ]
162
170
  return [
163
171
  (
@@ -173,16 +181,6 @@ def _get_all_model_functions_as_generative_functions() -> list[GenerativeQFunc]:
173
181
  ]
174
182
 
175
183
 
176
- def _get_gen_from_dec(dec_func: QFunc) -> GenerativeQFunc:
177
- synthesis_data = dec_func.synthesis_data
178
- if synthesis_data is not None and synthesis_data.should_synthesize_separately:
179
- raise ClassiqError(
180
- """The model contains generative functions,
181
- which cannot coexist with functions marked for separate synthesis"""
182
- )
183
- return GenerativeQFunc(dec_func._py_callable, dec_func.func_decl)
184
-
185
-
186
184
  def _interpret_generative_model(
187
185
  gen_model: Model, gen_functions: list[GenerativeQFunc]
188
186
  ) -> dict[str, NativeFunctionDefinition]:
@@ -192,7 +190,7 @@ def _interpret_generative_model(
192
190
  )
193
191
  interpreter = Interpreter(gen_model, gen_functions, is_frontend=True)
194
192
  set_frontend_interpreter(interpreter)
195
- expand_model, _ = interpreter.expand()
193
+ expand_model = interpreter.expand()
196
194
  functions_dict = nameables_to_dict(expand_model.functions)
197
195
 
198
196
  # Inline _gen_main call in main
@@ -1,8 +1,11 @@
1
1
  from classiq.interface.generator.constant import Constant
2
+ from classiq.interface.generator.types.compilation_metadata import CompilationMetadata
2
3
  from classiq.interface.generator.types.enum_declaration import EnumDeclaration
3
4
  from classiq.interface.generator.types.qstruct_declaration import QStructDeclaration
4
5
  from classiq.interface.generator.types.struct_declaration import StructDeclaration
5
- from classiq.interface.model.native_function_definition import NativeFunctionDefinition
6
+ from classiq.interface.model.native_function_definition import (
7
+ NativeFunctionDefinition,
8
+ )
6
9
 
7
10
 
8
11
  class ModelStateContainer:
@@ -11,6 +14,7 @@ class ModelStateContainer:
11
14
  qstruct_decls: dict[str, QStructDeclaration]
12
15
  native_defs: dict[str, NativeFunctionDefinition]
13
16
  constants: dict[str, Constant]
17
+ functions_compilation_metadata: dict[str, CompilationMetadata]
14
18
 
15
19
 
16
20
  QMODULE = ModelStateContainer()
@@ -253,7 +253,12 @@ class DSLPrettyPrinter(Visitor):
253
253
  def visit_Control(self, op: Control) -> str:
254
254
  control = f"{self._indent}control ({self.visit(op.expression)}) {{\n"
255
255
  control += self._visit_body(op.body)
256
- control += f"{self._indent}}}\n"
256
+ control += f"{self._indent}}}"
257
+ if op.else_block is not None:
258
+ control += " else {\n"
259
+ control += self._visit_body(op.else_block)
260
+ control += f"{self._indent}}}"
261
+ control += "\n"
257
262
  return control
258
263
 
259
264
  def visit_PhaseOperation(self, op: PhaseOperation) -> str:
@@ -50,9 +50,6 @@ from classiq.interface.model.quantum_expressions.arithmetic_operation import (
50
50
  ArithmeticOperation,
51
51
  ArithmeticOperationKind,
52
52
  )
53
- from classiq.interface.model.quantum_expressions.quantum_expression import (
54
- QuantumAssignmentOperation,
55
- )
56
53
  from classiq.interface.model.quantum_function_call import (
57
54
  OperandIdentifier,
58
55
  QuantumFunctionCall,
@@ -391,7 +388,10 @@ class PythonPrettyPrinter(Visitor):
391
388
 
392
389
  def visit_Control(self, op: Control) -> str:
393
390
  self._imports["control"] = 1
394
- return f"{self._indent}control({self.visit(op.expression)}, {self._visit_body(op.body)})\n"
391
+ control_else = (
392
+ f", {self._visit_body(op.else_block)}" if op.else_block is not None else ""
393
+ )
394
+ return f"{self._indent}control({self.visit(op.expression)}, {self._visit_body(op.body)}{control_else})\n"
395
395
 
396
396
  def visit_PhaseOperation(self, op: PhaseOperation) -> str:
397
397
  self._imports["phase"] = 1
@@ -429,13 +429,16 @@ class PythonPrettyPrinter(Visitor):
429
429
  code = f"lambda{argument_string}: {'[' if len(body) > 1 else ''}\n"
430
430
  self._level += 1
431
431
  for i, statement in enumerate(body):
432
- if isinstance(
433
- statement, (QuantumAssignmentOperation, VariableDeclarationStatement)
434
- ):
432
+ if isinstance(statement, VariableDeclarationStatement):
435
433
  raise AssertionError(
436
- "pretty printing quantum assignment operations or variable declaration statements in quantum lambda function is unsupported."
434
+ "pretty printing variable declaration statements in quantum lambda function is unsupported."
437
435
  )
438
- code += self.visit(statement)
436
+ if isinstance(statement, AmplitudeLoadingOperation):
437
+ code += self.visit_AmplitudeLoadingOperation(statement, in_lambda=True)
438
+ elif isinstance(statement, ArithmeticOperation):
439
+ code += self.visit_ArithmeticOperation(statement, in_lambda=True)
440
+ else:
441
+ code += self.visit(statement)
439
442
  if i < len(body) - 1:
440
443
  code += ","
441
444
  self._level -= 1
@@ -469,18 +472,29 @@ class PythonPrettyPrinter(Visitor):
469
472
  def visit_FieldHandleBinding(self, var_ref: FieldHandleBinding) -> str:
470
473
  return f"{self.visit(var_ref.base_handle)}.{self.visit(var_ref.field)}"
471
474
 
472
- def visit_ArithmeticOperation(self, arith_op: ArithmeticOperation) -> str:
475
+ def visit_ArithmeticOperation(
476
+ self, arith_op: ArithmeticOperation, in_lambda: bool = False
477
+ ) -> str:
473
478
  if arith_op.operation_kind == ArithmeticOperationKind.Assignment:
474
479
  op = "|="
480
+ func = "assign"
475
481
  elif arith_op.operation_kind == ArithmeticOperationKind.InplaceXor:
476
482
  op = "^="
483
+ func = "inplace_xor"
477
484
  else:
478
485
  op = "+="
486
+ func = "inplace_add"
487
+ if in_lambda:
488
+ self._imports[func] = 1
489
+ return f"{func}({self.visit(arith_op.expression)}, {self._indent}{self.visit(arith_op.result_var)})\n"
479
490
  return f"{self._indent}{self.visit(arith_op.result_var)} {op} {self.visit(arith_op.expression)}\n"
480
491
 
481
492
  def visit_AmplitudeLoadingOperation(
482
- self, amplitude_loading_op: AmplitudeLoadingOperation
493
+ self, amplitude_loading_op: AmplitudeLoadingOperation, in_lambda: bool = False
483
494
  ) -> str:
495
+ if in_lambda:
496
+ self._imports["assign_amplitude"] = 1
497
+ return f"assign_amplitude({self.visit(amplitude_loading_op.expression)}, {self._indent}{self.visit(amplitude_loading_op.result_var)})\n"
484
498
  return f"{self._indent}{self.visit(amplitude_loading_op.result_var)} *= {self.visit(amplitude_loading_op.expression)}\n"
485
499
 
486
500
  def _print_bind_handles(self, handles: list[HandleBinding]) -> str:
@@ -1,8 +1,8 @@
1
1
  import inspect
2
2
  from dataclasses import is_dataclass
3
- from typing import Any, Optional
3
+ from typing import Any, Optional, cast, get_origin
4
4
 
5
- from classiq.interface.exceptions import ClassiqError
5
+ from classiq.interface.exceptions import ClassiqError, ClassiqValueError
6
6
  from classiq.interface.generator.constant import Constant
7
7
  from classiq.interface.generator.expressions.expression import Expression
8
8
  from classiq.interface.generator.functions.classical_type import (
@@ -10,7 +10,13 @@ from classiq.interface.generator.functions.classical_type import (
10
10
  ClassicalList,
11
11
  )
12
12
 
13
+ from classiq.qmod.cparam import CArray, CParamScalar
13
14
  from classiq.qmod.declaration_inferrer import python_type_to_qmod
15
+ from classiq.qmod.generative import (
16
+ get_frontend_interpreter,
17
+ interpret_expression,
18
+ is_generative_mode,
19
+ )
14
20
  from classiq.qmod.model_state_container import ModelStateContainer
15
21
  from classiq.qmod.qmod_parameter import CParam, CParamList, CParamStruct
16
22
  from classiq.qmod.symbolic_expr import SymbolicExpr
@@ -49,7 +55,10 @@ class QConstant(SymbolicExpr):
49
55
  ):
50
56
  raise ClassiqError(f"Constant {self.name} is already defined in the model")
51
57
 
52
- QConstant.CURRENT_QMODULE.constants[self.name] = self._get_constant_node()
58
+ constant = self._get_constant_node()
59
+ QConstant.CURRENT_QMODULE.constants[self.name] = constant
60
+ if is_generative_mode():
61
+ get_frontend_interpreter().add_constant(constant)
53
62
 
54
63
  def _get_constant_node(self) -> Constant:
55
64
  if isinstance(self._value, QConstant):
@@ -113,3 +122,22 @@ class QConstant(SymbolicExpr):
113
122
  qmod_type,
114
123
  QConstant.CURRENT_QMODULE,
115
124
  )[item]
125
+
126
+ def __len__(self) -> int:
127
+ raise ClassiqValueError(
128
+ "len(<const>) is not supported for classical constants - use <const>.len "
129
+ "instead"
130
+ )
131
+
132
+ @property
133
+ def len(self) -> int:
134
+ self.add_to_model()
135
+ if get_origin(self._py_type) != CArray:
136
+ raise ClassiqValueError(
137
+ f"Constant {self.name!r} of type {self._py_type.__name__!r} does not "
138
+ f"have a 'len' property"
139
+ )
140
+ len_expr = f"get_field({self.name}, 'len')"
141
+ if is_generative_mode():
142
+ return interpret_expression(len_expr)
143
+ return cast(int, CParamScalar(len_expr))
@@ -9,9 +9,9 @@ from classiq.interface.exceptions import ClassiqError
9
9
  from classiq.interface.executor.execution_preferences import ExecutionPreferences
10
10
  from classiq.interface.generator.model.constraints import Constraints
11
11
  from classiq.interface.generator.model.preferences.preferences import Preferences
12
+ from classiq.interface.generator.types.compilation_metadata import CompilationMetadata
12
13
  from classiq.interface.model.model import Model
13
14
  from classiq.interface.model.native_function_definition import (
14
- FunctionSynthesisData,
15
15
  NativeFunctionDefinition,
16
16
  )
17
17
  from classiq.interface.model.quantum_function_declaration import (
@@ -35,7 +35,7 @@ class QFunc(QExpandable):
35
35
  _validate_no_gen_params(py_callable.__annotations__)
36
36
  super().__init__(py_callable)
37
37
  functools.update_wrapper(self, py_callable)
38
- self._synthesis_data: Optional[FunctionSynthesisData] = None
38
+ self.compilation_metadata: Optional[CompilationMetadata] = None
39
39
 
40
40
  @property
41
41
  def func_decl(self) -> NamedParamsQuantumFunctionDeclaration:
@@ -45,12 +45,19 @@ class QFunc(QExpandable):
45
45
  )
46
46
 
47
47
  @property
48
- def synthesis_data(self) -> Optional[FunctionSynthesisData]:
49
- return self._synthesis_data
50
-
51
- @synthesis_data.setter
52
- def synthesis_data(self, value: FunctionSynthesisData) -> None:
53
- self._synthesis_data = value
48
+ def should_synthesize_separately(self) -> bool:
49
+ if self.compilation_metadata is None:
50
+ return False
51
+ return self.compilation_metadata.should_synthesize_separately
52
+
53
+ @should_synthesize_separately.setter
54
+ def should_synthesize_separately(self, value: bool) -> None:
55
+ if self.compilation_metadata is None:
56
+ self.compilation_metadata = CompilationMetadata(
57
+ should_synthesize_separately=value
58
+ )
59
+ else:
60
+ self.compilation_metadata.should_synthesize_separately = value
54
61
 
55
62
  def __call__(self, *args: Any, **kwargs: Any) -> None:
56
63
  super().__call__(*args, **kwargs)
@@ -62,12 +69,16 @@ class QFunc(QExpandable):
62
69
  execution_preferences: Optional[ExecutionPreferences] = None,
63
70
  preferences: Optional[Preferences] = None,
64
71
  classical_execution_function: Optional[CFunc] = None,
72
+ functions_compilation_metadata: Optional[dict[str, CompilationMetadata]] = None,
65
73
  ) -> Model:
74
+ if functions_compilation_metadata is None:
75
+ functions_compilation_metadata = dict()
66
76
  self._qmodule.enum_decls = dict()
67
77
  self._qmodule.type_decls = dict()
68
78
  self._qmodule.qstruct_decls = dict()
69
79
  self._qmodule.native_defs = dict()
70
80
  self._qmodule.constants = dict()
81
+ self._qmodule.functions_compilation_metadata = functions_compilation_metadata
71
82
  QConstant.set_current_model(self._qmodule)
72
83
  self.expand()
73
84
  model_extra_settings: list[tuple[str, Any]] = [
@@ -86,6 +97,7 @@ class QFunc(QExpandable):
86
97
  enums=list(self._qmodule.enum_decls.values()),
87
98
  types=list(self._qmodule.type_decls.values()),
88
99
  qstructs=list(self._qmodule.qstruct_decls.values()),
100
+ functions_compilation_metadata=self._qmodule.functions_compilation_metadata,
89
101
  **{key: value for key, value in model_extra_settings if value},
90
102
  )
91
103
 
@@ -94,18 +106,12 @@ class QFunc(QExpandable):
94
106
  return
95
107
  super().expand()
96
108
  self._qmodule.native_defs[self.func_decl.name] = NativeFunctionDefinition(
97
- **{
98
- **self.func_decl.model_dump(),
99
- **{
100
- "body": self.body,
101
- "synthesis_data": (
102
- self.synthesis_data
103
- if self.synthesis_data is not None
104
- else FunctionSynthesisData()
105
- ),
106
- },
107
- },
109
+ **{**self.func_decl.model_dump(), **{"body": self.body}}
108
110
  )
111
+ if self.compilation_metadata is not None:
112
+ self._qmodule.functions_compilation_metadata[self.func_decl.name] = (
113
+ self.compilation_metadata
114
+ )
109
115
 
110
116
  def _add_constants_from_classical_code(
111
117
  self, classical_execution_function: CFunc
@@ -1,14 +1,13 @@
1
1
  from typing import Union
2
2
 
3
3
  from classiq.interface.exceptions import ClassiqError
4
- from classiq.interface.model.native_function_definition import FunctionSynthesisData
5
4
 
6
5
  from classiq.qmod.quantum_function import ExternalQFunc, GenerativeQFunc, QFunc
7
6
 
8
7
 
9
8
  def synthesize_separately(qfunc: Union[QFunc, GenerativeQFunc, ExternalQFunc]) -> QFunc:
10
9
  if isinstance(qfunc, QFunc):
11
- qfunc.synthesis_data = FunctionSynthesisData(should_synthesize_separately=True)
10
+ qfunc.should_synthesize_separately = True
12
11
  return qfunc
13
12
  if isinstance(qfunc, GenerativeQFunc):
14
13
  raise ClassiqError("Generative functions can not be synthesized separately")
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: classiq
3
- Version: 0.54.0
3
+ Version: 0.56.0
4
4
  Summary: Classiq's Python SDK for quantum computing
5
5
  Home-page: https://classiq.io
6
6
  License: Proprietary
7
7
  Keywords: quantum computing,quantum circuits,quantum algorithms,QAD,QDL
8
8
  Author: Classiq Technologies
9
9
  Author-email: support@classiq.io
10
- Requires-Python: >=3.8,<3.13
10
+ Requires-Python: >=3.9,<3.13
11
11
  Classifier: Development Status :: 4 - Beta
12
12
  Classifier: Intended Audience :: Developers
13
13
  Classifier: Intended Audience :: Education
@@ -16,7 +16,6 @@ Classifier: License :: Other/Proprietary License
16
16
  Classifier: Natural Language :: English
17
17
  Classifier: Operating System :: OS Independent
18
18
  Classifier: Programming Language :: Python :: 3
19
- Classifier: Programming Language :: Python :: 3.8
20
19
  Classifier: Programming Language :: Python :: 3.9
21
20
  Classifier: Programming Language :: Python :: 3.10
22
21
  Classifier: Programming Language :: Python :: 3.11