classiq 0.66.1__py3-none-any.whl → 0.68.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 (89) hide show
  1. classiq/_internals/api_wrapper.py +5 -1
  2. classiq/_internals/async_utils.py +1 -1
  3. classiq/_internals/authentication/password_manager.py +1 -1
  4. classiq/_internals/client.py +1 -1
  5. classiq/applications/finance/finance_model_constructor.py +9 -0
  6. classiq/applications/grover/grover_model_constructor.py +10 -0
  7. classiq/applications/qnn/qlayer.py +8 -2
  8. classiq/applications/qsvm/qsvm_model_constructor.py +9 -0
  9. classiq/execution/execution_session.py +7 -3
  10. classiq/executor.py +7 -2
  11. classiq/interface/_version.py +1 -1
  12. classiq/interface/ast_node.py +1 -1
  13. classiq/interface/chemistry/operator.py +1 -1
  14. classiq/interface/debug_info/debug_info.py +27 -0
  15. classiq/interface/exceptions.py +2 -5
  16. classiq/interface/generator/arith/argument_utils.py +1 -1
  17. classiq/interface/generator/arith/arithmetic.py +99 -2
  18. classiq/interface/generator/arith/arithmetic_expression_parser.py +1 -1
  19. classiq/interface/generator/arith/binary_ops.py +3 -0
  20. classiq/interface/generator/function_param_list_without_self_reference.py +2 -0
  21. classiq/interface/generator/functions/type_name.py +2 -2
  22. classiq/interface/generator/generated_circuit_data.py +38 -3
  23. classiq/interface/generator/hardware_efficient_ansatz.py +1 -1
  24. classiq/interface/generator/hva.py +1 -1
  25. classiq/interface/generator/model/preferences/preferences.py +8 -1
  26. classiq/interface/generator/quantum_program.py +18 -1
  27. classiq/interface/generator/reset.py +14 -0
  28. classiq/interface/generator/types/enum_declaration.py +33 -2
  29. classiq/interface/generator/ucc.py +1 -1
  30. classiq/interface/interface_version.py +1 -1
  31. classiq/interface/model/classical_if.py +2 -2
  32. classiq/interface/model/control.py +2 -2
  33. classiq/interface/model/invert.py +2 -2
  34. classiq/interface/model/power.py +2 -2
  35. classiq/interface/model/quantum_function_call.py +4 -0
  36. classiq/interface/model/quantum_statement.py +13 -0
  37. classiq/interface/model/repeat.py +2 -2
  38. classiq/interface/model/statement_block.py +1 -1
  39. classiq/interface/model/within_apply_operation.py +2 -2
  40. classiq/model_expansions/atomic_expression_functions_defs.py +2 -1
  41. classiq/model_expansions/capturing/captured_vars.py +184 -54
  42. classiq/model_expansions/closure.py +6 -3
  43. classiq/model_expansions/evaluators/control.py +14 -38
  44. classiq/model_expansions/function_builder.py +19 -14
  45. classiq/model_expansions/generative_functions.py +7 -11
  46. classiq/model_expansions/interpreters/base_interpreter.py +14 -5
  47. classiq/model_expansions/interpreters/generative_interpreter.py +87 -26
  48. classiq/model_expansions/quantum_operations/allocate.py +9 -3
  49. classiq/model_expansions/quantum_operations/assignment_result_processor.py +52 -0
  50. classiq/model_expansions/quantum_operations/bind.py +67 -14
  51. classiq/model_expansions/quantum_operations/block_evaluator.py +76 -0
  52. classiq/model_expansions/quantum_operations/call_emitter.py +79 -10
  53. classiq/model_expansions/quantum_operations/classicalif.py +10 -6
  54. classiq/model_expansions/quantum_operations/composite_emitter.py +27 -0
  55. classiq/model_expansions/quantum_operations/emitter.py +24 -3
  56. classiq/model_expansions/quantum_operations/expression_evaluator.py +33 -0
  57. classiq/model_expansions/quantum_operations/handle_evaluator.py +28 -0
  58. classiq/model_expansions/quantum_operations/quantum_function_call.py +3 -2
  59. classiq/model_expansions/quantum_operations/repeat.py +9 -3
  60. classiq/model_expansions/quantum_operations/variable_decleration.py +13 -2
  61. classiq/model_expansions/sympy_conversion/expression_to_sympy.py +1 -1
  62. classiq/open_library/functions/__init__.py +1 -2
  63. classiq/open_library/functions/amplitude_amplification.py +12 -9
  64. classiq/open_library/functions/discrete_sine_cosine_transform.py +16 -13
  65. classiq/open_library/functions/grover.py +11 -15
  66. classiq/open_library/functions/modular_exponentiation.py +7 -13
  67. classiq/open_library/functions/state_preparation.py +16 -17
  68. classiq/open_library/functions/swap_test.py +1 -1
  69. classiq/open_library/functions/utility_functions.py +10 -2
  70. classiq/qmod/builtins/functions/__init__.py +3 -0
  71. classiq/qmod/builtins/functions/chemistry.py +6 -38
  72. classiq/qmod/builtins/functions/mid_circuit_measurement.py +15 -0
  73. classiq/qmod/quantum_expandable.py +30 -6
  74. classiq/qmod/quantum_function.py +4 -0
  75. classiq/qmod/semantics/annotation/call_annotation.py +8 -2
  76. classiq/qmod/semantics/annotation/model_annotation.py +9 -0
  77. classiq/qmod/semantics/error_manager.py +0 -6
  78. classiq/qmod/semantics/static_semantics_visitor.py +0 -347
  79. classiq/qmod/semantics/validation/types_validation.py +1 -1
  80. classiq/qmod/symbolic.py +2 -1
  81. classiq/qmod/utilities.py +2 -1
  82. classiq/qmod/write_qmod.py +10 -7
  83. classiq/synthesis.py +20 -7
  84. {classiq-0.66.1.dist-info → classiq-0.68.0.dist-info}/METADATA +1 -1
  85. {classiq-0.66.1.dist-info → classiq-0.68.0.dist-info}/RECORD +86 -81
  86. classiq/model_expansions/quantum_operations/shallow_emitter.py +0 -166
  87. classiq/qmod/semantics/validation/func_call_validation.py +0 -99
  88. classiq/qmod/semantics/validation/handle_validation.py +0 -85
  89. {classiq-0.66.1.dist-info → classiq-0.68.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,76 @@
1
+ from collections.abc import Sequence
2
+ from typing import TYPE_CHECKING
3
+
4
+ from classiq.interface.model.quantum_statement import QuantumOperation, QuantumStatement
5
+
6
+ from classiq.model_expansions.closure import Closure
7
+ from classiq.model_expansions.quantum_operations.emitter import Emitter
8
+ from classiq.model_expansions.scope import Scope
9
+
10
+ if TYPE_CHECKING:
11
+ from classiq.model_expansions.interpreters.base_interpreter import BaseInterpreter
12
+
13
+
14
+ _BLOCK_RENAMES = {
15
+ "compute": "within",
16
+ "action": "apply",
17
+ }
18
+ _REVERSE_BLOCK_RENAMES = {rename: name for name, rename in _BLOCK_RENAMES.items()}
19
+
20
+
21
+ class BlockEvaluator(Emitter[QuantumOperation]):
22
+ def __init__(
23
+ self, interpreter: "BaseInterpreter", operation_name: str, *block_names: str
24
+ ) -> None:
25
+ super().__init__(interpreter)
26
+ self._operation_name = operation_name
27
+ self._block_names: Sequence[str] = block_names
28
+
29
+ def emit(self, op: QuantumOperation, /) -> bool:
30
+ expanded_blocks: dict[str, list[QuantumStatement]] = {}
31
+ blocks = [
32
+ block
33
+ for block in self._block_names
34
+ if hasattr(op, block) and getattr(op, block) is not None
35
+ ]
36
+
37
+ if len(blocks) > 0:
38
+ if op.is_generative():
39
+ expanded_blocks = self.expand_generative_blocks(op)
40
+ else:
41
+ expanded_blocks = self.expand_blocks(op, blocks)
42
+ expanded_blocks.update(expanded_blocks)
43
+
44
+ op = op.model_copy(update={**expanded_blocks, "back_ref": op.uuid})
45
+ self._builder.emit_statement(op)
46
+ return True
47
+
48
+ def expand_blocks(
49
+ self, op: QuantumOperation, block_names: list[str]
50
+ ) -> dict[str, list[QuantumStatement]]:
51
+ blocks = {
52
+ _BLOCK_RENAMES.get(block, block): getattr(op, block)
53
+ for block in block_names
54
+ }
55
+ block_closure = Closure(
56
+ name=self._operation_name,
57
+ scope=Scope(parent=self._current_scope),
58
+ blocks=blocks,
59
+ )
60
+ context = self._expand_operation(block_closure)
61
+ return {
62
+ block: context.statements(_BLOCK_RENAMES.get(block, block))
63
+ for block in block_names
64
+ }
65
+
66
+ def expand_generative_blocks(
67
+ self, op: QuantumOperation
68
+ ) -> dict[str, list[QuantumStatement]]:
69
+ blocks = [
70
+ block for block in self._block_names if op.has_generative_block(block)
71
+ ]
72
+ context = self._expand_generative_context(op, self._operation_name, blocks)
73
+ return {
74
+ _REVERSE_BLOCK_RENAMES.get(block, block): context.statements(block)
75
+ for block in blocks
76
+ }
@@ -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
@@ -17,7 +18,7 @@ def _is_all_identity_calls(body: Sequence[QuantumStatement]) -> bool:
17
18
 
18
19
 
19
20
  class ClassicalIfEmitter(CallEmitter[ClassicalIf]):
20
- def emit(self, classical_if: ClassicalIf, /) -> None:
21
+ def emit(self, classical_if: ClassicalIf, /) -> bool:
21
22
  condition = self._interpreter.evaluate(classical_if.condition).as_type(bool)
22
23
  op_name = "then" if condition else "else"
23
24
  is_generative = classical_if.is_generative()
@@ -25,7 +26,7 @@ class ClassicalIfEmitter(CallEmitter[ClassicalIf]):
25
26
  body: Sequence[QuantumStatement]
26
27
  if is_generative:
27
28
  if not classical_if.has_generative_block(op_name):
28
- return
29
+ return True
29
30
  context = self._expand_generative_context(classical_if, op_name, op_name)
30
31
  context.blocks["body"] = context.blocks[op_name]
31
32
  context.blocks.pop(op_name)
@@ -34,7 +35,7 @@ class ClassicalIfEmitter(CallEmitter[ClassicalIf]):
34
35
  body = classical_if.then if condition else classical_if.else_
35
36
 
36
37
  if _is_all_identity_calls(body):
37
- return
38
+ return True
38
39
 
39
40
  if is_generative or not self._should_wrap(body):
40
41
  for stmt in body:
@@ -42,12 +43,15 @@ class ClassicalIfEmitter(CallEmitter[ClassicalIf]):
42
43
  self._interpreter._builder.emit_statement(stmt)
43
44
  else:
44
45
  self._interpreter.emit_statement(stmt)
45
- return
46
+ return True
46
47
 
47
48
  then_else_func = FunctionClosure.create(
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,
52
53
  )
53
- self._emit_quantum_function_call(then_else_func, list())
54
+ self._emit_quantum_function_call(
55
+ then_else_func, list(), new_function_debug_info_by_node(classical_if)
56
+ )
57
+ return True
@@ -0,0 +1,27 @@
1
+ from collections.abc import Sequence
2
+ from typing import TYPE_CHECKING, Generic
3
+
4
+ from classiq.model_expansions.quantum_operations.emitter import (
5
+ Emitter,
6
+ QuantumStatementT,
7
+ )
8
+
9
+ if TYPE_CHECKING:
10
+ from classiq.model_expansions.interpreters.base_interpreter import BaseInterpreter
11
+
12
+
13
+ class CompositeEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT]):
14
+ def __init__(
15
+ self,
16
+ interpreter: "BaseInterpreter",
17
+ emitters: Sequence[Emitter[QuantumStatementT]],
18
+ ) -> None:
19
+ super().__init__(interpreter)
20
+ self._emitters = emitters
21
+
22
+ def emit(self, statement: QuantumStatementT, /) -> bool:
23
+ for emitter in self._emitters:
24
+ if emitter.emit(statement):
25
+ return True
26
+ self._builder.emit_statement(statement)
27
+ return True
@@ -1,3 +1,4 @@
1
+ import ast
1
2
  from abc import ABC, abstractmethod
2
3
  from typing import (
3
4
  TYPE_CHECKING,
@@ -9,7 +10,10 @@ from typing import (
9
10
 
10
11
  import sympy
11
12
 
12
- from classiq.interface.debug_info.debug_info import DebugInfoCollection
13
+ from classiq.interface.debug_info.debug_info import (
14
+ DebugInfoCollection,
15
+ new_function_debug_info_by_node,
16
+ )
13
17
  from classiq.interface.exceptions import ClassiqInternalExpansionError
14
18
  from classiq.interface.generator.expressions.evaluated_expression import (
15
19
  EvaluatedExpression,
@@ -36,12 +40,15 @@ from classiq.model_expansions.sympy_conversion.sympy_to_python import (
36
40
  translate_sympy_quantum_expression,
37
41
  )
38
42
  from classiq.model_expansions.utils.counted_name_allocator import CountedNameAllocator
43
+ from classiq.model_expansions.visitors.variable_references import VarRefCollector
39
44
  from classiq.qmod.quantum_function import GenerativeQFunc
40
45
 
41
46
  if TYPE_CHECKING:
42
47
  from classiq.model_expansions.interpreters.base_interpreter import BaseInterpreter
43
48
 
44
- QuantumStatementT = TypeVar("QuantumStatementT", bound=QuantumStatement)
49
+ QuantumStatementT = TypeVar(
50
+ "QuantumStatementT", bound=QuantumStatement, contravariant=True
51
+ )
45
52
 
46
53
 
47
54
  class Emitter(Generic[QuantumStatementT], ABC):
@@ -57,7 +64,7 @@ class Emitter(Generic[QuantumStatementT], ABC):
57
64
  )
58
65
 
59
66
  @abstractmethod
60
- def emit(self, statement: QuantumStatementT, /) -> None:
67
+ def emit(self, statement: QuantumStatementT, /) -> bool:
61
68
  pass
62
69
 
63
70
  def _expand_operation(self, closure: Closure) -> OperationContext:
@@ -140,6 +147,10 @@ class Emitter(Generic[QuantumStatementT], ABC):
140
147
  def emit_statement(self, statement: QuantumStatement) -> None:
141
148
  if isinstance(statement, QuantumOperation):
142
149
  self._update_captured_vars(statement)
150
+ if statement.uuid not in self._interpreter._model.debug_info:
151
+ self._interpreter._model.debug_info[statement.uuid] = (
152
+ new_function_debug_info_by_node(statement) # type:ignore[arg-type]
153
+ )
143
154
  self._builder.emit_statement(statement)
144
155
 
145
156
  def _update_captured_vars(self, op: QuantumOperation) -> None:
@@ -166,3 +177,13 @@ class Emitter(Generic[QuantumStatementT], ABC):
166
177
  defining_function=defining_function,
167
178
  direction=direction,
168
179
  )
180
+
181
+ def _get_symbols_in_expression(self, expr: Expression) -> list[QuantumSymbol]:
182
+ vrc = VarRefCollector(ignore_duplicated_handles=True)
183
+ vrc.visit(ast.parse(expr.expr))
184
+ handles = dict.fromkeys(
185
+ handle
186
+ for handle in vrc.var_handles
187
+ if isinstance(self._current_scope[handle.name].value, QuantumSymbol)
188
+ )
189
+ return [self._interpreter.evaluate(handle).value for handle in handles]
@@ -0,0 +1,33 @@
1
+ from typing import TYPE_CHECKING
2
+
3
+ from classiq.interface.generator.expressions.expression import Expression
4
+ from classiq.interface.generator.functions.port_declaration import (
5
+ PortDeclarationDirection,
6
+ )
7
+ from classiq.interface.model.quantum_statement import QuantumOperation
8
+
9
+ from classiq.model_expansions.quantum_operations.emitter import Emitter
10
+
11
+ if TYPE_CHECKING:
12
+ from classiq.model_expansions.interpreters.base_interpreter import BaseInterpreter
13
+
14
+
15
+ class ExpressionEvaluator(Emitter[QuantumOperation]):
16
+ def __init__(self, interpreter: "BaseInterpreter", expression_name: str) -> None:
17
+ super().__init__(interpreter)
18
+ self._expression_name = expression_name
19
+
20
+ def emit(self, op: QuantumOperation, /) -> bool:
21
+ expression = getattr(op, self._expression_name)
22
+ if not isinstance(expression, Expression) or expression.is_evaluated():
23
+ return False
24
+ evaluated_expression = self._evaluate_expression(
25
+ expression, preserve_bool_ops=True
26
+ )
27
+ for symbol in self._get_symbols_in_expression(evaluated_expression):
28
+ self._capture_handle(symbol.handle, PortDeclarationDirection.Inout)
29
+ op = op.model_copy(
30
+ update={self._expression_name: evaluated_expression, "back_ref": op.uuid}
31
+ )
32
+ self._interpreter.emit(op)
33
+ return True
@@ -0,0 +1,28 @@
1
+ from typing import TYPE_CHECKING
2
+
3
+ from classiq.interface.model.handle_binding import HandleBinding
4
+ from classiq.interface.model.quantum_statement import QuantumOperation
5
+
6
+ from classiq.model_expansions.quantum_operations.emitter import Emitter
7
+
8
+ if TYPE_CHECKING:
9
+ from classiq.model_expansions.interpreters.base_interpreter import BaseInterpreter
10
+
11
+
12
+ class HandleEvaluator(Emitter[QuantumOperation]):
13
+ def __init__(self, interpreter: "BaseInterpreter", handle_name: str) -> None:
14
+ super().__init__(interpreter)
15
+ self._handle_name = handle_name
16
+
17
+ def emit(self, op: QuantumOperation, /) -> bool:
18
+ handle = getattr(op, self._handle_name)
19
+ if not isinstance(handle, HandleBinding):
20
+ return False
21
+ evaluated_handle = self._interpreter.evaluate(handle).value.handle
22
+ if handle == evaluated_handle:
23
+ return False
24
+ op = op.model_copy(
25
+ update={self._handle_name: evaluated_handle, "back_ref": op.uuid}
26
+ )
27
+ self._interpreter.emit(op)
28
+ return True
@@ -28,16 +28,17 @@ class QuantumFunctionCallEmitter(CallEmitter[QuantumFunctionCall]):
28
28
  super().__init__(interpreter)
29
29
  self._model = self._interpreter._model
30
30
 
31
- def emit(self, call: QuantumFunctionCall, /) -> None:
31
+ def emit(self, call: QuantumFunctionCall, /) -> bool:
32
32
  if call.function == "allocate": # FIXME: Remove compatibility (CAD-25935)
33
33
  self._allocate_compatibility(call)
34
- return
34
+ return True
35
35
  function = self._interpreter.evaluate(call.function).as_type(FunctionClosure)
36
36
  args = call.positional_args
37
37
  with ErrorManager().call(function.name):
38
38
  self._emit_quantum_function_call(
39
39
  function, args, self._debug_info.get(call.uuid)
40
40
  )
41
+ return True
41
42
 
42
43
  def _allocate_compatibility(self, call: QuantumFunctionCall) -> None:
43
44
  if len(call.positional_args) != 2:
@@ -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 (
@@ -16,7 +17,7 @@ from classiq.qmod.quantum_function import GenerativeQFunc
16
17
 
17
18
 
18
19
  class RepeatEmitter(CallEmitter[Repeat]):
19
- def emit(self, repeat: Repeat, /) -> None:
20
+ def emit(self, repeat: Repeat, /) -> bool:
20
21
  count = self._interpreter.evaluate(repeat.count).as_type(int)
21
22
  if count < 0:
22
23
  raise ClassiqExpansionError(
@@ -25,6 +26,7 @@ class RepeatEmitter(CallEmitter[Repeat]):
25
26
  op_name = self._counted_name_allocator.allocate(REPEAT_OPERATOR_NAME)
26
27
  for i in range(count):
27
28
  self._emit_iteration(repeat, i, op_name)
29
+ return True
28
30
 
29
31
  def _emit_iteration(self, repeat: Repeat, i: int, op_name: str) -> None:
30
32
  closure_constructor: type[FunctionClosure]
@@ -50,7 +52,11 @@ class RepeatEmitter(CallEmitter[Repeat]):
50
52
  ],
51
53
  body=repeat.body,
52
54
  scope=Scope(parent=self._current_scope),
53
- is_lambda=True,
55
+ lambda_external_vars=self._builder.current_block.captured_vars,
54
56
  **extra_args,
55
57
  )
56
- self._emit_quantum_function_call(iteration_function, [Expression(expr=str(i))])
58
+ self._emit_quantum_function_call(
59
+ iteration_function,
60
+ [Expression(expr=str(i))],
61
+ new_function_debug_info_by_node(repeat),
62
+ )
@@ -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,
@@ -11,9 +12,15 @@ from classiq.model_expansions.scope import Evaluated, QuantumSymbol
11
12
 
12
13
 
13
14
  class VariableDeclarationStatementEmitter(Emitter[VariableDeclarationStatement]):
14
- def emit(self, variable_declaration: VariableDeclarationStatement, /) -> None:
15
- var_decl = variable_declaration.model_copy()
15
+ def emit(self, variable_declaration: VariableDeclarationStatement, /) -> bool:
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,8 @@ 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)
39
+ return True
@@ -174,6 +174,6 @@ class ExpressionSympyTranslator(ast.NodeTransformer):
174
174
  def visit_Attribute(self, node: ast.Attribute) -> ast.Call:
175
175
  return ast.Call(
176
176
  func=ast.Name("get_field"),
177
- args=[node.value, ast.Constant(value=node.attr)],
177
+ args=[self.visit(node.value), ast.Constant(value=node.attr)],
178
178
  keywords=[],
179
179
  )
@@ -10,7 +10,7 @@ from .hea import *
10
10
  from .linear_pauli_rotation import *
11
11
  from .linear_pauli_rotation import _single_pauli
12
12
  from .modular_exponentiation import *
13
- from .modular_exponentiation import _check_msb, _ctrl_x
13
+ from .modular_exponentiation import _check_msb
14
14
  from .qaoa_penalty import *
15
15
  from .qft_functions import *
16
16
  from .qpe import *
@@ -72,7 +72,6 @@ OPEN_LIBRARY_FUNCTIONS = [
72
72
  qst_type2,
73
73
  modular_increment,
74
74
  qft,
75
- _ctrl_x,
76
75
  _prepare_uniform_trimmed_state_step,
77
76
  _qct_d_operator,
78
77
  _qct_pi_operator,
@@ -4,7 +4,7 @@ from classiq.qmod.builtins.operations import (
4
4
  allocate,
5
5
  bind,
6
6
  control,
7
- repeat,
7
+ power,
8
8
  within_apply,
9
9
  )
10
10
  from classiq.qmod.cparam import CInt, CReal
@@ -24,7 +24,7 @@ def amplitude_amplification(
24
24
  """
25
25
  [Qmod Classiq-library function]
26
26
 
27
- Applies the Amplitude Amplification algorithm (QAE); Prepares a state using the given `space_transform` function, and applies `reps` repetititions
27
+ Applies the Amplitude Amplification algorithm; Prepares a state using the given `space_transform` function, and applies `reps` repetititions
28
28
  of the grover operator, using the given `oracle` functions which marks the "good" states.
29
29
 
30
30
  Args:
@@ -34,7 +34,10 @@ def amplitude_amplification(
34
34
  packed_vars: The variable that holds the state to be amplified. Assumed to be in the zero state at the beginning of the algorithm.
35
35
  """
36
36
  space_transform(packed_qvars)
37
- repeat(reps, lambda index: grover_operator(oracle, space_transform, packed_qvars))
37
+ power(
38
+ reps,
39
+ lambda: grover_operator(oracle, space_transform, packed_qvars),
40
+ )
38
41
 
39
42
 
40
43
  @qfunc
@@ -47,18 +50,18 @@ def exact_amplitude_amplification(
47
50
  """
48
51
  [Qmod Classiq-library function]
49
52
 
50
- Applies an exact version of the Amplitude Amplification algorithm (QAE), assuming knowledge of the amplitude of the marked state.
53
+ Applies an exact version of the Amplitude Amplification algorithm, assuming knowledge of the amplitude of the marked state.
51
54
  The function should be applied on the zero state, and it takes care for preparing the initial state before amplification using the `space_transform`.
52
55
 
53
56
  Based on the algorithm in [Quantum state preparation without coherent arithmetic](https://arxiv.org/abs/2210.14892).
54
57
 
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$.
58
+ Assuming the `space_transform` creates a state $|\\psi\\rangle = a|\\psi_{good}\\rangle + \\sqrt{1-a}|\\psi_{bad}\\rangle$, given `a` as the `amplitude`
59
+ argument, the function will load exactly the state $|\\psi_{good}\\rangle$.
57
60
 
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.
61
+ 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
62
 
60
63
  Args:
61
- amplitude: The amplitude of the state $|\\psi_good\rangle$ with regards to the initial state prepared by.
64
+ amplitude: The amplitude of the state $|\\psi_{good}\\rangle$ with regards to the initial state prepared by `space_transform`.
62
65
  oracle: The oracle operator that marks the "good" states. This operator should flip the sign of the amplitude of the "good" state.
63
66
  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
67
  packed_vars: The variable that holds the state to be amplified. Assumed to be in the zero state at the beginning of the algorithm.
@@ -68,7 +71,7 @@ def exact_amplitude_amplification(
68
71
  theta = pi / (4 * k + 2)
69
72
  rot_phase = 2 * acos(sin(theta) / amplitude)
70
73
 
71
- extended_qvars: QArray = QArray("extended_qvars")
74
+ extended_qvars: QArray = QArray()
72
75
  within_apply(
73
76
  lambda: [ # type:ignore[arg-type]
74
77
  allocate(aux),