classiq 0.64.0__py3-none-any.whl → 0.65.1__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 (51) hide show
  1. classiq/_internals/api_wrapper.py +30 -0
  2. classiq/applications/chemistry/chemistry_model_constructor.py +8 -9
  3. classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +4 -6
  4. classiq/applications/combinatorial_optimization/combinatorial_problem.py +2 -5
  5. classiq/applications/finance/finance_model_constructor.py +7 -12
  6. classiq/applications/grover/grover_model_constructor.py +4 -6
  7. classiq/applications/qsvm/qsvm_model_constructor.py +6 -4
  8. classiq/execution/execution_session.py +14 -13
  9. classiq/interface/_version.py +1 -1
  10. classiq/interface/backend/backend_preferences.py +1 -9
  11. classiq/interface/generator/expressions/qmod_qarray_proxy.py +11 -13
  12. classiq/interface/generator/functions/type_name.py +6 -0
  13. classiq/interface/model/allocate.py +16 -0
  14. classiq/interface/model/quantum_type.py +26 -0
  15. classiq/interface/model/statement_block.py +2 -0
  16. classiq/interface/server/routes.py +1 -0
  17. classiq/model_expansions/evaluators/quantum_type_utils.py +10 -0
  18. classiq/model_expansions/function_builder.py +35 -11
  19. classiq/model_expansions/generative_functions.py +6 -4
  20. classiq/model_expansions/interpreters/base_interpreter.py +37 -138
  21. classiq/model_expansions/interpreters/frontend_generative_interpreter.py +28 -0
  22. classiq/model_expansions/interpreters/generative_interpreter.py +144 -3
  23. classiq/model_expansions/quantum_operations/call_emitter.py +43 -91
  24. classiq/model_expansions/quantum_operations/declarative_call_emitter.py +87 -0
  25. classiq/model_expansions/quantum_operations/emitter.py +5 -0
  26. classiq/model_expansions/quantum_operations/quantum_function_call.py +9 -0
  27. classiq/model_expansions/quantum_operations/shallow_emitter.py +20 -1
  28. classiq/model_expansions/scope.py +15 -15
  29. classiq/model_expansions/scope_initialization.py +3 -5
  30. classiq/open_library/functions/discrete_sine_cosine_transform.py +8 -2
  31. classiq/open_library/functions/grover.py +1 -1
  32. classiq/open_library/functions/modular_exponentiation.py +8 -2
  33. classiq/open_library/functions/state_preparation.py +23 -13
  34. classiq/open_library/functions/swap_test.py +1 -2
  35. classiq/open_library/functions/variational.py +1 -2
  36. classiq/qmod/builtins/__init__.py +1 -1
  37. classiq/qmod/builtins/operations.py +51 -0
  38. classiq/qmod/native/pretty_printer.py +9 -1
  39. classiq/qmod/pretty_print/pretty_printer.py +12 -1
  40. classiq/qmod/qmod_variable.py +38 -38
  41. classiq/qmod/quantum_function.py +4 -4
  42. classiq/qmod/semantics/annotation/__init__.py +0 -0
  43. classiq/qmod/semantics/annotation/call_annotation.py +92 -0
  44. classiq/qmod/semantics/lambdas.py +25 -0
  45. classiq/qmod/semantics/static_semantics_visitor.py +8 -46
  46. classiq/qmod/utilities.py +16 -0
  47. {classiq-0.64.0.dist-info → classiq-0.65.1.dist-info}/METADATA +1 -1
  48. {classiq-0.64.0.dist-info → classiq-0.65.1.dist-info}/RECORD +50 -45
  49. classiq/qmod/semantics/annotation.py +0 -36
  50. /classiq/qmod/semantics/{qstruct_annotator.py → annotation/qstruct_annotator.py} +0 -0
  51. {classiq-0.64.0.dist-info → classiq-0.65.1.dist-info}/WHEEL +0 -0
@@ -0,0 +1,87 @@
1
+ from itertools import chain
2
+ from typing import TYPE_CHECKING, Generic
3
+
4
+ from classiq.interface.generator.functions.port_declaration import (
5
+ PortDeclarationDirection,
6
+ )
7
+ from classiq.interface.model.port_declaration import PortDeclaration
8
+
9
+ from classiq.model_expansions.closure import FunctionClosure, GenerativeClosure
10
+ from classiq.model_expansions.quantum_operations.call_emitter import CallEmitter
11
+ from classiq.model_expansions.quantum_operations.emitter import QuantumStatementT
12
+ from classiq.model_expansions.scope import Evaluated
13
+ from classiq.qmod.model_state_container import QMODULE
14
+
15
+ if TYPE_CHECKING:
16
+ from classiq.model_expansions.interpreters.generative_interpreter import (
17
+ GenerativeInterpreter,
18
+ )
19
+
20
+
21
+ class DeclarativeCallEmitter(
22
+ Generic[QuantumStatementT], CallEmitter[QuantumStatementT]
23
+ ):
24
+ _interpreter: "GenerativeInterpreter"
25
+
26
+ def __init__(self, interpreter: "GenerativeInterpreter") -> None:
27
+ super().__init__(interpreter)
28
+
29
+ def should_expand_function(
30
+ self, function: FunctionClosure, args: list[Evaluated]
31
+ ) -> bool:
32
+ if not super().should_expand_function(function, args):
33
+ return False
34
+
35
+ if self._is_function_purely_declarative(
36
+ function
37
+ ) and self._are_args_purely_declarative(args):
38
+ self._interpreter.add_purely_declarative_function(function)
39
+ return False
40
+
41
+ return True
42
+
43
+ def _is_function_purely_declarative(self, function: FunctionClosure) -> bool:
44
+ if function.name not in QMODULE.native_defs:
45
+ return False
46
+
47
+ if isinstance(function, GenerativeClosure):
48
+ return False
49
+
50
+ if any(
51
+ not param.quantum_type.is_instantiated
52
+ for param in function.positional_arg_declarations
53
+ if isinstance(param, PortDeclaration)
54
+ and param.direction == PortDeclarationDirection.Output
55
+ ):
56
+ return False
57
+
58
+ dependencies = QMODULE.function_dependencies[function.name]
59
+ return self._are_identifiers_purely_declarative(dependencies)
60
+
61
+ def _are_args_purely_declarative(self, args: list[Evaluated]) -> bool:
62
+ values = [arg.value for arg in args]
63
+ function_inputs: list[FunctionClosure] = list(
64
+ chain.from_iterable(
65
+ (
66
+ [arg]
67
+ if isinstance(arg, FunctionClosure)
68
+ else (
69
+ arg
70
+ if isinstance(arg, list)
71
+ and any(isinstance(item, FunctionClosure) for item in arg)
72
+ else []
73
+ )
74
+ )
75
+ for arg in values
76
+ )
77
+ )
78
+ if any(func.is_lambda for func in function_inputs):
79
+ return False
80
+ dependencies = [func.name for func in function_inputs if not func.is_lambda]
81
+ return self._are_identifiers_purely_declarative(dependencies)
82
+
83
+ def _are_identifiers_purely_declarative(self, dependencies: list[str]) -> bool:
84
+ return not any(
85
+ isinstance(self._current_scope[dep].value, GenerativeClosure)
86
+ for dep in dependencies
87
+ )
@@ -19,6 +19,7 @@ from classiq.interface.generator.expressions.expression import Expression
19
19
  from classiq.interface.generator.functions.port_declaration import (
20
20
  PortDeclarationDirection,
21
21
  )
22
+ from classiq.interface.helpers.pydantic_model_helpers import nameables_to_dict
22
23
  from classiq.interface.model.handle_binding import HandleBinding
23
24
  from classiq.interface.model.native_function_definition import NativeFunctionDefinition
24
25
  from classiq.interface.model.quantum_function_declaration import (
@@ -80,6 +81,10 @@ class Emitter(Generic[QuantumStatementT]):
80
81
  def _expanded_functions(self) -> dict[str, NativeFunctionDefinition]:
81
82
  return self._interpreter._expanded_functions
82
83
 
84
+ @property
85
+ def _expanded_functions_by_name(self) -> dict[str, NativeFunctionDefinition]:
86
+ return nameables_to_dict(list(self._interpreter._expanded_functions.values()))
87
+
83
88
  @property
84
89
  def _counted_name_allocator(self) -> CountedNameAllocator:
85
90
  return self._interpreter._counted_name_allocator
@@ -4,6 +4,9 @@ from classiq.interface.model.quantum_function_call import QuantumFunctionCall
4
4
 
5
5
  from classiq.model_expansions.closure import FunctionClosure
6
6
  from classiq.model_expansions.quantum_operations.call_emitter import CallEmitter
7
+ from classiq.model_expansions.quantum_operations.declarative_call_emitter import (
8
+ DeclarativeCallEmitter,
9
+ )
7
10
  from classiq.qmod.semantics.error_manager import ErrorManager
8
11
 
9
12
  if TYPE_CHECKING:
@@ -22,3 +25,9 @@ class QuantumFunctionCallEmitter(CallEmitter[QuantumFunctionCall]):
22
25
  self._emit_quantum_function_call(
23
26
  function, args, self._debug_info.get(call.uuid)
24
27
  )
28
+
29
+
30
+ class DeclarativeQuantumFunctionCallEmitter(
31
+ QuantumFunctionCallEmitter, DeclarativeCallEmitter
32
+ ):
33
+ pass
@@ -6,6 +6,7 @@ from classiq.interface.generator.expressions.expression import Expression
6
6
  from classiq.interface.generator.functions.port_declaration import (
7
7
  PortDeclarationDirection,
8
8
  )
9
+ from classiq.interface.model.allocate import Allocate
9
10
  from classiq.interface.model.handle_binding import HandleBinding
10
11
  from classiq.interface.model.quantum_expressions.arithmetic_operation import (
11
12
  ArithmeticOperation,
@@ -17,7 +18,10 @@ from classiq.interface.model.quantum_expressions.quantum_expression import (
17
18
  from classiq.interface.model.quantum_statement import QuantumOperation, QuantumStatement
18
19
 
19
20
  from classiq.model_expansions.closure import Closure
20
- from classiq.model_expansions.evaluators.quantum_type_utils import copy_type_information
21
+ from classiq.model_expansions.evaluators.quantum_type_utils import (
22
+ copy_type_information,
23
+ set_size,
24
+ )
21
25
  from classiq.model_expansions.quantum_operations.emitter import Emitter
22
26
  from classiq.model_expansions.scope import QuantumSymbol, Scope
23
27
  from classiq.model_expansions.transformers.ast_renamer import rename_variables
@@ -73,6 +77,8 @@ class ShallowEmitter(Emitter[QuantumOperation]):
73
77
  op = op.model_copy(update=expanded_components)
74
78
  if isinstance(op, QuantumAssignmentOperation):
75
79
  self._post_process_assignment(op)
80
+ if isinstance(op, Allocate):
81
+ self._post_process_allocate(op)
76
82
  self._builder.emit_statement(op)
77
83
 
78
84
  def _post_process_assignment(self, op: QuantumAssignmentOperation) -> None:
@@ -86,6 +92,19 @@ class ShallowEmitter(Emitter[QuantumOperation]):
86
92
  direction = PortDeclarationDirection.Inout
87
93
  self._capture_handle(op.result_var, direction)
88
94
 
95
+ def _post_process_allocate(self, allocate: Allocate) -> None:
96
+ target_symbol = self._interpreter.evaluate(allocate.target).value
97
+ if not isinstance(target_symbol, QuantumSymbol):
98
+ return
99
+ self._capture_handle(target_symbol.handle, PortDeclarationDirection.Output)
100
+ if allocate.size is None or not allocate.size.is_evaluated():
101
+ return
102
+ set_size(
103
+ target_symbol.quantum_type,
104
+ allocate.size.to_int_value(),
105
+ str(target_symbol.handle),
106
+ )
107
+
89
108
  def _split_components(
90
109
  self, op: QuantumOperation
91
110
  ) -> tuple[list[str], list[str], list[str]]:
@@ -19,9 +19,6 @@ 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_qarray_proxy import (
23
- ILLEGAL_SLICE_BOUNDS_MSG,
24
- )
25
22
  from classiq.interface.generator.expressions.qmod_struct_instance import (
26
23
  QmodStructInstance,
27
24
  )
@@ -65,16 +62,19 @@ class QuantumSymbol:
65
62
  def _slice(self, start: int, end: int) -> "QuantumSymbol":
66
63
  if not isinstance(self.quantum_type, QuantumBitvector):
67
64
  raise ClassiqExpansionError(
68
- f"{self.quantum_type.type_name} is not subscriptable."
65
+ f"{self.quantum_type.type_name} is not subscriptable"
69
66
  )
70
67
  if start >= end:
71
- raise ClassiqExpansionError(ILLEGAL_SLICE_BOUNDS_MSG.format(start, end))
72
- if (
73
- self.quantum_type.has_length
74
- and end - start > self.quantum_type.length_value
75
- ):
76
68
  raise ClassiqExpansionError(
77
- f"Slice [{start}:{end}] is out of bounds for {self.handle}."
69
+ f"{self.quantum_type.type_name} slice '{self.handle}[{start}:{end}]' "
70
+ f"has non-positive length"
71
+ )
72
+ array_length = self.quantum_type.length_value
73
+ if start < 0 or end > array_length:
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}"
78
78
  )
79
79
  return QuantumSymbol(
80
80
  handle=SlicedHandleBinding(
@@ -91,13 +91,13 @@ class QuantumSymbol:
91
91
  def _subscript(self, index: int) -> "QuantumSymbol":
92
92
  if not isinstance(self.quantum_type, QuantumBitvector):
93
93
  raise ClassiqExpansionError(
94
- f"{self.quantum_type.type_name} is not subscriptable."
94
+ f"{self.quantum_type.type_name} is not subscriptable"
95
95
  )
96
- if index < 0 or (
97
- self.quantum_type.has_length and index > self.quantum_type.length_value
98
- ):
96
+ array_length = self.quantum_type.length_value
97
+ if index < 0 or index >= array_length:
99
98
  raise ClassiqExpansionError(
100
- f"Subscript [{index}] is out of bounds for {self.handle}."
99
+ f"Subscript {index} is out of bounds of {self.quantum_type.type_name} "
100
+ f"{str(self.handle)!r} of length {array_length}"
101
101
  )
102
102
  return QuantumSymbol(
103
103
  handle=SubscriptHandleBinding(
@@ -78,7 +78,8 @@ def add_generative_functions_to_scope(
78
78
  functions: Sequence[GenerativeQFunc], scope: Scope
79
79
  ) -> None:
80
80
  for function in functions:
81
- if function.func_decl.name not in scope:
81
+ name = function.func_decl.name
82
+ if name == MAIN_FUNCTION_NAME or name not in scope:
82
83
  scope[function.func_decl.name] = Evaluated(
83
84
  value=GenerativeFunctionClosure.create(
84
85
  name=function.func_decl.name,
@@ -138,10 +139,7 @@ def add_entry_point_params_to_scope(
138
139
  )
139
140
 
140
141
 
141
- def init_top_level_scope(
142
- model: Model, generative_functions: list[GenerativeQFunc], scope: Scope
143
- ) -> None:
144
- add_generative_functions_to_scope(generative_functions, scope)
142
+ def init_top_level_scope(model: Model, scope: Scope) -> None:
145
143
  add_functions_to_scope(model.functions, scope)
146
144
  add_constants_to_scope(model.constants, scope)
147
145
  _init_builtins_scope(scope)
@@ -3,9 +3,15 @@ from classiq.open_library.functions.utility_functions import (
3
3
  apply_to_all,
4
4
  modular_increment,
5
5
  )
6
- from classiq.qmod.builtins.functions import allocate
7
6
  from classiq.qmod.builtins.functions.standard_gates import PHASE, H, S, X, Z
8
- from classiq.qmod.builtins.operations import bind, control, invert, repeat, within_apply
7
+ from classiq.qmod.builtins.operations import (
8
+ allocate,
9
+ bind,
10
+ control,
11
+ invert,
12
+ repeat,
13
+ within_apply,
14
+ )
9
15
  from classiq.qmod.qfunc import qfunc
10
16
  from classiq.qmod.qmod_variable import QArray, QBit, QNum
11
17
  from classiq.qmod.symbolic import pi
@@ -1,7 +1,7 @@
1
1
  from classiq.open_library.functions.utility_functions import hadamard_transform
2
- from classiq.qmod.builtins.functions import allocate
3
2
  from classiq.qmod.builtins.functions.standard_gates import H, U, X
4
3
  from classiq.qmod.builtins.operations import (
4
+ allocate,
5
5
  bind,
6
6
  control,
7
7
  invert,
@@ -1,8 +1,14 @@
1
1
  from classiq.open_library.functions.qft_functions import qft, qft_no_swap
2
2
  from classiq.qmod.builtins.classical_functions import qft_const_adder_phase
3
- from classiq.qmod.builtins.functions import allocate
4
3
  from classiq.qmod.builtins.functions.standard_gates import PHASE, SWAP, X
5
- from classiq.qmod.builtins.operations import bind, control, invert, repeat, within_apply
4
+ from classiq.qmod.builtins.operations import (
5
+ allocate,
6
+ bind,
7
+ control,
8
+ invert,
9
+ repeat,
10
+ within_apply,
11
+ )
6
12
  from classiq.qmod.cparam import CInt
7
13
  from classiq.qmod.qfunc import qfunc
8
14
  from classiq.qmod.qmod_variable import QArray, QBit, QNum
@@ -1,12 +1,14 @@
1
+ import warnings
1
2
  from typing import Literal
2
3
 
4
+ from classiq.interface.exceptions import ClassiqDeprecationWarning
5
+
3
6
  from classiq.open_library.functions.utility_functions import (
4
7
  hadamard_transform,
5
8
  modular_increment,
6
9
  )
7
- from classiq.qmod.builtins.functions import allocate
8
10
  from classiq.qmod.builtins.functions.standard_gates import CX, IDENTITY, RY, H, X
9
- from classiq.qmod.builtins.operations import control, if_, repeat
11
+ from classiq.qmod.builtins.operations import allocate, control, if_, repeat
10
12
  from classiq.qmod.cparam import CBool, CInt
11
13
  from classiq.qmod.qfunc import qfunc
12
14
  from classiq.qmod.qmod_variable import Output, QArray, QBit, QNum
@@ -16,6 +18,7 @@ from classiq.qmod.symbolic import (
16
18
  exp,
17
19
  floor,
18
20
  log,
21
+ logical_or,
19
22
  max as qmax,
20
23
  min as qmin,
21
24
  pi,
@@ -239,16 +242,19 @@ def prepare_bell_state(state_num: CInt, q: Output[QArray[QBit, Literal[2]]]) ->
239
242
 
240
243
  """
241
244
  allocate(2, q)
242
- inplace_prepare_int(state_num, q)
245
+ if_(logical_or(state_num == 1, state_num == 3), lambda: X(q[0]))
246
+ if_(logical_or(state_num == 2, state_num == 3), lambda: X(q[1]))
243
247
  H(q[0])
244
248
  CX(q[0], q[1])
245
249
 
246
250
 
247
251
  @qfunc
248
- def inplace_prepare_int(value: CInt, target: QArray[QBit]) -> None:
252
+ def inplace_prepare_int(value: CInt, target: QNum) -> None:
249
253
  """
250
254
  [Qmod Classiq-library function]
251
255
 
256
+ This function is **deprecated**. Use in-place-xor assignment statement in the form _target-var_ **^=** _quantum-expression_ or **inplace_xor(**_quantum-expression_**,** _target-var_**)** instead.
257
+
252
258
  Transitions a quantum variable in the zero state $|0\\rangle$ into the computational basis state $|\\text{value}\\rangle$.
253
259
  In the general case, the function performs a bitwise-XOR, i.e. transitions the state $|\\psi\\rangle$ into $|\\psi \\oplus \\text{value}\\rangle$.
254
260
 
@@ -259,14 +265,12 @@ def inplace_prepare_int(value: CInt, target: QArray[QBit]) -> None:
259
265
  Note:
260
266
  If the value cannot fit into the quantum variable, it is truncated, i.e. treated as the value modulo $2^\\text{target.size}$.
261
267
  """
262
- repeat(
263
- target.len,
264
- lambda index: if_(
265
- (floor(value / (2**index)) % 2) == 1,
266
- lambda: X(target[index]),
267
- lambda: IDENTITY(target[index]),
268
- ),
268
+ warnings.warn(
269
+ "Function 'inplace_prepare_int' is deprecated. Use in-place-xor assignment statement in the form '<var> ^= <expression>' or 'inplace_xor(<expression>, <var>)' instead.",
270
+ ClassiqDeprecationWarning,
271
+ stacklevel=1,
269
272
  )
273
+ target ^= value
270
274
 
271
275
 
272
276
  @qfunc
@@ -277,6 +281,8 @@ def prepare_int(
277
281
  """
278
282
  [Qmod Classiq-library function]
279
283
 
284
+ This function is **deprecated**. Use assignment statement in the form _target-var_ **|=** _quantum-expression_ or **assign(**_quantum-expression_**,** _target-var_**)** instead.
285
+
280
286
  Initializes a quantum variable to the computational basis state $|\\text{value}\\rangle$.
281
287
  The number of allocated qubits is automatically computed from the value, and is the minimal number required for representation in the computational basis.
282
288
 
@@ -287,5 +293,9 @@ def prepare_int(
287
293
  Note:
288
294
  If the output variable has been declared with a specific number of qubits, it must match the number of allocated qubits.
289
295
  """
290
- allocate(floor(log(value, 2)) + 1, out)
291
- inplace_prepare_int(value, out)
296
+ warnings.warn(
297
+ "Function 'prepare_int' is deprecated. Use assignment statement in the form '<var> |= <expression>' or 'assign(<expression>, <var>)' instead.",
298
+ ClassiqDeprecationWarning,
299
+ stacklevel=1,
300
+ )
301
+ out |= value
@@ -1,6 +1,5 @@
1
- from classiq.qmod.builtins.functions import allocate
2
1
  from classiq.qmod.builtins.functions.standard_gates import SWAP, H
3
- from classiq.qmod.builtins.operations import control, repeat
2
+ from classiq.qmod.builtins.operations import allocate, control, repeat
4
3
  from classiq.qmod.qfunc import qfunc
5
4
  from classiq.qmod.qmod_variable import Output, QArray, QBit
6
5
 
@@ -1,6 +1,5 @@
1
- from classiq.qmod.builtins.functions import allocate
2
1
  from classiq.qmod.builtins.functions.standard_gates import RX, RY, RZ
3
- from classiq.qmod.builtins.operations import repeat
2
+ from classiq.qmod.builtins.operations import allocate, repeat
4
3
  from classiq.qmod.cparam import CReal
5
4
  from classiq.qmod.qfunc import qfunc
6
5
  from classiq.qmod.qmod_parameter import CArray
@@ -12,7 +12,7 @@ from .enums import * # noqa: F403
12
12
  from .enums import __all__ as _builtin_enums
13
13
  from .functions import * # noqa: F403
14
14
  from .functions import __all__ as _builtin_functions
15
- from .operations import * # noqa: F403
15
+ from .operations import * # type:ignore[assignment] # noqa: F403
16
16
  from .operations import __all__ as _builtin_operations
17
17
  from .structs import * # noqa: F403
18
18
  from .structs import __all__ as _builtin_structs
@@ -7,6 +7,7 @@ from typing import (
7
7
  Callable,
8
8
  Final,
9
9
  Union,
10
+ overload,
10
11
  )
11
12
 
12
13
  from classiq.interface.exceptions import ClassiqValueError
@@ -15,6 +16,7 @@ from classiq.interface.generator.functions.builtins.internal_operators import (
15
16
  REPEAT_OPERATOR_NAME,
16
17
  )
17
18
  from classiq.interface.generator.functions.classical_type import Integer
19
+ from classiq.interface.model.allocate import Allocate
18
20
  from classiq.interface.model.bind_operation import BindOperation
19
21
  from classiq.interface.model.classical_if import ClassicalIf
20
22
  from classiq.interface.model.classical_parameter_declaration import (
@@ -41,6 +43,7 @@ from classiq.interface.model.statement_block import StatementBlock
41
43
  from classiq.interface.model.within_apply_operation import WithinApply
42
44
 
43
45
  from classiq.qmod.generative import is_generative_mode
46
+ from classiq.qmod.qmod_constant import QConstant
44
47
  from classiq.qmod.qmod_variable import Input, Output, QArray, QBit, QScalar, QVar
45
48
  from classiq.qmod.quantum_callable import QCallable
46
49
  from classiq.qmod.quantum_expandable import prepare_arg
@@ -50,6 +53,53 @@ from classiq.qmod.utilities import get_source_ref
50
53
  _MISSING_VALUE: Final[int] = -1
51
54
 
52
55
 
56
+ @overload
57
+ def allocate(num_qubits: Union[int, SymbolicExpr], out: Output[QVar]) -> None:
58
+ pass
59
+
60
+
61
+ @overload
62
+ def allocate(out: Output[QVar]) -> None:
63
+ pass
64
+
65
+
66
+ def allocate(*args: Any, **kwargs: Any) -> None:
67
+ """
68
+ Initialize a quantum variable.
69
+
70
+ If 'num_qubits' is specified, 'num_qubits' qubits will be allocated for variable
71
+ 'out'. Otherwise, the number of qubits will be inferred according to the type of
72
+ 'out'.
73
+
74
+ Args:
75
+ size: The number of qubits to be allocated (optional)
76
+ out: The target variable
77
+ """
78
+ assert QCallable.CURRENT_EXPANDABLE is not None
79
+ source_ref = get_source_ref(sys._getframe(1))
80
+ if len(args) == 0:
81
+ size = kwargs.get("num_qubits", None)
82
+ target = kwargs["out"]
83
+ elif len(args) == 1:
84
+ if "out" in kwargs:
85
+ size = args[0]
86
+ target = kwargs["out"]
87
+ else:
88
+ size = None
89
+ target = args[0]
90
+ else:
91
+ size, target = args
92
+ if isinstance(size, QConstant):
93
+ size.add_to_model()
94
+ QCallable.CURRENT_EXPANDABLE.append_statement_to_body(
95
+ Allocate(
96
+ size=None if size is None else Expression(expr=str(size)),
97
+ target=target.get_handle_binding(),
98
+ source_ref=source_ref,
99
+ )
100
+ )
101
+
102
+
53
103
  def bind(
54
104
  source: Union[Input[QVar], list[Input[QVar]]],
55
105
  destination: Union[Output[QVar], list[Output[QVar]]],
@@ -369,6 +419,7 @@ def _operand_to_body(
369
419
 
370
420
 
371
421
  __all__ = [
422
+ "allocate",
372
423
  "assign",
373
424
  "assign_amplitude",
374
425
  "bind",
@@ -22,6 +22,7 @@ from classiq.interface.generator.types.enum_declaration import EnumDeclaration
22
22
  from classiq.interface.generator.types.qstruct_declaration import QStructDeclaration
23
23
  from classiq.interface.generator.types.struct_declaration import StructDeclaration
24
24
  from classiq.interface.generator.visitor import NodeType, Visitor
25
+ from classiq.interface.model.allocate import Allocate
25
26
  from classiq.interface.model.bind_operation import BindOperation
26
27
  from classiq.interface.model.classical_if import ClassicalIf
27
28
  from classiq.interface.model.classical_parameter_declaration import (
@@ -80,7 +81,7 @@ from classiq.interface.model.within_apply_operation import WithinApply
80
81
 
81
82
  from classiq.open_library.functions import OPEN_LIBRARY_FUNCTIONS
82
83
  from classiq.qmod.native.expression_to_qmod import transform_expression
83
- from classiq.qmod.semantics.static_semantics_visitor import resolve_function_calls
84
+ from classiq.qmod.semantics.annotation.call_annotation import resolve_function_calls
84
85
  from classiq.qmod.utilities import DEFAULT_DECIMAL_PRECISION
85
86
 
86
87
 
@@ -253,6 +254,13 @@ class DSLPrettyPrinter(Visitor):
253
254
  self._level -= 1
254
255
  return f"{self.visit_QuantumFunctionDeclaration(func_def)} {{\n{body}}}\n"
255
256
 
257
+ def visit_Allocate(self, allocate: Allocate) -> str:
258
+ if allocate.size is not None:
259
+ size = f"{self.visit(allocate.size)}, "
260
+ else:
261
+ size = ""
262
+ return f"{self._indent}allocate({size}{self.visit(allocate.target)});\n"
263
+
256
264
  def visit_QuantumFunctionCall(self, func_call: QuantumFunctionCall) -> str:
257
265
  positional_args = ", ".join(
258
266
  self.visit(arg_decl) for arg_decl in func_call.positional_args
@@ -25,6 +25,7 @@ from classiq.interface.generator.types.enum_declaration import EnumDeclaration
25
25
  from classiq.interface.generator.types.qstruct_declaration import QStructDeclaration
26
26
  from classiq.interface.generator.types.struct_declaration import StructDeclaration
27
27
  from classiq.interface.generator.visitor import NodeType, Visitor
28
+ from classiq.interface.model.allocate import Allocate
28
29
  from classiq.interface.model.bind_operation import BindOperation
29
30
  from classiq.interface.model.classical_if import ClassicalIf
30
31
  from classiq.interface.model.classical_parameter_declaration import (
@@ -376,7 +377,10 @@ class PythonPrettyPrinter(Visitor):
376
377
 
377
378
  def visit_NativeFunctionDefinition(self, func_def: NativeFunctionDefinition) -> str:
378
379
  self._level += 1
379
- body = "".join(self.visit(statement) for statement in func_def.body)
380
+ if len(func_def.body) == 0:
381
+ body = " pass"
382
+ else:
383
+ body = "".join(self.visit(statement) for statement in func_def.body)
380
384
  self._level -= 1
381
385
  return f"{self.visit_QuantumFunctionDeclaration(func_def)} \n{body}\n"
382
386
 
@@ -402,6 +406,13 @@ class PythonPrettyPrinter(Visitor):
402
406
  )
403
407
  return ", ".join(self.visit(arg) for arg in func_call.positional_args)
404
408
 
409
+ def visit_Allocate(self, allocate: Allocate) -> str:
410
+ if allocate.size is not None:
411
+ size = f"{self.visit(allocate.size)}, "
412
+ else:
413
+ size = ""
414
+ return f"{self._indent}allocate({size}{self.visit(allocate.target)})\n"
415
+
405
416
  def visit_Control(self, op: Control) -> str:
406
417
  self._imports["control"] = 1
407
418
  control_else = (