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.
- classiq/__init__.py +2 -0
- classiq/_internals/authentication/auth0.py +29 -0
- classiq/_internals/authentication/auth_flow_factory.py +43 -0
- classiq/_internals/authentication/machine_credentials_flow.py +26 -0
- classiq/_internals/authentication/password_manager.py +84 -0
- classiq/_internals/authentication/token_manager.py +24 -8
- classiq/analyzer/show_interactive_hack.py +0 -8
- classiq/applications/chemistry/op_utils.py +32 -0
- classiq/applications/combinatorial_optimization/combinatorial_problem.py +1 -1
- classiq/evaluators/qmod_annotated_expression.py +1 -1
- classiq/evaluators/qmod_expression_visitors/qmod_expression_evaluator.py +1 -8
- classiq/evaluators/qmod_expression_visitors/qmod_expression_simplifier.py +1 -1
- classiq/evaluators/qmod_node_evaluators/attribute_evaluation.py +2 -2
- classiq/evaluators/qmod_node_evaluators/binary_op_evaluation.py +18 -29
- classiq/evaluators/qmod_node_evaluators/min_max_evaluation.py +1 -6
- classiq/evaluators/qmod_node_evaluators/numeric_attrs_utils.py +1 -7
- classiq/evaluators/qmod_type_inference/quantum_type_comparison.py +52 -0
- classiq/execution/all_hardware_devices.py +59 -1
- classiq/execution/execution_session.py +1 -1
- classiq/execution/functions/__init__.py +13 -0
- classiq/execution/functions/expectation_value.py +106 -0
- classiq/execution/functions/minimize.py +90 -0
- classiq/execution/functions/sample.py +76 -0
- classiq/execution/functions/state_vector.py +113 -0
- classiq/execution/functions/util/__init__.py +0 -0
- classiq/execution/functions/util/_logging.py +19 -0
- classiq/execution/functions/util/backend_preferences.py +188 -0
- classiq/execution/functions/util/constants.py +9 -0
- classiq/execution/functions/util/parse_provider_backend.py +90 -0
- classiq/interface/_version.py +1 -1
- classiq/interface/backend/backend_preferences.py +81 -0
- classiq/interface/backend/provider_config/providers/aqt.py +1 -1
- classiq/interface/backend/provider_config/providers/azure.py +1 -2
- classiq/interface/backend/provider_config/providers/ibm.py +1 -1
- classiq/interface/backend/quantum_backend_providers.py +14 -0
- classiq/interface/exceptions.py +0 -4
- classiq/interface/executor/result.py +9 -5
- classiq/interface/generator/arith/binary_ops.py +62 -2
- classiq/interface/generator/arith/number_utils.py +15 -6
- classiq/interface/generator/compiler_keywords.py +1 -0
- classiq/interface/generator/function_param_list.py +8 -2
- classiq/interface/generator/function_params.py +1 -1
- classiq/interface/generator/functions/builtins/internal_operators.py +5 -9
- classiq/interface/generator/functions/classical_type.py +60 -0
- classiq/interface/generator/functions/type_name.py +36 -0
- classiq/interface/generator/generated_circuit_data.py +0 -2
- classiq/interface/generator/transpiler_basis_gates.py +1 -0
- classiq/interface/generator/types/compilation_metadata.py +18 -0
- classiq/interface/hardware.py +2 -0
- classiq/interface/helpers/model_normalizer.py +42 -6
- classiq/interface/interface_version.py +1 -1
- classiq/interface/model/invert.py +8 -0
- classiq/interface/model/model.py +19 -0
- classiq/interface/model/model_visitor.py +4 -2
- classiq/interface/model/quantum_type.py +36 -0
- classiq/interface/model/statement_block.py +0 -4
- classiq/interface/qubits_mapping/__init__.py +4 -0
- classiq/interface/qubits_mapping/path_expr_range.py +69 -0
- classiq/interface/qubits_mapping/qubits_mapping.py +231 -0
- classiq/interface/qubits_mapping/slices.py +112 -0
- classiq/model_expansions/arithmetic.py +6 -0
- classiq/model_expansions/capturing/captured_vars.py +16 -12
- classiq/model_expansions/function_builder.py +9 -1
- classiq/model_expansions/interpreters/base_interpreter.py +9 -8
- classiq/model_expansions/interpreters/generative_interpreter.py +9 -24
- classiq/model_expansions/quantum_operations/arithmetic/explicit_boolean_expressions.py +1 -0
- classiq/model_expansions/quantum_operations/assignment_result_processor.py +132 -28
- classiq/model_expansions/quantum_operations/bind.py +4 -0
- classiq/model_expansions/quantum_operations/call_emitter.py +5 -35
- classiq/model_expansions/quantum_operations/emitter.py +1 -4
- classiq/model_expansions/quantum_operations/expression_evaluator.py +0 -3
- classiq/model_expansions/visitors/uncomputation_signature_inference.py +0 -9
- classiq/qmod/builtins/functions/__init__.py +21 -9
- classiq/qmod/builtins/functions/allocation.py +0 -36
- classiq/qmod/builtins/functions/arithmetic.py +183 -0
- classiq/qmod/builtins/functions/exponentiation.py +32 -2
- classiq/qmod/builtins/functions/gray_code.py +23 -0
- classiq/qmod/builtins/functions/mcx_func.py +10 -0
- classiq/qmod/builtins/operations.py +2 -38
- classiq/qmod/builtins/structs.py +22 -3
- classiq/qmod/native/pretty_printer.py +1 -12
- classiq/qmod/pretty_print/pretty_printer.py +1 -17
- classiq/qmod/qmod_parameter.py +4 -0
- classiq/qmod/qmod_variable.py +38 -63
- classiq/qmod/quantum_function.py +43 -7
- classiq/qmod/semantics/validation/function_name_collisions_validation.py +7 -4
- classiq/qmod/semantics/validation/model_validation.py +7 -2
- classiq/qmod/symbolic_type.py +4 -2
- classiq/qprog_to_cudaq.py +347 -0
- {classiq-0.102.0.dist-info → classiq-1.0.0.dist-info}/METADATA +4 -1
- {classiq-0.102.0.dist-info → classiq-1.0.0.dist-info}/RECORD +93 -76
- classiq/interface/generator/amplitude_loading.py +0 -103
- classiq/interface/model/quantum_expressions/amplitude_loading_operation.py +0 -77
- {classiq-0.102.0.dist-info → classiq-1.0.0.dist-info}/WHEEL +0 -0
- {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
|
-
|
|
60
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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.
|
|
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)
|
|
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
|
|
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
|
-
|
|
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:
|