classiq 0.76.0__py3-none-any.whl → 0.77.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 (44) hide show
  1. classiq/applications/chemistry/chemistry_model_constructor.py +7 -6
  2. classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +9 -1
  3. classiq/applications/combinatorial_optimization/combinatorial_problem.py +4 -3
  4. classiq/interface/_version.py +1 -1
  5. classiq/interface/generator/expressions/proxies/quantum/qmod_qarray_proxy.py +6 -15
  6. classiq/interface/generator/expressions/proxies/quantum/qmod_qscalar_proxy.py +14 -5
  7. classiq/interface/generator/expressions/proxies/quantum/qmod_sized_proxy.py +5 -3
  8. classiq/interface/model/handle_binding.py +8 -0
  9. classiq/interface/model/model.py +3 -6
  10. classiq/interface/model/quantum_function_call.py +31 -1
  11. classiq/interface/model/quantum_statement.py +14 -1
  12. classiq/interface/source_reference.py +7 -2
  13. classiq/model_expansions/capturing/captured_vars.py +16 -6
  14. classiq/model_expansions/evaluators/arg_type_match.py +2 -2
  15. classiq/model_expansions/evaluators/argument_types.py +3 -3
  16. classiq/model_expansions/evaluators/classical_expression.py +9 -9
  17. classiq/model_expansions/evaluators/parameter_types.py +19 -11
  18. classiq/model_expansions/expression_evaluator.py +20 -11
  19. classiq/model_expansions/function_builder.py +45 -0
  20. classiq/model_expansions/generative_functions.py +1 -1
  21. classiq/model_expansions/interpreters/base_interpreter.py +15 -1
  22. classiq/model_expansions/interpreters/frontend_generative_interpreter.py +4 -3
  23. classiq/model_expansions/interpreters/generative_interpreter.py +4 -4
  24. classiq/model_expansions/quantum_operations/allocate.py +2 -2
  25. classiq/model_expansions/quantum_operations/assignment_result_processor.py +3 -1
  26. classiq/model_expansions/quantum_operations/call_emitter.py +21 -11
  27. classiq/model_expansions/quantum_operations/emitter.py +1 -6
  28. classiq/model_expansions/scope.py +53 -3
  29. classiq/model_expansions/transformers/model_renamer.py +2 -2
  30. classiq/model_expansions/visitors/symbolic_param_inference.py +3 -3
  31. classiq/open_library/functions/lookup_table.py +1 -1
  32. classiq/open_library/functions/state_preparation.py +1 -1
  33. classiq/qmod/create_model_function.py +21 -3
  34. classiq/qmod/global_declarative_switch.py +19 -0
  35. classiq/qmod/native/pretty_printer.py +4 -0
  36. classiq/qmod/pretty_print/pretty_printer.py +4 -0
  37. classiq/qmod/qfunc.py +31 -23
  38. classiq/qmod/quantum_expandable.py +29 -1
  39. classiq/qmod/quantum_function.py +26 -19
  40. classiq/qmod/write_qmod.py +36 -10
  41. classiq/synthesis.py +7 -6
  42. {classiq-0.76.0.dist-info → classiq-0.77.0.dist-info}/METADATA +1 -1
  43. {classiq-0.76.0.dist-info → classiq-0.77.0.dist-info}/RECORD +44 -43
  44. {classiq-0.76.0.dist-info → classiq-0.77.0.dist-info}/WHEEL +0 -0
@@ -11,12 +11,15 @@ from classiq.interface.generator.compiler_keywords import (
11
11
  from classiq.interface.generator.functions.builtins.internal_operators import (
12
12
  WITHIN_APPLY_NAME,
13
13
  )
14
+ from classiq.interface.generator.functions.type_qualifier import TypeQualifier
14
15
  from classiq.interface.model.model import MAIN_FUNCTION_NAME
15
16
  from classiq.interface.model.native_function_definition import (
16
17
  NativeFunctionDefinition,
17
18
  )
19
+ from classiq.interface.model.port_declaration import PortDeclaration
18
20
  from classiq.interface.model.quantum_function_declaration import (
19
21
  PositionalArg,
22
+ QuantumOperandDeclaration,
20
23
  )
21
24
  from classiq.interface.model.quantum_statement import QuantumStatement
22
25
  from classiq.interface.model.variable_declaration_statement import (
@@ -212,6 +215,7 @@ class OperationBuilder:
212
215
  self, function_context: FunctionContext, params: Sequence[PositionalArg]
213
216
  ) -> NativeFunctionDefinition:
214
217
  name = self._get_expanded_function_name(function_context)
218
+ self._override_type_qualifier(function_context, params)
215
219
 
216
220
  return NativeFunctionDefinition(
217
221
  name=name,
@@ -244,3 +248,44 @@ class OperationBuilder:
244
248
  raise ClassiqInternalExpansionError("Could not allocate function name")
245
249
 
246
250
  return name
251
+
252
+ def _override_type_qualifier(
253
+ self, function_context: FunctionContext, params: Sequence[PositionalArg]
254
+ ) -> None:
255
+ """
256
+ The type qualifier can be changed according to the operand passed to the
257
+ function. For example,
258
+ apply_to_all(X, q) --> q will be QFree after expansion
259
+ apply_to_all(H, q) --> q will be Quantum after expansion
260
+ This also holds for the intermediate lambda created during the expansion.
261
+
262
+ We don't override the type qualifier if it's explicitly specified (QFree or
263
+ Const), neither in the function declaration nor in the operand declaration.
264
+ """
265
+
266
+ if function_context.is_lambda:
267
+ self._update_type_qualifiers(params)
268
+ return
269
+
270
+ orig_name = function_context.name
271
+ if orig_name == MAIN_FUNCTION_NAME or orig_name not in self.current_scope:
272
+ return
273
+
274
+ orig_func = self.current_scope[orig_name].value
275
+ if not any(
276
+ isinstance(param_decl, QuantumOperandDeclaration)
277
+ for param_decl in orig_func.positional_arg_declarations
278
+ ):
279
+ return
280
+
281
+ self._update_type_qualifiers(params)
282
+
283
+ @staticmethod
284
+ def _update_type_qualifiers(params: Sequence[PositionalArg]) -> None:
285
+ # only override the qualifier if it's unspecified (not QFree or Const)
286
+ for param in params:
287
+ if (
288
+ isinstance(param, PortDeclaration)
289
+ and param.type_qualifier is TypeQualifier.Quantum
290
+ ):
291
+ param.type_qualifier = TypeQualifier.Inferred
@@ -134,7 +134,7 @@ class _InterpreterExpandable(QFunc):
134
134
  dummy_function = NativeFunctionDefinition(
135
135
  name=current_operation.name,
136
136
  positional_arg_declarations=current_operation.positional_arg_declarations,
137
- body=self._interpreter._builder._current_statements + [stmt],
137
+ body=[stmt],
138
138
  )
139
139
  declarative_functions = {
140
140
  name: func
@@ -24,6 +24,7 @@ from classiq.interface.generator.types.compilation_metadata import CompilationMe
24
24
  from classiq.interface.model.handle_binding import (
25
25
  FieldHandleBinding,
26
26
  HandleBinding,
27
+ HandlesList,
27
28
  SlicedHandleBinding,
28
29
  SubscriptHandleBinding,
29
30
  )
@@ -51,7 +52,12 @@ from classiq.model_expansions.function_builder import (
51
52
  OperationBuilder,
52
53
  OperationContext,
53
54
  )
54
- from classiq.model_expansions.scope import Evaluated, QuantumSymbol, Scope
55
+ from classiq.model_expansions.scope import (
56
+ Evaluated,
57
+ QuantumSymbol,
58
+ QuantumSymbolList,
59
+ Scope,
60
+ )
55
61
  from classiq.model_expansions.scope_initialization import (
56
62
  add_entry_point_params_to_scope,
57
63
  init_builtin_types,
@@ -233,6 +239,14 @@ class BaseInterpreter:
233
239
  )
234
240
  return Evaluated(value=fields[field_name])
235
241
 
242
+ @evaluate.register
243
+ def evaluate_handles_list(self, handles_list: HandlesList) -> Evaluated:
244
+ return Evaluated(
245
+ value=QuantumSymbolList.from_symbols(
246
+ [self.evaluate(handle).value for handle in handles_list.handles]
247
+ )
248
+ )
249
+
236
250
  @abstractmethod
237
251
  def emit(self, statement: QuantumStatement) -> None:
238
252
  pass
@@ -1,5 +1,6 @@
1
1
  import inspect
2
2
  import os
3
+ from typing import Optional
3
4
 
4
5
  from pydantic import ValidationError
5
6
 
@@ -33,9 +34,9 @@ class FrontendGenerativeInterpreter(GenerativeInterpreter):
33
34
  def infer_symbolic_parameters(
34
35
  self,
35
36
  functions: list[NativeFunctionDefinition],
36
- additional_signatures: (
37
- list[NamedParamsQuantumFunctionDeclaration] | None
38
- ) = None,
37
+ additional_signatures: Optional[
38
+ list[NamedParamsQuantumFunctionDeclaration]
39
+ ] = None,
39
40
  ) -> None:
40
41
  SymbolicParamInference(functions, additional_signatures).infer()
41
42
 
@@ -1,5 +1,5 @@
1
1
  from functools import singledispatchmethod
2
- from typing import Any
2
+ from typing import Any, Optional
3
3
 
4
4
  import numpy as np
5
5
  from numpy.random import permutation
@@ -104,9 +104,9 @@ class GenerativeInterpreter(BaseInterpreter):
104
104
  def infer_symbolic_parameters(
105
105
  self,
106
106
  functions: list[NativeFunctionDefinition],
107
- additional_signatures: (
108
- list[NamedParamsQuantumFunctionDeclaration] | None
109
- ) = None,
107
+ additional_signatures: Optional[
108
+ list[NamedParamsQuantumFunctionDeclaration]
109
+ ] = None,
110
110
  ) -> None:
111
111
  pass
112
112
 
@@ -1,4 +1,4 @@
1
- from typing import TYPE_CHECKING
1
+ from typing import TYPE_CHECKING, Optional
2
2
 
3
3
  import sympy
4
4
 
@@ -49,7 +49,7 @@ class AllocateEmitter(Emitter[Allocate]):
49
49
  self.emit_statement(allocate)
50
50
  return True
51
51
 
52
- def _get_var_size(self, target: QuantumSymbol, size: Expression | None) -> str:
52
+ def _get_var_size(self, target: QuantumSymbol, size: Optional[Expression]) -> str:
53
53
  if size is None:
54
54
  if not target.quantum_type.is_evaluated:
55
55
  raise ClassiqValueError(
@@ -1,3 +1,5 @@
1
+ from typing import Optional
2
+
1
3
  from classiq.interface.exceptions import ClassiqExpansionError
2
4
  from classiq.interface.generator.arith.arithmetic import compute_arithmetic_result_type
3
5
  from classiq.interface.generator.expressions.expression import Expression
@@ -69,7 +71,7 @@ class AssignmentResultProcessor(Emitter[QuantumAssignmentOperation]):
69
71
  self._assign_to_inferred_var_and_bind(op, result_type, inferred_result_type)
70
72
  return True
71
73
 
72
- def _infer_result_type(self, op: ArithmeticOperation) -> QuantumNumeric | None:
74
+ def _infer_result_type(self, op: ArithmeticOperation) -> Optional[QuantumNumeric]:
73
75
  expr = self._evaluate_expression(op.expression)
74
76
  if len(self._get_classical_vars_in_expression(expr)):
75
77
  return None
@@ -4,6 +4,7 @@ from typing import (
4
4
  TYPE_CHECKING,
5
5
  Any,
6
6
  Generic,
7
+ Optional,
7
8
  cast,
8
9
  )
9
10
  from uuid import UUID
@@ -62,7 +63,13 @@ from classiq.model_expansions.quantum_operations.emitter import (
62
63
  Emitter,
63
64
  QuantumStatementT,
64
65
  )
65
- from classiq.model_expansions.scope import Evaluated, QuantumSymbol, Scope
66
+ from classiq.model_expansions.scope import (
67
+ Evaluated,
68
+ QuantumSymbol,
69
+ QuantumSymbolList,
70
+ QuantumVariable,
71
+ Scope,
72
+ )
66
73
  from classiq.model_expansions.transformers.var_splitter import VarSplitter
67
74
  from classiq.model_expansions.utils.text_utils import are, readable_list, s
68
75
  from classiq.qmod.semantics.validation.signature_validation import (
@@ -74,11 +81,14 @@ if TYPE_CHECKING:
74
81
 
75
82
 
76
83
  def _validate_cloning(evaluated_args: list[Evaluated]) -> None:
77
- handles = [
78
- arg.value.handle
84
+ handles = chain.from_iterable(
85
+ (
86
+ [arg.value.handle]
87
+ if isinstance(arg.value, QuantumSymbol)
88
+ else arg.value.handles if isinstance(arg.value, QuantumSymbolList) else []
89
+ )
79
90
  for arg in evaluated_args
80
- if isinstance(arg.value, QuantumSymbol)
81
- ]
91
+ )
82
92
  for handle, other_handle in combinations(handles, 2):
83
93
  if handle.overlaps(other_handle):
84
94
  if handle == other_handle:
@@ -130,7 +140,7 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
130
140
  self,
131
141
  function: FunctionClosure,
132
142
  args: list[ArgValue],
133
- propagated_debug_info: FunctionDebugInfo | None,
143
+ propagated_debug_info: Optional[FunctionDebugInfo],
134
144
  ) -> QuantumFunctionCall:
135
145
  call = self._create_quantum_function_call(
136
146
  function, args, propagated_debug_info=propagated_debug_info
@@ -140,8 +150,8 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
140
150
 
141
151
  @staticmethod
142
152
  def _get_back_ref(
143
- propagated_debug_info: FunctionDebugInfo | None,
144
- ) -> UUID | None:
153
+ propagated_debug_info: Optional[FunctionDebugInfo],
154
+ ) -> Optional[UUID]:
145
155
  if propagated_debug_info is None:
146
156
  return None
147
157
  if propagated_debug_info.node is None:
@@ -152,7 +162,7 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
152
162
  self,
153
163
  function: FunctionClosure,
154
164
  args: list[ArgValue],
155
- propagated_debug_info: FunctionDebugInfo | None,
165
+ propagated_debug_info: Optional[FunctionDebugInfo],
156
166
  ) -> QuantumFunctionCall:
157
167
  function = function.clone()
158
168
  function = function.set_depth(self._builder.current_function.depth + 1)
@@ -281,7 +291,7 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
281
291
  ) -> None:
282
292
  for parameter, argument in zip(parameters, arguments):
283
293
  param_handle = HandleBinding(name=parameter.name)
284
- if isinstance(argument.value, QuantumSymbol):
294
+ if isinstance(argument.value, QuantumVariable):
285
295
  assert isinstance(parameter, PortDeclaration)
286
296
  closure.scope[parameter.name] = Evaluated(
287
297
  QuantumSymbol(
@@ -314,7 +324,7 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
314
324
  positional_args = [
315
325
  arg.emit()
316
326
  for arg in evaluated_args
317
- if isinstance(arg.value, QuantumSymbol) or _is_symbolic(arg.value)
327
+ if isinstance(arg.value, QuantumVariable) or _is_symbolic(arg.value)
318
328
  ]
319
329
 
320
330
  return positional_args
@@ -181,12 +181,7 @@ class Emitter(Generic[QuantumStatementT], ABC):
181
181
  self._capture_classical_var(var_name, var_type)
182
182
 
183
183
  def _update_captured_vars(self, op: QuantumOperation) -> None:
184
- handles = (
185
- [(handle, PortDeclarationDirection.Input) for handle in op.inputs]
186
- + [(handle, PortDeclarationDirection.Output) for handle in op.outputs]
187
- + [(handle, PortDeclarationDirection.Inout) for handle in op.inouts]
188
- )
189
- for handle, direction in handles:
184
+ for handle, direction in op.handles_with_directions:
190
185
  self._capture_handle(handle, direction)
191
186
 
192
187
  def _capture_handle(
@@ -22,7 +22,9 @@ from classiq.interface.generator.expressions.proxies.classical.qmod_struct_insta
22
22
  from classiq.interface.generator.functions.type_name import TypeName
23
23
  from classiq.interface.model.handle_binding import (
24
24
  FieldHandleBinding,
25
+ GeneralHandle,
25
26
  HandleBinding,
27
+ HandlesList,
26
28
  SlicedHandleBinding,
27
29
  SubscriptHandleBinding,
28
30
  )
@@ -39,10 +41,17 @@ T = TypeVar("T")
39
41
 
40
42
 
41
43
  @dataclass(frozen=True)
42
- class QuantumSymbol:
43
- handle: HandleBinding
44
+ class QuantumVariable:
44
45
  quantum_type: QuantumType
45
46
 
47
+ def emit(self) -> GeneralHandle:
48
+ raise NotImplementedError
49
+
50
+
51
+ @dataclass(frozen=True)
52
+ class QuantumSymbol(QuantumVariable):
53
+ handle: HandleBinding
54
+
46
55
  @property
47
56
  def is_subscript(self) -> bool:
48
57
  return isinstance(self.handle, (SubscriptHandleBinding, SlicedHandleBinding))
@@ -140,6 +149,47 @@ class QuantumSymbol:
140
149
  for field_name, field_type in quantum_type.fields.items()
141
150
  }
142
151
 
152
+ def __str__(self) -> str:
153
+ return str(self.handle)
154
+
155
+
156
+ @dataclass(frozen=True)
157
+ class QuantumSymbolList(QuantumVariable):
158
+ handles: list[HandleBinding]
159
+
160
+ @staticmethod
161
+ def from_symbols(
162
+ symbols: list[Union[QuantumSymbol, "QuantumSymbolList"]],
163
+ ) -> "QuantumSymbolList":
164
+ handles = list(
165
+ itertools.chain.from_iterable(
166
+ (
167
+ symbol.handles
168
+ if isinstance(symbol, QuantumSymbolList)
169
+ else [symbol.handle]
170
+ )
171
+ for symbol in symbols
172
+ )
173
+ )
174
+ if len(handles) == 0:
175
+ raise ClassiqExpansionError("Empty concatenation expression")
176
+ length: Optional[Expression]
177
+ if any(not symbol.quantum_type.has_size_in_bits for symbol in symbols):
178
+ length = None
179
+ else:
180
+ length = Expression(
181
+ expr=str(sum(symbol.quantum_type.size_in_bits for symbol in symbols))
182
+ )
183
+ return QuantumSymbolList(
184
+ handles=handles, quantum_type=QuantumBitvector(length=length)
185
+ )
186
+
187
+ def emit(self) -> HandlesList:
188
+ return HandlesList(handles=self.handles)
189
+
190
+ def __str__(self) -> str:
191
+ return str(self.handles)
192
+
143
193
 
144
194
  @singledispatch
145
195
  def evaluated_to_str(value: Any) -> str:
@@ -183,7 +233,7 @@ class Evaluated: # FIXME: Merge with EvaluatedExpression if possible
183
233
  def emit(self) -> ArgValue:
184
234
  from classiq.model_expansions.closure import FunctionClosure
185
235
 
186
- if isinstance(self.value, (QuantumSymbol, FunctionClosure)):
236
+ if isinstance(self.value, (QuantumVariable, FunctionClosure)):
187
237
  return self.value.emit()
188
238
  if isinstance(self.value, list) and all(
189
239
  isinstance(item, FunctionClosure) for item in self.value
@@ -33,7 +33,7 @@ def _handle_contains_handle(handle: HandleBinding, other_handle: HandleBinding)
33
33
  return 0
34
34
 
35
35
 
36
- class _ExprNormalizer(ast.NodeTransformer):
36
+ class ExprNormalizer(ast.NodeTransformer):
37
37
  def visit_Call(self, node: ast.Call) -> ast.AST:
38
38
  if not isinstance(node.func, ast.Name):
39
39
  return self.generic_visit(node)
@@ -74,7 +74,7 @@ SymbolRenaming = Mapping[HandleBinding, Sequence[HandleRenaming]]
74
74
  def _rewrite_expression(
75
75
  symbol_mapping: SymbolRenaming, expression: Expression
76
76
  ) -> Expression:
77
- normalized_expr = _ExprNormalizer().visit(ast.parse(expression.expr))
77
+ normalized_expr = ExprNormalizer().visit(ast.parse(expression.expr))
78
78
  vrc = VarRefCollector(
79
79
  ignore_duplicated_handles=True, ignore_sympy_symbols=True, unevaluated=True
80
80
  )
@@ -62,9 +62,9 @@ class SymbolicParamInference(ModelVisitor):
62
62
  def __init__(
63
63
  self,
64
64
  functions: list[NativeFunctionDefinition],
65
- additional_signatures: (
66
- list[NamedParamsQuantumFunctionDeclaration] | None
67
- ) = None,
65
+ additional_signatures: Optional[
66
+ list[NamedParamsQuantumFunctionDeclaration]
67
+ ] = None,
68
68
  ) -> None:
69
69
  self._functions = nameables_to_dict(functions)
70
70
  self._additional_signatures = (
@@ -39,7 +39,7 @@ def span_lookup_table(func: RealFunction, *targets: QNum) -> QNum:
39
39
  The quantum result of applying func to targets
40
40
 
41
41
  Notes:
42
- Must be called inside a generative function (`@qfunc(generative=True)`)
42
+ Must be called inside a generative function (`@qfunc`)
43
43
  """
44
44
  if len(targets) == 0:
45
45
  raise ClassiqValueError("No targets specified")
@@ -334,7 +334,7 @@ def _classical_hadamard_transform(arr: list[float]) -> np.ndarray:
334
334
  return 1 / np.sqrt(len(arr)) * np.array(sympy.fwht(np.array(arr)))
335
335
 
336
336
 
337
- @qfunc(generative=True)
337
+ @qfunc
338
338
  def _load_phases(
339
339
  phases: list[float],
340
340
  target: QArray[QBit, Literal["log(get_field(phases, 'len'), 2)"]],
@@ -1,4 +1,4 @@
1
- from typing import Optional, Union
1
+ from typing import Optional, Union, cast
2
2
 
3
3
  from classiq.interface.exceptions import ClassiqError
4
4
  from classiq.interface.executor.execution_preferences import ExecutionPreferences
@@ -7,10 +7,26 @@ from classiq.interface.generator.model.preferences.preferences import Preference
7
7
  from classiq.interface.model.model import MAIN_FUNCTION_NAME, SerializedModel
8
8
 
9
9
  from classiq.qmod.classical_function import CFunc
10
- from classiq.qmod.quantum_function import GenerativeQFunc, QFunc
10
+ from classiq.qmod.quantum_function import BaseQFunc, GenerativeQFunc, QFunc
11
11
  from classiq.qmod.write_qmod import write_qmod
12
12
 
13
13
 
14
+ class _EntryPointWrapper(str):
15
+ entry_point: BaseQFunc
16
+
17
+
18
+ def add_entry_point(
19
+ new_model: SerializedModel, old_model: SerializedModel
20
+ ) -> SerializedModel:
21
+ if not hasattr(old_model, "entry_point"):
22
+ return new_model
23
+ new_model_with_entry_point = _EntryPointWrapper(new_model)
24
+ new_model_with_entry_point.entry_point = cast(
25
+ _EntryPointWrapper, old_model
26
+ ).entry_point
27
+ return cast(SerializedModel, new_model_with_entry_point)
28
+
29
+
14
30
  def create_model(
15
31
  entry_point: Union[QFunc, GenerativeQFunc],
16
32
  constraints: Optional[Constraints] = None,
@@ -48,7 +64,9 @@ def create_model(
48
64
  preferences,
49
65
  classical_execution_function,
50
66
  )
51
- result = model.get_model()
67
+ serialized_model = _EntryPointWrapper(model.get_model())
68
+ serialized_model.entry_point = entry_point
69
+ result = cast(SerializedModel, serialized_model)
52
70
 
53
71
  if out_file is not None:
54
72
  write_qmod(result, out_file)
@@ -0,0 +1,19 @@
1
+ from collections.abc import Iterator
2
+ from contextlib import contextmanager
3
+
4
+ _DECLARATIVE_SWITCH = False
5
+
6
+
7
+ def get_global_declarative_switch() -> bool:
8
+ return _DECLARATIVE_SWITCH
9
+
10
+
11
+ @contextmanager
12
+ def set_global_declarative_switch() -> Iterator[None]:
13
+ global _DECLARATIVE_SWITCH
14
+ previous = _DECLARATIVE_SWITCH
15
+ _DECLARATIVE_SWITCH = True
16
+ try:
17
+ yield
18
+ finally:
19
+ _DECLARATIVE_SWITCH = previous
@@ -35,6 +35,7 @@ from classiq.interface.model.control import Control
35
35
  from classiq.interface.model.handle_binding import (
36
36
  FieldHandleBinding,
37
37
  HandleBinding,
38
+ HandlesList,
38
39
  SlicedHandleBinding,
39
40
  SubscriptHandleBinding,
40
41
  )
@@ -395,6 +396,9 @@ class DSLPrettyPrinter(ModelVisitor):
395
396
  def visit_FieldHandleBinding(self, var_ref: FieldHandleBinding) -> str:
396
397
  return f"{self.visit(var_ref.base_handle)}.{self.visit(var_ref.field)}"
397
398
 
399
+ def visit_HandlesList(self, handles: HandlesList) -> str:
400
+ return f"{{{', '.join(map(self.visit, handles.handles))}}}"
401
+
398
402
  def visit_ArithmeticOperation(self, arith_op: ArithmeticOperation) -> str:
399
403
  if arith_op.operation_kind == ArithmeticOperationKind.Assignment:
400
404
  op = "="
@@ -37,6 +37,7 @@ from classiq.interface.model.control import Control
37
37
  from classiq.interface.model.handle_binding import (
38
38
  FieldHandleBinding,
39
39
  HandleBinding,
40
+ HandlesList,
40
41
  SlicedHandleBinding,
41
42
  SubscriptHandleBinding,
42
43
  )
@@ -518,6 +519,9 @@ class PythonPrettyPrinter(ModelVisitor):
518
519
  def visit_FieldHandleBinding(self, var_ref: FieldHandleBinding) -> str:
519
520
  return f"{self.visit(var_ref.base_handle)}.{self.visit(var_ref.field)}"
520
521
 
522
+ def visit_HandlesList(self, handles: HandlesList) -> str:
523
+ return self.visit(handles.handles)
524
+
521
525
  def visit_ArithmeticOperation(
522
526
  self, arith_op: ArithmeticOperation, in_lambda: bool = False
523
527
  ) -> str:
classiq/qmod/qfunc.py CHANGED
@@ -1,9 +1,9 @@
1
- from collections.abc import Iterator
2
- from contextlib import contextmanager
1
+ import warnings
3
2
  from typing import Callable, Literal, Optional, Union, overload
4
3
 
5
- from classiq.interface.exceptions import ClassiqInternalError
4
+ from classiq.interface.exceptions import ClassiqDeprecationWarning, ClassiqInternalError
6
5
 
6
+ from classiq.qmod.global_declarative_switch import get_global_declarative_switch
7
7
  from classiq.qmod.quantum_callable import QCallable
8
8
  from classiq.qmod.quantum_function import (
9
9
  BaseQFunc,
@@ -12,22 +12,9 @@ from classiq.qmod.quantum_function import (
12
12
  QFunc,
13
13
  )
14
14
 
15
- _GENERATIVE_SWITCH = False
16
-
17
-
18
- @contextmanager
19
- def set_global_generative_switch() -> Iterator[None]:
20
- global _GENERATIVE_SWITCH
21
- previous = _GENERATIVE_SWITCH
22
- _GENERATIVE_SWITCH = True
23
- try:
24
- yield
25
- finally:
26
- _GENERATIVE_SWITCH = previous
27
-
28
15
 
29
16
  @overload
30
- def qfunc(func: Callable) -> QFunc: ...
17
+ def qfunc(func: Callable) -> GenerativeQFunc: ...
31
18
 
32
19
 
33
20
  @overload
@@ -42,16 +29,16 @@ def qfunc(
42
29
  @overload
43
30
  def qfunc(
44
31
  *,
45
- generative: Literal[True],
32
+ generative: Literal[False],
46
33
  synthesize_separately: bool = False,
47
34
  atomic_qualifiers: Optional[list[str]] = None,
48
- ) -> Callable[[Callable], GenerativeQFunc]: ...
35
+ ) -> Callable[[Callable], QFunc]: ...
49
36
 
50
37
 
51
38
  @overload
52
39
  def qfunc(
53
40
  *, synthesize_separately: bool, atomic_qualifiers: Optional[list[str]] = None
54
- ) -> Callable[[Callable], QFunc]: ...
41
+ ) -> Callable[[Callable], GenerativeQFunc]: ...
55
42
 
56
43
 
57
44
  @overload
@@ -59,17 +46,38 @@ def qfunc(
59
46
  *,
60
47
  synthesize_separately: bool = False,
61
48
  atomic_qualifiers: Optional[list[str]] = None,
62
- ) -> Callable[[Callable], QFunc]: ...
49
+ ) -> Callable[[Callable], GenerativeQFunc]: ...
63
50
 
64
51
 
65
52
  def qfunc(
66
53
  func: Optional[Callable] = None,
67
54
  *,
68
55
  external: bool = False,
69
- generative: bool = False,
56
+ generative: Optional[bool] = None,
70
57
  synthesize_separately: bool = False,
71
58
  atomic_qualifiers: Optional[list[str]] = None,
72
59
  ) -> Union[Callable[[Callable], QCallable], QCallable]:
60
+ if generative is True:
61
+ warnings.warn(
62
+ "The use of `generative=True` is no longer required. Note that the "
63
+ "treatment of parameters of Qmod types will change from Python value to "
64
+ "symbolic in a near release. Change Qmod types to the corresponding Python "
65
+ "built-in types in order to use the parameters in Python expression "
66
+ "contexts.\n"
67
+ "Recommended changes:\n"
68
+ "@qfunc(generative=True) -> @qfunc\n"
69
+ "CInt->int\n"
70
+ "CReal->float\n"
71
+ "CArray->list\n\n"
72
+ "For more information see https://docs.classiq.io/latest/qmod-reference/language-reference/generative-descriptions/",
73
+ ClassiqDeprecationWarning,
74
+ stacklevel=2,
75
+ )
76
+ elif generative is None:
77
+ generative = True
78
+ if get_global_declarative_switch():
79
+ generative = False
80
+
73
81
  def wrapper(func: Callable) -> QCallable:
74
82
  qfunc: BaseQFunc
75
83
 
@@ -77,7 +85,7 @@ def qfunc(
77
85
  _validate_directives(synthesize_separately, atomic_qualifiers)
78
86
  return ExternalQFunc(func)
79
87
 
80
- if generative or _GENERATIVE_SWITCH:
88
+ if generative:
81
89
  qfunc = GenerativeQFunc(func)
82
90
  else:
83
91
  qfunc = QFunc(func)