classiq 0.102.0__py3-none-any.whl → 1.0.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 (95) hide show
  1. classiq/__init__.py +2 -0
  2. classiq/_internals/authentication/auth0.py +29 -0
  3. classiq/_internals/authentication/auth_flow_factory.py +43 -0
  4. classiq/_internals/authentication/machine_credentials_flow.py +26 -0
  5. classiq/_internals/authentication/password_manager.py +84 -0
  6. classiq/_internals/authentication/token_manager.py +24 -8
  7. classiq/analyzer/show_interactive_hack.py +0 -8
  8. classiq/applications/chemistry/op_utils.py +32 -0
  9. classiq/applications/combinatorial_optimization/combinatorial_problem.py +1 -1
  10. classiq/evaluators/qmod_annotated_expression.py +1 -1
  11. classiq/evaluators/qmod_expression_visitors/qmod_expression_evaluator.py +1 -8
  12. classiq/evaluators/qmod_expression_visitors/qmod_expression_simplifier.py +1 -1
  13. classiq/evaluators/qmod_node_evaluators/attribute_evaluation.py +2 -2
  14. classiq/evaluators/qmod_node_evaluators/binary_op_evaluation.py +18 -29
  15. classiq/evaluators/qmod_node_evaluators/min_max_evaluation.py +1 -6
  16. classiq/evaluators/qmod_node_evaluators/numeric_attrs_utils.py +1 -7
  17. classiq/evaluators/qmod_type_inference/quantum_type_comparison.py +52 -0
  18. classiq/execution/all_hardware_devices.py +59 -1
  19. classiq/execution/execution_session.py +1 -1
  20. classiq/execution/functions/__init__.py +13 -0
  21. classiq/execution/functions/expectation_value.py +106 -0
  22. classiq/execution/functions/minimize.py +90 -0
  23. classiq/execution/functions/sample.py +76 -0
  24. classiq/execution/functions/state_vector.py +113 -0
  25. classiq/execution/functions/util/__init__.py +0 -0
  26. classiq/execution/functions/util/_logging.py +19 -0
  27. classiq/execution/functions/util/backend_preferences.py +188 -0
  28. classiq/execution/functions/util/constants.py +9 -0
  29. classiq/execution/functions/util/parse_provider_backend.py +90 -0
  30. classiq/interface/_version.py +1 -1
  31. classiq/interface/backend/backend_preferences.py +81 -0
  32. classiq/interface/backend/provider_config/providers/aqt.py +1 -1
  33. classiq/interface/backend/provider_config/providers/azure.py +1 -2
  34. classiq/interface/backend/provider_config/providers/ibm.py +1 -1
  35. classiq/interface/backend/quantum_backend_providers.py +14 -0
  36. classiq/interface/exceptions.py +0 -4
  37. classiq/interface/executor/result.py +9 -5
  38. classiq/interface/generator/arith/binary_ops.py +62 -2
  39. classiq/interface/generator/arith/number_utils.py +15 -6
  40. classiq/interface/generator/compiler_keywords.py +1 -0
  41. classiq/interface/generator/function_param_list.py +8 -2
  42. classiq/interface/generator/function_params.py +1 -1
  43. classiq/interface/generator/functions/builtins/internal_operators.py +5 -9
  44. classiq/interface/generator/functions/classical_type.py +60 -0
  45. classiq/interface/generator/functions/type_name.py +36 -0
  46. classiq/interface/generator/generated_circuit_data.py +0 -2
  47. classiq/interface/generator/transpiler_basis_gates.py +1 -0
  48. classiq/interface/generator/types/compilation_metadata.py +18 -0
  49. classiq/interface/hardware.py +2 -0
  50. classiq/interface/helpers/model_normalizer.py +42 -6
  51. classiq/interface/interface_version.py +1 -1
  52. classiq/interface/model/invert.py +8 -0
  53. classiq/interface/model/model.py +19 -0
  54. classiq/interface/model/model_visitor.py +4 -2
  55. classiq/interface/model/quantum_type.py +36 -0
  56. classiq/interface/model/statement_block.py +0 -4
  57. classiq/interface/qubits_mapping/__init__.py +4 -0
  58. classiq/interface/qubits_mapping/path_expr_range.py +69 -0
  59. classiq/interface/qubits_mapping/qubits_mapping.py +231 -0
  60. classiq/interface/qubits_mapping/slices.py +112 -0
  61. classiq/model_expansions/arithmetic.py +6 -0
  62. classiq/model_expansions/capturing/captured_vars.py +16 -12
  63. classiq/model_expansions/function_builder.py +9 -1
  64. classiq/model_expansions/interpreters/base_interpreter.py +9 -8
  65. classiq/model_expansions/interpreters/generative_interpreter.py +9 -24
  66. classiq/model_expansions/quantum_operations/arithmetic/explicit_boolean_expressions.py +1 -0
  67. classiq/model_expansions/quantum_operations/assignment_result_processor.py +132 -28
  68. classiq/model_expansions/quantum_operations/bind.py +4 -0
  69. classiq/model_expansions/quantum_operations/call_emitter.py +5 -35
  70. classiq/model_expansions/quantum_operations/emitter.py +1 -4
  71. classiq/model_expansions/quantum_operations/expression_evaluator.py +0 -3
  72. classiq/model_expansions/visitors/uncomputation_signature_inference.py +0 -9
  73. classiq/qmod/builtins/functions/__init__.py +21 -9
  74. classiq/qmod/builtins/functions/allocation.py +0 -36
  75. classiq/qmod/builtins/functions/arithmetic.py +183 -0
  76. classiq/qmod/builtins/functions/exponentiation.py +32 -2
  77. classiq/qmod/builtins/functions/gray_code.py +23 -0
  78. classiq/qmod/builtins/functions/mcx_func.py +10 -0
  79. classiq/qmod/builtins/operations.py +2 -38
  80. classiq/qmod/builtins/structs.py +22 -3
  81. classiq/qmod/native/pretty_printer.py +1 -12
  82. classiq/qmod/pretty_print/pretty_printer.py +1 -17
  83. classiq/qmod/qmod_parameter.py +4 -0
  84. classiq/qmod/qmod_variable.py +38 -63
  85. classiq/qmod/quantum_function.py +43 -7
  86. classiq/qmod/semantics/validation/function_name_collisions_validation.py +7 -4
  87. classiq/qmod/semantics/validation/model_validation.py +7 -2
  88. classiq/qmod/symbolic_type.py +4 -2
  89. classiq/qprog_to_cudaq.py +347 -0
  90. {classiq-0.102.0.dist-info → classiq-1.0.0.dist-info}/METADATA +4 -1
  91. {classiq-0.102.0.dist-info → classiq-1.0.0.dist-info}/RECORD +93 -76
  92. classiq/interface/generator/amplitude_loading.py +0 -103
  93. classiq/interface/model/quantum_expressions/amplitude_loading_operation.py +0 -77
  94. {classiq-0.102.0.dist-info → classiq-1.0.0.dist-info}/WHEEL +0 -0
  95. {classiq-0.102.0.dist-info → classiq-1.0.0.dist-info}/licenses/LICENSE.txt +0 -0
@@ -0,0 +1,231 @@
1
+ import dataclasses
2
+ import itertools
3
+ from collections.abc import Iterable, Iterator
4
+ from contextlib import contextmanager
5
+ from typing import Any, Generic, TypeGuard, TypeVar, cast
6
+
7
+ from classiq.interface.exceptions import ClassiqInternalError
8
+ from classiq.interface.generator.functions.concrete_types import ConcreteQuantumType
9
+ from classiq.interface.generator.functions.port_declaration import (
10
+ PortDeclarationDirection,
11
+ )
12
+ from classiq.interface.generator.visitor import RetType
13
+ from classiq.interface.model.allocate import Allocate
14
+ from classiq.interface.model.bind_operation import BindOperation
15
+ from classiq.interface.model.handle_binding import ConcreteHandleBinding, HandleBinding
16
+ from classiq.interface.model.model import Model
17
+ from classiq.interface.model.model_visitor import ModelStatementsVisitor
18
+ from classiq.interface.model.native_function_definition import NativeFunctionDefinition
19
+ from classiq.interface.model.quantum_function_call import QuantumFunctionCall
20
+ from classiq.interface.model.quantum_function_declaration import (
21
+ NamedParamsQuantumFunctionDeclaration,
22
+ QuantumFunctionDeclaration,
23
+ )
24
+ from classiq.interface.model.quantum_type import QuantumType
25
+ from classiq.interface.model.variable_declaration_statement import (
26
+ VariableDeclarationStatement,
27
+ )
28
+
29
+ from .path_expr_range import get_path_expr_range
30
+ from .slices import Slices
31
+
32
+
33
+ @dataclasses.dataclass
34
+ class FunctionScope:
35
+ variables: dict[str, ConcreteQuantumType] = dataclasses.field(default_factory=dict)
36
+ allocated_variables: dict[str, Slices] = dataclasses.field(default_factory=dict)
37
+ total_resources: int = dataclasses.field(default=0)
38
+
39
+ def add_new_var(self, var_name: str, quantum_type: ConcreteQuantumType) -> None:
40
+ self.variables[var_name] = quantum_type
41
+
42
+ def allocate_new_var(self, var_name: str, var_size: int) -> None:
43
+ new_total_resources = self.total_resources + var_size
44
+ self.allocated_variables[var_name] = Slices(
45
+ [(self.total_resources, new_total_resources)]
46
+ )
47
+ self.total_resources = new_total_resources
48
+
49
+ def bind_slices_to_var(self, var_name: str, slices: Slices) -> None:
50
+ self.allocated_variables[var_name] = slices
51
+
52
+
53
+ T = TypeVar("T", bound=FunctionScope)
54
+
55
+ FREE_NAME = "free"
56
+
57
+
58
+ class QubitsMapping(Generic[T], ModelStatementsVisitor):
59
+ """
60
+ Visitor that maps quantum variables to virtual qubit slices in a quantum model.
61
+
62
+ This visitor traverses the model's quantum functions and tracks the mapping of
63
+ quantum variables to their virtual qubit allocations. It maintains scopes for each
64
+ function, tracks the call stack, and maps variables to slices representing ranges
65
+ of qubits. This class assumes that the visited model is a compiled qmod, which mainly
66
+ means that all allocations are in the main function, and the model does not contain within_apply.
67
+ """
68
+
69
+ def __init__(self, scope_type: type[FunctionScope] = FunctionScope) -> None:
70
+ super().__init__()
71
+ self._scope_type: type[T] = cast(type[T], scope_type)
72
+ self.scopes: dict[str, T] = {}
73
+ self._main_func_name: str
74
+ self._current_function: str
75
+
76
+ @property
77
+ def _current_scope(self) -> T:
78
+ return self.scopes[self._current_function]
79
+
80
+ @contextmanager
81
+ def _function_scoping(
82
+ self, func_def: NamedParamsQuantumFunctionDeclaration
83
+ ) -> Iterator[T]:
84
+ func_name = func_def.name
85
+ _previous_function = self._current_function
86
+ self._current_function = func_name
87
+ self.scopes[func_name] = self._initialize_function_scope(func_def)
88
+ yield self.scopes[func_name]
89
+ self._current_function = _previous_function
90
+
91
+ def _is_entry_point(self, func_name: str) -> bool:
92
+ return func_name == self._main_func_name
93
+
94
+ def _is_free_function_call(self, call: QuantumFunctionCall) -> bool:
95
+ return call.func_decl.name == FREE_NAME
96
+
97
+ def _is_function_with_definition(
98
+ self, func_decl: QuantumFunctionDeclaration
99
+ ) -> TypeGuard[NativeFunctionDefinition]:
100
+ return isinstance(func_decl, NativeFunctionDefinition)
101
+
102
+ def visit_Model(self, model: Model) -> RetType | None:
103
+ self._main_func_name = model.main_func.name
104
+ self._current_function = self._main_func_name
105
+ self.visit_BaseModel(model)
106
+ return None
107
+
108
+ def visit_NativeFunctionDefinition(
109
+ self, func: NativeFunctionDefinition
110
+ ) -> RetType | None:
111
+ if func.name in self.scopes:
112
+ return None
113
+ with self._function_scoping(func):
114
+ self.visit(func.body)
115
+ return None
116
+
117
+ def visit_Allocate(self, stat: Allocate) -> RetType | None:
118
+ if not self._is_entry_point(self._current_function):
119
+ raise ClassiqInternalError(
120
+ "compiled qmod can't have allocation outside of main function"
121
+ )
122
+ var_name = stat.target.name
123
+ var_type = self._current_scope.variables[var_name]
124
+ self._current_scope.allocate_new_var(stat.target.name, var_type.size_in_bits)
125
+ return None
126
+
127
+ def visit_VariableDeclarationStatement(
128
+ self, stat: VariableDeclarationStatement
129
+ ) -> RetType | None:
130
+ if isinstance(stat.qmod_type, QuantumType):
131
+ self._current_scope.add_new_var(stat.name, stat.qmod_type)
132
+ return None
133
+
134
+ def _visit_free_function_call(self, stat: QuantumFunctionCall) -> None | Any:
135
+ if not self._is_entry_point(self._current_function):
136
+ raise ClassiqInternalError(
137
+ "compiled qmod can't have free outside of main function"
138
+ )
139
+ input_name = stat.inputs[0].name
140
+ self._current_scope.allocated_variables.pop(input_name)
141
+ return None
142
+
143
+ def _visit_quantum_function_call(self, stat: QuantumFunctionCall) -> None | Any:
144
+ if self._is_free_function_call(stat):
145
+ self._visit_free_function_call(stat)
146
+ return None
147
+ func_decl = stat.func_decl
148
+ if not self._is_function_with_definition(func_decl):
149
+ return None
150
+ input_slices = self.get_call_input_slices(stat)
151
+ end_call_scope = self.scopes[stat.func_decl.name]
152
+ for port, handle in zip(func_decl.port_declarations, stat.ports):
153
+ if port.direction == PortDeclarationDirection.Input:
154
+ new_slices = Slices()
155
+ else:
156
+ relative_output = end_call_scope.allocated_variables[port.name]
157
+ new_slices = input_slices.mapping_virtual_slices(relative_output)
158
+ self._update_by_handle(handle, new_slices, port.direction)
159
+ return None
160
+
161
+ def visit_QuantumFunctionCall(self, stat: QuantumFunctionCall) -> RetType | None:
162
+ if self._is_free_function_call(stat):
163
+ self._visit_free_function_call(stat)
164
+ return None
165
+ self._visit_quantum_function_call(stat)
166
+ return None
167
+
168
+ def visit_BindOperation(self, stat: BindOperation) -> RetType | None:
169
+ input_slices = self._handles_to_slices(stat.in_handles)
170
+ for in_handle in stat.in_handles:
171
+ self._current_scope.allocated_variables.pop(in_handle.name)
172
+ start_index, end_index = 0, 0
173
+ for out in stat.out_handles:
174
+ out_var = self._current_scope.variables[out.name]
175
+ end_index += out_var.size_in_bits
176
+ new_slices = input_slices.get_virtual_slice(start_index, end_index)
177
+ self._update_by_handle(out, new_slices, PortDeclarationDirection.Output)
178
+ start_index = end_index
179
+ return None
180
+
181
+ def get_call_input_slices(self, stat: QuantumFunctionCall) -> Slices:
182
+ input_handles = (
183
+ handle
184
+ for inp, handle in zip(stat.func_decl.port_declarations, stat.ports)
185
+ if inp.direction.is_input
186
+ )
187
+ return self._handles_to_slices(input_handles)
188
+
189
+ def _handles_to_slices(self, handles: Iterable[ConcreteHandleBinding]) -> Slices:
190
+ return Slices(
191
+ itertools.chain.from_iterable(
192
+ self._handle_to_slices(handle) for handle in handles
193
+ )
194
+ )
195
+
196
+ def _handle_to_slices(self, handle: HandleBinding) -> Slices:
197
+ quantum_type = self._current_scope.variables[handle.name]
198
+ var_mapping = self._current_scope.allocated_variables[handle.name]
199
+ start, stop = get_path_expr_range(handle, quantum_type)
200
+ return var_mapping.get_virtual_slice(start, stop)
201
+
202
+ def _update_by_handle(
203
+ self,
204
+ handle: HandleBinding,
205
+ new_slices: Slices,
206
+ direction: PortDeclarationDirection,
207
+ ) -> None:
208
+ if direction == PortDeclarationDirection.Input:
209
+ self._current_scope.allocated_variables.pop(handle.name)
210
+ elif direction == PortDeclarationDirection.Output:
211
+ pass
212
+ else:
213
+ quantum_type = self._current_scope.variables[handle.name]
214
+ start, stop = get_path_expr_range(handle, quantum_type)
215
+ var_mapping = self._current_scope.allocated_variables[handle.name].copy()
216
+ var_mapping.update_virtual_slice(start, stop, new_slices)
217
+ new_slices = var_mapping
218
+ self._current_scope.bind_slices_to_var(handle.name, new_slices)
219
+
220
+ def _initialize_function_scope(
221
+ self, func_def: NamedParamsQuantumFunctionDeclaration
222
+ ) -> T:
223
+ function_variables = self._scope_type()
224
+ ports = func_def.port_declarations
225
+ for port in ports:
226
+ function_variables.add_new_var(port.name, port.quantum_type)
227
+ if port.direction != PortDeclarationDirection.Output:
228
+ function_variables.allocate_new_var(
229
+ port.name, port.quantum_type.size_in_bits
230
+ )
231
+ return function_variables
@@ -0,0 +1,112 @@
1
+ from collections import deque
2
+ from collections.abc import Iterable, Reversible
3
+ from itertools import chain
4
+
5
+ from typing_extensions import override
6
+
7
+
8
+ class Slices(deque[tuple[int, int]]):
9
+ """A deque of slice objects that automatically merges adjacent slices.
10
+
11
+ Slices represent a collection of non-overlapping, potentially non-contiguous
12
+ slice ranges. When slices are appended or prepended, adjacent slices are
13
+ automatically merged to maintain a compact representation. Methods use virtual
14
+ notation, access the elements in the object as if all slices were explicitly
15
+ written out contiguously.
16
+
17
+ The class is primarily used for managing qubit allocations and mappings in
18
+ quantum circuit synthesis, where it tracks which physical qubit ranges
19
+ correspond to logical variable ranges.
20
+ """
21
+
22
+ @override
23
+ def append(self, physical_slice: tuple[int, int]) -> None:
24
+ if self and self[-1][1] == physical_slice[0]:
25
+ last_physical_slice = self.pop()
26
+ new_physical_slice = (last_physical_slice[0], physical_slice[1])
27
+ else:
28
+ new_physical_slice = physical_slice
29
+ super().append(new_physical_slice)
30
+
31
+ @override
32
+ def appendleft(self, physical_slice: tuple[int, int]) -> None:
33
+ if self and self[0][0] == physical_slice[1]:
34
+ first_physical_slice = self.popleft()
35
+ new_physical_slice = (physical_slice[0], first_physical_slice[1])
36
+ else:
37
+ new_physical_slice = physical_slice
38
+ super().appendleft(new_physical_slice)
39
+
40
+ def _multiple_appendleft(
41
+ self, physical_slices: Reversible[tuple[int, int]]
42
+ ) -> None:
43
+ for physical_slice in reversed(physical_slices):
44
+ self.appendleft(physical_slice)
45
+
46
+ @override
47
+ def extend(self, physical_slices: Iterable[tuple[int, int]]) -> None:
48
+ for physical_slice in physical_slices:
49
+ self.append(physical_slice)
50
+
51
+ def pop_prefix_virtual_slice(self, virtual_end: int) -> "Slices":
52
+ result = Slices()
53
+ current_virtual_end, result_physical_end, physical_slice_end = 0, 0, 0
54
+ while current_virtual_end < virtual_end:
55
+ physical_slice_start, physical_slice_end = self.popleft()
56
+ current_virtual_end += physical_slice_end - physical_slice_start
57
+ overlap_virtual_end = min(virtual_end, current_virtual_end)
58
+ result_physical_end = physical_slice_end + (
59
+ overlap_virtual_end - current_virtual_end
60
+ )
61
+ result.append((physical_slice_start, result_physical_end))
62
+ if result_physical_end != physical_slice_end:
63
+ self.appendleft((result_physical_end, physical_slice_end))
64
+ return result
65
+
66
+ def get_virtual_slice(self, virtual_start: int, virtual_end: int) -> "Slices":
67
+ result = Slices()
68
+ current_virtual_start = 0
69
+ for physical_slice in self:
70
+ physical_slice_start, physical_slice_end = physical_slice
71
+ current_virtual_end = current_virtual_start + (
72
+ physical_slice_end - physical_slice_start
73
+ )
74
+ overlap_virtual_start = max(virtual_start, current_virtual_start)
75
+ overlap_virtual_end = min(virtual_end, current_virtual_end)
76
+ if overlap_virtual_start < overlap_virtual_end:
77
+ new_physical_start = physical_slice_start + (
78
+ overlap_virtual_start - current_virtual_start
79
+ )
80
+ new_physical_end = physical_slice_end + (
81
+ overlap_virtual_end - current_virtual_end
82
+ )
83
+ result.append((new_physical_start, new_physical_end))
84
+ if current_virtual_end >= virtual_end:
85
+ break
86
+ current_virtual_start = current_virtual_end
87
+ return result
88
+
89
+ def update_virtual_slice(
90
+ self, virtual_start: int, virtual_end: int, new: "Slices"
91
+ ) -> None:
92
+ start = self.pop_prefix_virtual_slice(virtual_start)
93
+ self.pop_prefix_virtual_slice(virtual_end - virtual_start)
94
+ self._multiple_appendleft(new)
95
+ self._multiple_appendleft(start)
96
+
97
+ def mapping_virtual_slices(self, virtual_slices: "Slices") -> "Slices":
98
+ mappings = Slices()
99
+ for virtual_slice in virtual_slices:
100
+ virtual_start, virtual_end = virtual_slice
101
+ for mapped_slice in self.get_virtual_slice(virtual_start, virtual_end):
102
+ mappings.append(mapped_slice)
103
+ return mappings
104
+
105
+ def size(self) -> int:
106
+ return sum(_slice[1] - _slice[0] for _slice in self)
107
+
108
+ @property
109
+ def indices(self) -> tuple[int, ...]:
110
+ return tuple(
111
+ chain.from_iterable(range(_slice[0], _slice[1]) for _slice in self)
112
+ )
@@ -88,6 +88,12 @@ class NumericAttributes:
88
88
  return self.lb
89
89
  return None
90
90
 
91
+ def fits_in(self, other: "NumericAttributes") -> bool:
92
+ return (
93
+ self.integer_digits <= other.integer_digits
94
+ and self.fraction_digits <= other.fraction_digits
95
+ )
96
+
91
97
  @classmethod
92
98
  def from_bounds(
93
99
  cls,
@@ -56,8 +56,8 @@ if TYPE_CHECKING:
56
56
  from classiq.model_expansions.closure import FunctionClosure
57
57
 
58
58
 
59
- INITIALIZED_VAR_MESSAGE = "Variable '{}' should be uninitialized here"
60
- UNINITIALIZED_VAR_MESSAGE = "Variable '{}' should be initialized here"
59
+ _INITIALIZED_VAR_MESSAGE = "Variable '{}' should be uninitialized here"
60
+ _UNINITIALIZED_VAR_MESSAGE = "Variable '{}' should be initialized here"
61
61
 
62
62
 
63
63
  def _get_symbol_expr(symbol: str, classical_type: ClassicalType) -> Expression:
@@ -311,7 +311,7 @@ class CapturedVars:
311
311
  return PortDirection.Inout
312
312
  if target_direction == PortDirection.Outin:
313
313
  return PortDirection.Input
314
- raise ClassiqExpansionError(UNINITIALIZED_VAR_MESSAGE.format(var_name))
314
+ raise ClassiqExpansionError(_UNINITIALIZED_VAR_MESSAGE.format(var_name))
315
315
 
316
316
  if source_direction == PortDirection.Output:
317
317
  if target_direction == PortDirection.Input:
@@ -320,18 +320,18 @@ class CapturedVars:
320
320
  PortDirection.Output,
321
321
  PortDirection.Outin,
322
322
  ):
323
- raise ClassiqExpansionError(INITIALIZED_VAR_MESSAGE.format(var_name))
323
+ raise ClassiqExpansionError(_INITIALIZED_VAR_MESSAGE.format(var_name))
324
324
  return PortDirection.Output
325
325
 
326
326
  if source_direction == PortDirection.Inout:
327
327
  if target_direction in (PortDirection.Input, PortDirection.Inout):
328
328
  return target_direction
329
- raise ClassiqExpansionError(INITIALIZED_VAR_MESSAGE.format(var_name))
329
+ raise ClassiqExpansionError(_INITIALIZED_VAR_MESSAGE.format(var_name))
330
330
 
331
331
  if source_direction == PortDirection.Outin:
332
332
  if target_direction in (PortDirection.Output, PortDirection.Outin):
333
333
  return target_direction
334
- raise ClassiqExpansionError(UNINITIALIZED_VAR_MESSAGE.format(var_name))
334
+ raise ClassiqExpansionError(_UNINITIALIZED_VAR_MESSAGE.format(var_name))
335
335
 
336
336
  raise ClassiqInternalExpansionError(f"Unexpected direction {source_direction}")
337
337
 
@@ -346,7 +346,7 @@ class CapturedVars:
346
346
  PortDirection.Outin,
347
347
  ):
348
348
  raise ClassiqExpansionError(
349
- UNINITIALIZED_VAR_MESSAGE.format(captured_handle.handle)
349
+ _UNINITIALIZED_VAR_MESSAGE.format(captured_handle.handle)
350
350
  )
351
351
  return existing_captured_handle
352
352
 
@@ -356,7 +356,7 @@ class CapturedVars:
356
356
  PortDirection.Outin,
357
357
  ):
358
358
  raise ClassiqExpansionError(
359
- INITIALIZED_VAR_MESSAGE.format(captured_handle.handle)
359
+ _INITIALIZED_VAR_MESSAGE.format(captured_handle.handle)
360
360
  )
361
361
  return captured_handle
362
362
 
@@ -705,7 +705,9 @@ class CapturedVars:
705
705
  def set_parent(self, parent: "CapturedVars") -> None:
706
706
  self._handle_states += parent._get_handle_states()
707
707
 
708
- def get_state(self, var_name: str, defining_function: "FunctionClosure") -> bool:
708
+ def get_state(
709
+ self, var_name: str, defining_function: "FunctionClosure"
710
+ ) -> bool | None:
709
711
  for name, func, state in self._handle_states:
710
712
  if name == var_name and _same_closure(func, defining_function):
711
713
  return state
@@ -717,9 +719,7 @@ class CapturedVars:
717
719
  PortDirection.Output,
718
720
  PortDirection.Inout,
719
721
  )
720
- raise ClassiqInternalExpansionError(
721
- f"Cannot find {var_name!r} from {defining_function.name!r}"
722
- )
722
+ return None
723
723
 
724
724
  def clone(self) -> "CapturedVars":
725
725
  return CapturedVars(
@@ -846,6 +846,10 @@ def validate_end_state(func: "FunctionClosure", captured_vars: CapturedVars) ->
846
846
  for param in func.positional_arg_declarations:
847
847
  if isinstance(param, PortDeclaration):
848
848
  state = captured_vars.get_state(param.name, func)
849
+ if state is None:
850
+ raise ClassiqInternalExpansionError(
851
+ f"Cannot find {param.name!r} from {func.name!r}"
852
+ )
849
853
  expected_state = param.direction in (
850
854
  PortDeclarationDirection.Output,
851
855
  PortDeclarationDirection.Inout,
@@ -13,6 +13,8 @@ from classiq.interface.generator.compiler_keywords import (
13
13
  )
14
14
  from classiq.interface.generator.functions.builtins.internal_operators import (
15
15
  BLOCK_OPERATOR_NAME,
16
+ INVERT_OPERATOR_NAMES,
17
+ SINGLE_CALL_INVERT_OPERATOR_NAME,
16
18
  SKIP_CONTROL_OPERATOR_NAME,
17
19
  WITHIN_APPLY_NAME,
18
20
  )
@@ -44,6 +46,7 @@ BLOCKS_ALLOWED_CAPTURING = (
44
46
  WITHIN_APPLY_NAME,
45
47
  BLOCK_OPERATOR_NAME,
46
48
  SKIP_CONTROL_OPERATOR_NAME,
49
+ SINGLE_CALL_INVERT_OPERATOR_NAME,
47
50
  )
48
51
 
49
52
 
@@ -155,6 +158,8 @@ class OperationBuilder:
155
158
  self._operations[-1].blocks[block_name] = block
156
159
  yield
157
160
  captured_vars = self.current_block.captured_vars
161
+ if self.current_operation.name in INVERT_OPERATOR_NAMES:
162
+ captured_vars = captured_vars.negate()
158
163
  if (
159
164
  not isinstance(self.current_operation, FunctionClosure)
160
165
  and self.current_operation.name not in BLOCKS_ALLOWED_CAPTURING
@@ -178,7 +183,10 @@ class OperationBuilder:
178
183
  context.closure.captured_vars.init_params(original_operation)
179
184
  else:
180
185
  context = OperationContext(closure=original_operation)
181
- context.closure.captured_vars.set_parent(self.current_block.captured_vars)
186
+ if context.name != SINGLE_CALL_INVERT_OPERATOR_NAME:
187
+ context.closure.captured_vars.set_parent(
188
+ self.current_block.captured_vars
189
+ )
182
190
  self._operations.append(context)
183
191
  yield context
184
192
  self._finalize_within_apply()
@@ -32,6 +32,7 @@ from classiq.interface.model.handle_binding import (
32
32
  from classiq.interface.model.model import MAIN_FUNCTION_NAME, Model
33
33
  from classiq.interface.model.native_function_definition import NativeFunctionDefinition
34
34
  from classiq.interface.model.quantum_function_declaration import (
35
+ NamedParamsQuantumFunctionDeclaration,
35
36
  QuantumFunctionDeclaration,
36
37
  )
37
38
  from classiq.interface.model.quantum_lambda_function import (
@@ -76,6 +77,7 @@ from classiq.model_expansions.scope_initialization import (
76
77
  from classiq.model_expansions.utils.counted_name_allocator import CountedNameAllocator
77
78
  from classiq.qmod.builtins.constants import __all__ as builtin_constants
78
79
  from classiq.qmod.builtins.enums import BUILTIN_ENUM_DECLARATIONS
80
+ from classiq.qmod.builtins.functions import CORE_LIB_DECLS
79
81
  from classiq.qmod.builtins.structs import BUILTIN_STRUCT_DECLARATIONS
80
82
  from classiq.qmod.model_state_container import QMODULE
81
83
  from classiq.qmod.semantics.annotation.qstruct_annotator import QStructAnnotator
@@ -98,7 +100,7 @@ class BaseInterpreter:
98
100
  skip_type_modifier_validation: bool = False
99
101
 
100
102
  def __init__(self, model: Model) -> None:
101
- validate_model(model)
103
+ validate_model(model, self.get_builtin_functions())
102
104
  self._model = model
103
105
  self._top_level_scope = Scope()
104
106
  self._counted_name_allocator = CountedNameAllocator()
@@ -121,6 +123,9 @@ class BaseInterpreter:
121
123
  self._counted_name_allocator = CountedNameAllocator()
122
124
  self._error_manager: ErrorManager = ErrorManager()
123
125
 
126
+ def get_builtin_functions(self) -> list[NamedParamsQuantumFunctionDeclaration]:
127
+ return CORE_LIB_DECLS
128
+
124
129
  def _expand_main_func(self) -> None:
125
130
  main_closure = self._get_main_closure(
126
131
  self._top_level_scope[MAIN_FUNCTION_NAME].value
@@ -207,15 +212,14 @@ class BaseInterpreter:
207
212
  expression: Expression,
208
213
  *,
209
214
  simplify: bool = False,
210
- treat_qnum_as_float: bool = False,
211
215
  ) -> Evaluated:
212
216
  if expression.is_evaluated():
213
217
  return Evaluated(value=expression.value.value)
214
218
  expr_ast = ast.parse(expression.expr, mode="eval").body
215
- expr_val = self._eval_expr(ast.unparse(expr_ast), treat_qnum_as_float)
219
+ expr_val = self._eval_expr(ast.unparse(expr_ast))
216
220
  if simplify and not expr_val.has_value(expr_val.root):
217
221
  simplified_expr = simplify_qmod_expression(expr_val)
218
- expr_val = self._eval_expr(simplified_expr, treat_qnum_as_float)
222
+ expr_val = self._eval_expr(simplified_expr)
219
223
  if expr_val.has_value(expr_val.root):
220
224
  value = expr_val.get_value(expr_val.root)
221
225
  else:
@@ -223,12 +227,9 @@ class BaseInterpreter:
223
227
 
224
228
  return Evaluated(value=value)
225
229
 
226
- def _eval_expr(
227
- self, expr: str, treat_qnum_as_float: bool
228
- ) -> QmodAnnotatedExpression:
230
+ def _eval_expr(self, expr: str) -> QmodAnnotatedExpression:
229
231
  return evaluate_qmod_expression(
230
232
  expr,
231
- treat_qnum_as_float=treat_qnum_as_float,
232
233
  machine_precision=self._model.preferences.machine_precision,
233
234
  classical_struct_declarations=list(QMODULE.type_decls.values()),
234
235
  enum_declarations=list(QMODULE.enum_decls.values()),
@@ -9,10 +9,11 @@ from classiq.interface.generator.expressions.expression import Expression
9
9
  from classiq.interface.generator.functions.builtins.internal_operators import (
10
10
  BLOCK_OPERATOR_NAME,
11
11
  CLASSICAL_IF_OPERATOR_NAME,
12
+ COMPOUND_INVERT_OPERATOR_NAME,
12
13
  CONTROL_OPERATOR_NAME,
13
- INVERT_OPERATOR_NAME,
14
14
  POWER_OPERATOR_NAME,
15
15
  REPEAT_OPERATOR_NAME,
16
+ SINGLE_CALL_INVERT_OPERATOR_NAME,
16
17
  SKIP_CONTROL_OPERATOR_NAME,
17
18
  WITHIN_APPLY_NAME,
18
19
  )
@@ -23,14 +24,11 @@ from classiq.interface.model.bounds import SetBoundsStatement
23
24
  from classiq.interface.model.classical_if import ClassicalIf
24
25
  from classiq.interface.model.control import Control
25
26
  from classiq.interface.model.inplace_binary_operation import InplaceBinaryOperation
26
- from classiq.interface.model.invert import Invert
27
+ from classiq.interface.model.invert import BlockKind, Invert
27
28
  from classiq.interface.model.model import Model
28
29
  from classiq.interface.model.native_function_definition import NativeFunctionDefinition
29
30
  from classiq.interface.model.phase_operation import PhaseOperation
30
31
  from classiq.interface.model.power import Power
31
- from classiq.interface.model.quantum_expressions.amplitude_loading_operation import (
32
- AmplitudeLoadingOperation,
33
- )
34
32
  from classiq.interface.model.quantum_expressions.arithmetic_operation import (
35
33
  ArithmeticOperation,
36
34
  )
@@ -199,24 +197,6 @@ class GenerativeInterpreter(BaseInterpreter):
199
197
  bind
200
198
  )
201
199
 
202
- @emit.register
203
- def emit_amplitude_loading_operation(self, op: AmplitudeLoadingOperation) -> None:
204
- CompositeEmitter[AmplitudeLoadingOperation](
205
- self,
206
- [
207
- HandleEvaluator(self, "result_var"),
208
- ExpressionEvaluator(
209
- self,
210
- "expression",
211
- readable_expression_name="amplitude-encoding expression",
212
- simplify=True,
213
- treat_qnum_as_float=True,
214
- allow_runtime_vars=self._symbolic_parameters_switch,
215
- ),
216
- AssignmentResultProcessor(self),
217
- ],
218
- ).emit(op)
219
-
220
200
  @emit.register
221
201
  def _emit_arithmetic_operation(self, op: ArithmeticOperation) -> None:
222
202
  self.emit_arithmetic_operation(op)
@@ -285,7 +265,12 @@ class GenerativeInterpreter(BaseInterpreter):
285
265
 
286
266
  @emit.register
287
267
  def emit_invert(self, invert: Invert) -> None:
288
- BlockEvaluator(self, INVERT_OPERATOR_NAME, "body").emit(invert)
268
+ match invert.block_kind:
269
+ case BlockKind.SingleCall:
270
+ op_name = SINGLE_CALL_INVERT_OPERATOR_NAME
271
+ case BlockKind.Compound:
272
+ op_name = COMPOUND_INVERT_OPERATOR_NAME
273
+ BlockEvaluator(self, op_name, "body").emit(invert)
289
274
 
290
275
  @emit.register
291
276
  def emit_skip_control(self, skip_control: SkipControl) -> None:
@@ -50,6 +50,7 @@ def convert_inplace_op_bool_expression(
50
50
  def _supported_types() -> tuple[str, ...]:
51
51
  return (
52
52
  QuantumBit().qmod_type_name,
53
+ QuantumNumeric().qmod_type_name,
53
54
  QuantumNumeric(
54
55
  size=Expression(expr="1"),
55
56
  is_signed=Expression(expr="False"),