classiq 0.66.1__py3-none-any.whl → 0.67.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 (50) hide show
  1. classiq/applications/finance/finance_model_constructor.py +9 -0
  2. classiq/applications/grover/grover_model_constructor.py +10 -0
  3. classiq/applications/qnn/qlayer.py +8 -2
  4. classiq/applications/qsvm/qsvm_model_constructor.py +9 -0
  5. classiq/interface/_version.py +1 -1
  6. classiq/interface/debug_info/debug_info.py +12 -0
  7. classiq/interface/exceptions.py +2 -5
  8. classiq/interface/generator/arith/argument_utils.py +1 -1
  9. classiq/interface/generator/arith/arithmetic.py +3 -1
  10. classiq/interface/generator/arith/binary_ops.py +3 -0
  11. classiq/interface/generator/function_param_list_without_self_reference.py +2 -0
  12. classiq/interface/generator/functions/type_name.py +2 -2
  13. classiq/interface/generator/generated_circuit_data.py +34 -1
  14. classiq/interface/generator/hardware_efficient_ansatz.py +1 -1
  15. classiq/interface/generator/hva.py +1 -1
  16. classiq/interface/generator/model/preferences/preferences.py +8 -1
  17. classiq/interface/generator/reset.py +14 -0
  18. classiq/interface/generator/ucc.py +1 -1
  19. classiq/interface/interface_version.py +1 -1
  20. classiq/interface/model/quantum_statement.py +13 -0
  21. classiq/model_expansions/atomic_expression_functions_defs.py +2 -1
  22. classiq/model_expansions/capturing/captured_vars.py +184 -54
  23. classiq/model_expansions/closure.py +6 -3
  24. classiq/model_expansions/evaluators/control.py +14 -38
  25. classiq/model_expansions/function_builder.py +19 -14
  26. classiq/model_expansions/generative_functions.py +7 -11
  27. classiq/model_expansions/interpreters/base_interpreter.py +14 -5
  28. classiq/model_expansions/interpreters/generative_interpreter.py +9 -8
  29. classiq/model_expansions/quantum_operations/allocate.py +6 -2
  30. classiq/model_expansions/quantum_operations/bind.py +65 -13
  31. classiq/model_expansions/quantum_operations/call_emitter.py +79 -10
  32. classiq/model_expansions/quantum_operations/classicalif.py +5 -2
  33. classiq/model_expansions/quantum_operations/emitter.py +8 -1
  34. classiq/model_expansions/quantum_operations/repeat.py +7 -2
  35. classiq/model_expansions/quantum_operations/shallow_emitter.py +1 -1
  36. classiq/model_expansions/quantum_operations/variable_decleration.py +11 -1
  37. classiq/open_library/functions/amplitude_amplification.py +4 -4
  38. classiq/open_library/functions/grover.py +5 -5
  39. classiq/qmod/builtins/functions/__init__.py +3 -0
  40. classiq/qmod/builtins/functions/mid_circuit_measurement.py +15 -0
  41. classiq/qmod/quantum_function.py +4 -0
  42. classiq/qmod/semantics/annotation/call_annotation.py +8 -2
  43. classiq/qmod/semantics/annotation/model_annotation.py +9 -0
  44. classiq/qmod/semantics/error_manager.py +0 -6
  45. classiq/qmod/semantics/static_semantics_visitor.py +0 -347
  46. {classiq-0.66.1.dist-info → classiq-0.67.0.dist-info}/METADATA +1 -1
  47. {classiq-0.66.1.dist-info → classiq-0.67.0.dist-info}/RECORD +48 -47
  48. classiq/qmod/semantics/validation/func_call_validation.py +0 -99
  49. classiq/qmod/semantics/validation/handle_validation.py +0 -85
  50. {classiq-0.66.1.dist-info → classiq-0.67.0.dist-info}/WHEEL +0 -0
@@ -1,4 +1,7 @@
1
- from classiq.interface.exceptions import ClassiqExpansionError
1
+ from classiq.interface.exceptions import (
2
+ ClassiqExpansionError,
3
+ ClassiqInternalExpansionError,
4
+ )
2
5
  from classiq.interface.model.bind_operation import BindOperation
3
6
 
4
7
  from classiq.model_expansions.evaluators.parameter_types import (
@@ -9,21 +12,12 @@ from classiq.model_expansions.evaluators.quantum_type_utils import (
9
12
  validate_bind_targets,
10
13
  )
11
14
  from classiq.model_expansions.quantum_operations.emitter import Emitter
12
- from classiq.model_expansions.scope import QuantumSymbol
15
+ from classiq.model_expansions.scope import Evaluated, QuantumSymbol
13
16
 
14
17
 
15
18
  class BindEmitter(Emitter[BindOperation]):
16
19
  def emit(self, bind: BindOperation, /) -> None:
17
- inputs: list[QuantumSymbol] = [
18
- self._interpreter.evaluate(arg).as_type(QuantumSymbol)
19
- for arg in bind.in_handles
20
- ]
21
- outputs: list[QuantumSymbol] = [
22
- self._interpreter.evaluate(arg).as_type(QuantumSymbol)
23
- for arg in bind.out_handles
24
- ]
25
- inputs = evaluate_types_in_quantum_symbols(inputs, self._current_scope)
26
- outputs = evaluate_types_in_quantum_symbols(outputs, self._current_scope)
20
+ inputs, outputs = self._get_inputs_outputs(bind)
27
21
  validate_bind_targets(bind, self._current_scope)
28
22
  unsized_outputs = [
29
23
  output for output in outputs if not output.quantum_type.has_size_in_bits
@@ -56,5 +50,63 @@ class BindEmitter(Emitter[BindOperation]):
56
50
  )
57
51
 
58
52
  self.emit_statement(
59
- BindOperation(in_handles=bind.in_handles, out_handles=bind.out_handles)
53
+ BindOperation(
54
+ in_handles=bind.in_handles,
55
+ out_handles=bind.out_handles,
56
+ back_ref=bind.uuid,
57
+ )
60
58
  )
59
+
60
+ def _get_inputs_outputs(
61
+ self, bind: BindOperation
62
+ ) -> tuple[list[QuantumSymbol], list[QuantumSymbol]]:
63
+ evaluated_inputs: list[Evaluated] = [
64
+ self._interpreter.evaluate(arg) for arg in bind.in_handles
65
+ ]
66
+ evaluated_outputs: list[Evaluated] = [
67
+ self._interpreter.evaluate(arg) for arg in bind.out_handles
68
+ ]
69
+ self._validate_handle_states(evaluated_inputs, evaluated_outputs)
70
+ inputs: list[QuantumSymbol] = [
71
+ input.as_type(QuantumSymbol) for input in evaluated_inputs
72
+ ]
73
+ outputs: list[QuantumSymbol] = [
74
+ output.as_type(QuantumSymbol) for output in evaluated_outputs
75
+ ]
76
+ inputs = evaluate_types_in_quantum_symbols(inputs, self._current_scope)
77
+ outputs = evaluate_types_in_quantum_symbols(outputs, self._current_scope)
78
+ return inputs, outputs
79
+
80
+ def _validate_handle_states(
81
+ self, inputs: list[Evaluated], outputs: list[Evaluated]
82
+ ) -> None:
83
+ input_var_names: set[str] = set()
84
+ for inp in inputs:
85
+ if inp.defining_function is None:
86
+ raise ClassiqInternalExpansionError
87
+ var_name = inp.value.handle.name
88
+ input_var_names.add(var_name)
89
+ state = self._builder.current_block.captured_vars.get_state(
90
+ var_name, inp.defining_function
91
+ )
92
+ if not state:
93
+ raise ClassiqExpansionError(
94
+ f"Cannot use uninitialized quantum variable "
95
+ f"{inp.value.handle.name!r} on the left-hand side of a bind "
96
+ f"statement"
97
+ )
98
+ for out in outputs:
99
+ if out.defining_function is None:
100
+ raise ClassiqInternalExpansionError
101
+ var_name = out.value.handle.name
102
+ if var_name in input_var_names:
103
+ continue
104
+ state = self._builder.current_block.captured_vars.get_state(
105
+ var_name, out.defining_function
106
+ )
107
+ if state:
108
+ raise ClassiqExpansionError(
109
+ f"Cannot use initialized quantum variable "
110
+ f"{out.value.handle.name!r} on the right-hand side of a bind "
111
+ f"statement"
112
+ )
@@ -1,13 +1,17 @@
1
1
  from collections.abc import Sequence
2
- from itertools import chain
2
+ from itertools import chain, combinations
3
3
  from typing import (
4
4
  TYPE_CHECKING,
5
5
  Generic,
6
- Optional,
7
6
  cast,
8
7
  )
8
+ from uuid import UUID
9
9
 
10
10
  from classiq.interface.debug_info.debug_info import FunctionDebugInfo
11
+ from classiq.interface.exceptions import ClassiqExpansionError
12
+ from classiq.interface.generator.functions.port_declaration import (
13
+ PortDeclarationDirection,
14
+ )
11
15
  from classiq.interface.generator.generated_circuit_data import OperationLevel
12
16
  from classiq.interface.model.classical_parameter_declaration import (
13
17
  ClassicalParameterDeclaration,
@@ -26,6 +30,8 @@ from classiq.interface.model.variable_declaration_statement import (
26
30
  )
27
31
 
28
32
  from classiq.model_expansions.capturing.captured_vars import (
33
+ INITIALIZED_VAR_MESSAGE,
34
+ UNINITIALIZED_VAR_MESSAGE,
29
35
  validate_args_are_not_propagated,
30
36
  )
31
37
  from classiq.model_expansions.closure import FunctionClosure
@@ -53,6 +59,25 @@ if TYPE_CHECKING:
53
59
  from classiq.model_expansions.interpreters.base_interpreter import BaseInterpreter
54
60
 
55
61
 
62
+ def _validate_cloning(evaluated_args: list[Evaluated]) -> None:
63
+ handles = [
64
+ arg.value.handle
65
+ for arg in evaluated_args
66
+ if isinstance(arg.value, QuantumSymbol)
67
+ ]
68
+ for handle, other_handle in combinations(handles, 2):
69
+ if handle.overlaps(other_handle):
70
+ if handle == other_handle:
71
+ raise ClassiqExpansionError(
72
+ f"Quantum cloning violation: Argument {str(handle)!r} is "
73
+ f"duplicated"
74
+ )
75
+ raise ClassiqExpansionError(
76
+ f"Quantum cloning violation: Arguments {str(handle)!r} and "
77
+ f"{str(other_handle)!r} overlap"
78
+ )
79
+
80
+
56
81
  class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSplitter):
57
82
  def __init__(self, interpreter: "BaseInterpreter") -> None:
58
83
  Emitter.__init__(self, interpreter)
@@ -71,15 +96,15 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
71
96
  name=self._counted_name_allocator.allocate(name),
72
97
  body=body,
73
98
  scope=Scope(parent=self._current_scope),
74
- is_lambda=True,
99
+ lambda_external_vars=self._builder.current_block.captured_vars,
75
100
  )
76
- return self._create_quantum_function_call(wrapping_function, list())
101
+ return self._create_quantum_function_call(wrapping_function, list(), None)
77
102
 
78
103
  def _emit_quantum_function_call(
79
104
  self,
80
105
  function: FunctionClosure,
81
106
  args: list[ArgValue],
82
- propagated_debug_info: Optional[FunctionDebugInfo] = None,
107
+ propagated_debug_info: FunctionDebugInfo | None,
83
108
  ) -> QuantumFunctionCall:
84
109
  call = self._create_quantum_function_call(
85
110
  function, args, propagated_debug_info=propagated_debug_info
@@ -87,15 +112,27 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
87
112
  self.emit_statement(call)
88
113
  return call
89
114
 
115
+ @staticmethod
116
+ def _get_back_ref(
117
+ propagated_debug_info: FunctionDebugInfo | None,
118
+ ) -> UUID | None:
119
+ if propagated_debug_info is None:
120
+ return None
121
+ if propagated_debug_info.node is None:
122
+ return None
123
+ return propagated_debug_info.node.uuid
124
+
90
125
  def _create_quantum_function_call(
91
126
  self,
92
127
  function: FunctionClosure,
93
128
  args: list[ArgValue],
94
- propagated_debug_info: Optional[FunctionDebugInfo] = None,
129
+ propagated_debug_info: FunctionDebugInfo | None,
95
130
  ) -> QuantumFunctionCall:
96
131
  function = function.clone()
97
132
  function = function.set_depth(self._builder.current_function.depth + 1)
133
+ self._validate_call_args(function.positional_arg_declarations, args)
98
134
  evaluated_args = [self._interpreter.evaluate(arg) for arg in args]
135
+ _validate_cloning(evaluated_args)
99
136
  new_declaration = self._prepare_fully_typed_declaration(
100
137
  function, evaluated_args
101
138
  )
@@ -114,13 +151,15 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
114
151
  new_positional_args = self._get_new_positional_args(
115
152
  evaluated_args, is_atomic, new_positional_arg_decls
116
153
  )
117
- captured_args = function.captured_vars.get_captured_args(
154
+ captured_args = function.captured_vars.filter_vars(function).get_captured_args(
118
155
  self._builder.current_function
119
156
  )
120
157
  validate_args_are_not_propagated(new_positional_args, captured_args)
121
158
  new_positional_args.extend(captured_args)
122
159
  new_call = QuantumFunctionCall(
123
- function=new_declaration.name, positional_args=new_positional_args
160
+ function=new_declaration.name,
161
+ positional_args=new_positional_args,
162
+ back_ref=self._get_back_ref(propagated_debug_info),
124
163
  )
125
164
  is_allocate_or_free = new_call.func_name == free.func_decl.name
126
165
  parameters = {
@@ -144,6 +183,7 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
144
183
  ),
145
184
  is_allocate_or_free=is_allocate_or_free,
146
185
  port_to_passed_variable_map=port_to_passed_variable_map,
186
+ node=new_call._as_back_ref(),
147
187
  )
148
188
  new_call.set_func_decl(new_declaration)
149
189
  return new_call
@@ -187,7 +227,10 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
187
227
  ) -> NativeFunctionDefinition:
188
228
  func_def = self._builder.create_definition(function_context)
189
229
 
190
- captured_ports = function_context.closure.captured_vars.get_captured_ports()
230
+ captured_vars = function_context.closure.captured_vars.filter_vars(
231
+ function_context.closure
232
+ )
233
+ captured_ports = captured_vars.get_captured_ports()
191
234
  if len(captured_ports) == 0:
192
235
  return func_def
193
236
  func_def.positional_arg_declarations = list(
@@ -197,7 +240,7 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
197
240
  if not function_context.is_lambda:
198
241
  return func_def
199
242
  func_def.body = self.rewrite(
200
- func_def.body, function_context.closure.captured_vars.get_captured_mapping()
243
+ func_def.body, captured_vars.get_captured_mapping()
201
244
  )
202
245
 
203
246
  return func_def
@@ -265,3 +308,29 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
265
308
  evaluated_args,
266
309
  ),
267
310
  )
311
+
312
+ def _validate_call_args(
313
+ self, params: Sequence[PositionalArg], args: list[ArgValue]
314
+ ) -> None:
315
+ for param, arg in zip(params, args):
316
+ if not isinstance(param, PortDeclaration) or not isinstance(
317
+ arg, HandleBinding
318
+ ):
319
+ continue
320
+ var_name = arg.name
321
+ symbol = self._interpreter.evaluate(var_name)
322
+ if (
323
+ not isinstance(symbol.value, QuantumSymbol)
324
+ or symbol.defining_function is None
325
+ ):
326
+ continue
327
+ var_state = self._builder.current_block.captured_vars.get_state(
328
+ var_name, symbol.defining_function
329
+ )
330
+ if not var_state and param.direction in (
331
+ PortDeclarationDirection.Inout,
332
+ PortDeclarationDirection.Input,
333
+ ):
334
+ raise ClassiqExpansionError(UNINITIALIZED_VAR_MESSAGE.format(var_name))
335
+ if var_state and param.direction == PortDeclarationDirection.Output:
336
+ raise ClassiqExpansionError(INITIALIZED_VAR_MESSAGE.format(var_name))
@@ -1,5 +1,6 @@
1
1
  from collections.abc import Sequence
2
2
 
3
+ from classiq.interface.debug_info.debug_info import new_function_debug_info_by_node
3
4
  from classiq.interface.model.classical_if import ClassicalIf
4
5
  from classiq.interface.model.quantum_function_call import QuantumFunctionCall
5
6
  from classiq.interface.model.quantum_statement import QuantumStatement
@@ -48,6 +49,8 @@ class ClassicalIfEmitter(CallEmitter[ClassicalIf]):
48
49
  name=self._counted_name_allocator.allocate("then" if condition else "else"),
49
50
  body=body,
50
51
  scope=Scope(parent=self._current_scope),
51
- is_lambda=True,
52
+ lambda_external_vars=self._builder.current_block.captured_vars,
53
+ )
54
+ self._emit_quantum_function_call(
55
+ then_else_func, list(), new_function_debug_info_by_node(classical_if)
52
56
  )
53
- self._emit_quantum_function_call(then_else_func, list())
@@ -9,7 +9,10 @@ from typing import (
9
9
 
10
10
  import sympy
11
11
 
12
- from classiq.interface.debug_info.debug_info import DebugInfoCollection
12
+ from classiq.interface.debug_info.debug_info import (
13
+ DebugInfoCollection,
14
+ new_function_debug_info_by_node,
15
+ )
13
16
  from classiq.interface.exceptions import ClassiqInternalExpansionError
14
17
  from classiq.interface.generator.expressions.evaluated_expression import (
15
18
  EvaluatedExpression,
@@ -140,6 +143,10 @@ class Emitter(Generic[QuantumStatementT], ABC):
140
143
  def emit_statement(self, statement: QuantumStatement) -> None:
141
144
  if isinstance(statement, QuantumOperation):
142
145
  self._update_captured_vars(statement)
146
+ if statement.uuid not in self._interpreter._model.debug_info:
147
+ self._interpreter._model.debug_info[statement.uuid] = (
148
+ new_function_debug_info_by_node(statement) # type:ignore[arg-type]
149
+ )
143
150
  self._builder.emit_statement(statement)
144
151
 
145
152
  def _update_captured_vars(self, op: QuantumOperation) -> None:
@@ -1,3 +1,4 @@
1
+ from classiq.interface.debug_info.debug_info import new_function_debug_info_by_node
1
2
  from classiq.interface.exceptions import ClassiqExpansionError
2
3
  from classiq.interface.generator.expressions.expression import Expression
3
4
  from classiq.interface.generator.functions.builtins.internal_operators import (
@@ -50,7 +51,11 @@ class RepeatEmitter(CallEmitter[Repeat]):
50
51
  ],
51
52
  body=repeat.body,
52
53
  scope=Scope(parent=self._current_scope),
53
- is_lambda=True,
54
+ lambda_external_vars=self._builder.current_block.captured_vars,
54
55
  **extra_args,
55
56
  )
56
- self._emit_quantum_function_call(iteration_function, [Expression(expr=str(i))])
57
+ self._emit_quantum_function_call(
58
+ iteration_function,
59
+ [Expression(expr=str(i))],
60
+ new_function_debug_info_by_node(repeat),
61
+ )
@@ -70,7 +70,7 @@ class ShallowEmitter(Emitter[QuantumOperation]):
70
70
  expanded_components[handle_name] = self._interpreter.evaluate(
71
71
  handle
72
72
  ).value.handle
73
-
73
+ expanded_components["back_ref"] = op.uuid
74
74
  op = op.model_copy(update=expanded_components)
75
75
  if isinstance(op, QuantumAssignmentOperation):
76
76
  self._post_process_assignment(op)
@@ -1,3 +1,4 @@
1
+ from classiq.interface.exceptions import ClassiqExpansionError
1
2
  from classiq.interface.model.handle_binding import HandleBinding
2
3
  from classiq.interface.model.variable_declaration_statement import (
3
4
  VariableDeclarationStatement,
@@ -12,8 +13,14 @@ from classiq.model_expansions.scope import Evaluated, QuantumSymbol
12
13
 
13
14
  class VariableDeclarationStatementEmitter(Emitter[VariableDeclarationStatement]):
14
15
  def emit(self, variable_declaration: VariableDeclarationStatement, /) -> None:
15
- var_decl = variable_declaration.model_copy()
16
+ var_decl = variable_declaration.model_copy(
17
+ update=dict(back_ref=variable_declaration.uuid)
18
+ )
16
19
  var_decl.quantum_type = variable_declaration.quantum_type.model_copy()
20
+ if variable_declaration.name in self._current_scope:
21
+ raise ClassiqExpansionError(
22
+ f"Variable {variable_declaration.name!r} is already defined"
23
+ )
17
24
  self._current_scope[variable_declaration.name] = Evaluated(
18
25
  value=QuantumSymbol(
19
26
  handle=HandleBinding(name=var_decl.name),
@@ -25,4 +32,7 @@ class VariableDeclarationStatementEmitter(Emitter[VariableDeclarationStatement])
25
32
  ),
26
33
  defining_function=self._builder.current_function,
27
34
  )
35
+ self._builder.current_block.captured_vars.init_var(
36
+ var_decl.name, self._builder.current_function
37
+ )
28
38
  self.emit_statement(var_decl)
@@ -52,13 +52,13 @@ def exact_amplitude_amplification(
52
52
 
53
53
  Based on the algorithm in [Quantum state preparation without coherent arithmetic](https://arxiv.org/abs/2210.14892).
54
54
 
55
- Assuming the `space_transform` creates a state $|\\psi\rangle = a|\\psi_good\rangle + \\sqrt(1-a)|\\psi_bad\rangle$, given `a` as the `amplitude`
56
- argument, the function will load exactly the state $|\\psi_good\rangle$.
55
+ Assuming the `space_transform` creates a state $|\\psi\\rangle = a|\\psi_{good}\\rangle + \\sqrt{1-a}|\\psi_{bad}\\rangle$, given `a` as the `amplitude`
56
+ argument, the function will load exactly the state $|\\psi_{good}\\rangle$.
57
57
 
58
- Note: if the `amplitude` argument is not exact, the resulting state will not be exactly $|\\psi_good\rangle$, and there will be additional internal auxilliary of the function that is not released correctly.
58
+ Note: if the `amplitude` argument is not exact, the resulting state will not be exactly $|\\psi_{good}\\rangle$, and there will be additional internal auxilliary of the function that is not released correctly.
59
59
 
60
60
  Args:
61
- amplitude: The amplitude of the state $|\\psi_good\rangle$ with regards to the initial state prepared by.
61
+ amplitude: The amplitude of the state $|\\psi_{good}\\rangle$ with regards to the initial state prepared by `space_transform`.
62
62
  oracle: The oracle operator that marks the "good" states. This operator should flip the sign of the amplitude of the "good" state.
63
63
  space_transform: The space transform operator (which is known also the state preparation operator). First applied to prepare the state before the amplification, then used inside the Grover operator.
64
64
  packed_vars: The variable that holds the state to be amplified. Assumed to be in the zero state at the beginning of the algorithm.
@@ -56,8 +56,8 @@ def reflect_about_zero(packed_vars: QArray[QBit]) -> None:
56
56
  besides the |0> state). Implements the operator $S_0$:
57
57
 
58
58
  $$
59
- \begin{equation}
60
- S_0|{x}\rangle = (-1)^{(x\ne0)}|{x}\rangle= (2|{0}\rangle\\langle{0}|-I)|{x}\rangle
59
+ \\begin{equation}
60
+ S_0|{x}\\rangle = (-1)^{(x\\ne0)}|{x}\\rangle= (2|{0}\\rangle\\langle{0}|-I)|{x}\\rangle
61
61
  \\end{equation}
62
62
  $$
63
63
 
@@ -85,7 +85,7 @@ def grover_diffuser(
85
85
  is the `space_transform` parameter. It is defined as:
86
86
 
87
87
  $$
88
- \begin{equation}
88
+ \\begin{equation}
89
89
  D = A S_0 A^{\\dagger}
90
90
  \\end{equation}
91
91
  $$
@@ -118,11 +118,11 @@ def grover_operator(
118
118
  $$
119
119
 
120
120
  where $S_{\\psi_1}$ is a reflection about marked states, and $S_{\\psi_0}$ is a reflection
121
- about a given state defined by $|\\psi_0\rangle = A|0\rangle$.
121
+ about a given state defined by $|\\psi_0\\rangle = A|0\\rangle$.
122
122
 
123
123
  Args:
124
124
  oracle: A unitary operator which adds a phase of (-1) to marked states.
125
- space_transform: The operator which creates $|\\psi_0\rangle$, the initial state, used by the diffuser to reflect about it.
125
+ space_transform: The operator which creates $|\\psi_0\\rangle$, the initial state, used by the diffuser to reflect about it.
126
126
  packed_vars: The state to which to apply the grover operator.
127
127
  """
128
128
  oracle(packed_vars)
@@ -4,6 +4,7 @@ from .benchmarking import *
4
4
  from .chemistry import *
5
5
  from .exponentiation import *
6
6
  from .finance import *
7
+ from .mid_circuit_measurement import *
7
8
  from .operators import *
8
9
  from .qsvm import *
9
10
  from .standard_gates import *
@@ -66,6 +67,7 @@ CORE_LIB_DECLS = [
66
67
  suzuki_trotter,
67
68
  qdrift,
68
69
  exponentiation_with_depth_constraint,
70
+ RESET,
69
71
  )
70
72
  ]
71
73
 
@@ -129,6 +131,7 @@ __all__ = [ # noqa: RUF022
129
131
  "single_pauli_exponent",
130
132
  "suzuki_trotter",
131
133
  "unitary",
134
+ "RESET",
132
135
  ]
133
136
  BUILTIN_FUNCTION_DECLARATIONS = {
134
137
  func_decl.name: func_decl for func_decl in STD_QMOD_OPERATORS + CORE_LIB_DECLS
@@ -0,0 +1,15 @@
1
+ from classiq.qmod.qfunc import qfunc
2
+ from classiq.qmod.qmod_variable import QBit
3
+
4
+
5
+ @qfunc(external=True)
6
+ def RESET(target: QBit) -> None:
7
+ """
8
+ Resets the target qubit to the |0> state.
9
+
10
+ Performed by measuring the qubit and applying an X gate if necessary.
11
+
12
+ Args:
13
+ target: the qubit to reset
14
+ """
15
+ pass
@@ -31,6 +31,7 @@ from classiq.qmod.qmod_parameter import CArray, CParam
31
31
  from classiq.qmod.qmod_variable import QVar
32
32
  from classiq.qmod.quantum_callable import QCallable, QCallableList
33
33
  from classiq.qmod.quantum_expandable import QExpandable, QTerminalCallable
34
+ from classiq.qmod.semantics.annotation.qstruct_annotator import QStructAnnotator
34
35
  from classiq.qmod.utilities import mangle_keyword
35
36
 
36
37
 
@@ -161,6 +162,9 @@ class QFunc(BaseQFunc):
161
162
  )
162
163
 
163
164
  generative_functions = list(self._qmodule.generative_functions.values())
165
+ QStructAnnotator().visit(model_stub)
166
+ for gen_func in generative_functions:
167
+ QStructAnnotator().visit(gen_func.func_decl)
164
168
  resolve_function_calls(
165
169
  model_stub,
166
170
  dict(model_stub.function_dict)
@@ -1,6 +1,6 @@
1
1
  from collections.abc import Iterator, Mapping
2
2
  from contextlib import contextmanager
3
- from typing import Any
3
+ from typing import Any, Optional
4
4
 
5
5
  from classiq.interface.exceptions import ClassiqError
6
6
  from classiq.interface.generator.expressions.expression import Expression
@@ -11,6 +11,7 @@ from classiq.interface.generator.functions.port_declaration import (
11
11
  from classiq.interface.model.classical_parameter_declaration import (
12
12
  ClassicalParameterDeclaration,
13
13
  )
14
+ from classiq.interface.model.model import Model
14
15
  from classiq.interface.model.model_visitor import ModelVisitor
15
16
  from classiq.interface.model.native_function_definition import NativeFunctionDefinition
16
17
  from classiq.interface.model.port_declaration import PortDeclaration
@@ -108,8 +109,13 @@ class _CallLambdaAnnotator(ModelVisitor):
108
109
 
109
110
  def resolve_function_calls(
110
111
  root: Any,
111
- quantum_function_dict: Mapping[str, QuantumFunctionDeclaration],
112
+ quantum_function_dict: Optional[Mapping[str, QuantumFunctionDeclaration]] = None,
112
113
  ) -> None:
114
+ if quantum_function_dict is None:
115
+ quantum_function_dict = {}
116
+ quantum_function_dict = dict(quantum_function_dict)
117
+ if isinstance(root, Model):
118
+ quantum_function_dict |= root.function_dict
113
119
  all_functions: Mapping[str, QuantumFunctionDeclaration] = {
114
120
  **BUILTIN_FUNCTION_DECLARATIONS,
115
121
  **quantum_function_dict,
@@ -0,0 +1,9 @@
1
+ from classiq.interface.model.model import Model
2
+
3
+ from classiq.qmod.semantics.annotation.call_annotation import resolve_function_calls
4
+ from classiq.qmod.semantics.annotation.qstruct_annotator import QStructAnnotator
5
+
6
+
7
+ def annotate_model(model: Model) -> None:
8
+ QStructAnnotator().visit(model)
9
+ resolve_function_calls(model)
@@ -94,9 +94,3 @@ class ErrorManager:
94
94
  self._call_stack.append(func_name)
95
95
  yield
96
96
  self._call_stack.pop()
97
-
98
-
99
- def append_error(node: ASTNode, message: str) -> None:
100
- instance = ErrorManager()
101
- with instance.node_context(node):
102
- instance.add_error(message)