classiq 0.66.0__py3-none-any.whl → 0.67.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- classiq/applications/finance/finance_model_constructor.py +9 -0
- classiq/applications/grover/grover_model_constructor.py +10 -0
- classiq/applications/qnn/qlayer.py +8 -2
- classiq/applications/qsvm/qsvm_model_constructor.py +9 -0
- classiq/interface/_version.py +1 -1
- classiq/interface/debug_info/debug_info.py +12 -0
- classiq/interface/exceptions.py +2 -5
- classiq/interface/generator/arith/argument_utils.py +1 -1
- classiq/interface/generator/arith/arithmetic.py +3 -1
- classiq/interface/generator/arith/binary_ops.py +3 -0
- classiq/interface/generator/function_param_list_without_self_reference.py +2 -0
- classiq/interface/generator/functions/type_name.py +2 -2
- classiq/interface/generator/generated_circuit_data.py +34 -1
- classiq/interface/generator/hardware_efficient_ansatz.py +1 -1
- classiq/interface/generator/hva.py +1 -1
- classiq/interface/generator/model/preferences/preferences.py +8 -1
- classiq/interface/generator/reset.py +14 -0
- classiq/interface/generator/ucc.py +1 -1
- classiq/interface/interface_version.py +1 -1
- classiq/interface/model/quantum_statement.py +13 -0
- classiq/model_expansions/atomic_expression_functions_defs.py +2 -1
- classiq/model_expansions/capturing/captured_vars.py +184 -54
- classiq/model_expansions/closure.py +6 -3
- classiq/model_expansions/evaluators/control.py +14 -38
- classiq/model_expansions/function_builder.py +19 -14
- classiq/model_expansions/generative_functions.py +7 -11
- classiq/model_expansions/interpreters/base_interpreter.py +14 -5
- classiq/model_expansions/interpreters/generative_interpreter.py +9 -8
- classiq/model_expansions/quantum_operations/allocate.py +6 -2
- classiq/model_expansions/quantum_operations/bind.py +65 -13
- classiq/model_expansions/quantum_operations/call_emitter.py +79 -10
- classiq/model_expansions/quantum_operations/classicalif.py +5 -2
- classiq/model_expansions/quantum_operations/emitter.py +8 -1
- classiq/model_expansions/quantum_operations/repeat.py +7 -2
- classiq/model_expansions/quantum_operations/shallow_emitter.py +1 -1
- classiq/model_expansions/quantum_operations/variable_decleration.py +11 -1
- classiq/open_library/functions/__init__.py +8 -0
- classiq/open_library/functions/amplitude_amplification.py +92 -0
- classiq/open_library/functions/grover.py +5 -5
- classiq/qmod/builtins/functions/__init__.py +3 -0
- classiq/qmod/builtins/functions/mid_circuit_measurement.py +15 -0
- classiq/qmod/quantum_function.py +4 -0
- classiq/qmod/semantics/annotation/call_annotation.py +8 -2
- classiq/qmod/semantics/annotation/model_annotation.py +9 -0
- classiq/qmod/semantics/error_manager.py +0 -6
- classiq/qmod/semantics/static_semantics_visitor.py +0 -347
- {classiq-0.66.0.dist-info → classiq-0.67.0.dist-info}/METADATA +1 -1
- {classiq-0.66.0.dist-info → classiq-0.67.0.dist-info}/RECORD +49 -47
- classiq/qmod/semantics/validation/func_call_validation.py +0 -99
- classiq/qmod/semantics/validation/handle_validation.py +0 -85
- {classiq-0.66.0.dist-info → classiq-0.67.0.dist-info}/WHEEL +0 -0
@@ -1,7 +1,7 @@
|
|
1
1
|
import dataclasses
|
2
2
|
from collections.abc import Sequence
|
3
3
|
from dataclasses import dataclass, field
|
4
|
-
from typing import TYPE_CHECKING
|
4
|
+
from typing import TYPE_CHECKING
|
5
5
|
|
6
6
|
from classiq.interface.enum_utils import StrEnum
|
7
7
|
from classiq.interface.exceptions import (
|
@@ -34,8 +34,8 @@ if TYPE_CHECKING:
|
|
34
34
|
from classiq.model_expansions.closure import FunctionClosure
|
35
35
|
|
36
36
|
|
37
|
-
|
38
|
-
|
37
|
+
INITIALIZED_VAR_MESSAGE = "Variable '{}' should be uninitialized here"
|
38
|
+
UNINITIALIZED_VAR_MESSAGE = "Variable '{}' should be initialized here"
|
39
39
|
|
40
40
|
|
41
41
|
class PortDirection(StrEnum):
|
@@ -103,6 +103,11 @@ class _CapturedHandle:
|
|
103
103
|
def change_direction(self, new_direction: PortDirection) -> "_CapturedHandle":
|
104
104
|
return dataclasses.replace(self, direction=new_direction)
|
105
105
|
|
106
|
+
def change_defining_function(
|
107
|
+
self, new_defining_function: "FunctionClosure"
|
108
|
+
) -> "_CapturedHandle":
|
109
|
+
return dataclasses.replace(self, defining_function=new_defining_function)
|
110
|
+
|
106
111
|
def set_propagated(self) -> "_CapturedHandle":
|
107
112
|
return dataclasses.replace(self, is_propagated=True)
|
108
113
|
|
@@ -119,9 +124,13 @@ class _CapturedHandle:
|
|
119
124
|
return dataclasses.replace(self, handle=handle, quantum_type=quantum_type)
|
120
125
|
|
121
126
|
|
127
|
+
HandleState = tuple[str, "FunctionClosure", bool]
|
128
|
+
|
129
|
+
|
122
130
|
@dataclass
|
123
131
|
class CapturedVars:
|
124
132
|
_captured_handles: list[_CapturedHandle] = field(default_factory=list)
|
133
|
+
_handle_states: list[HandleState] = field(default_factory=list)
|
125
134
|
|
126
135
|
def capture_handle(
|
127
136
|
self,
|
@@ -151,9 +160,25 @@ class CapturedVars:
|
|
151
160
|
else "allocate"
|
152
161
|
)
|
153
162
|
raise ClassiqExpansionError(
|
154
|
-
f"Cannot
|
163
|
+
f"Cannot {verb} partial variable {str(captured_handle.handle)!r}"
|
155
164
|
)
|
156
165
|
|
166
|
+
# A handle should be in either _captured_handles or _handle_states, but never
|
167
|
+
# both
|
168
|
+
|
169
|
+
new_handle_states = []
|
170
|
+
for var_name, defining_function, handle_state in self._handle_states:
|
171
|
+
if captured_handle.handle.name == var_name and _same_closure(
|
172
|
+
captured_handle.defining_function, defining_function
|
173
|
+
):
|
174
|
+
# verify variable state
|
175
|
+
self._conjugate_direction(
|
176
|
+
handle_state, captured_handle.direction, var_name
|
177
|
+
)
|
178
|
+
else:
|
179
|
+
new_handle_states.append((var_name, defining_function, handle_state))
|
180
|
+
self._handle_states = new_handle_states
|
181
|
+
|
157
182
|
new_captured_handles = []
|
158
183
|
for existing_captured_handle in self._captured_handles:
|
159
184
|
if not existing_captured_handle.is_same_var(captured_handle):
|
@@ -163,8 +188,12 @@ class CapturedVars:
|
|
163
188
|
existing_captured_handle
|
164
189
|
)
|
165
190
|
if existing_captured_handle.handle == captured_handle.handle:
|
166
|
-
captured_handle =
|
167
|
-
|
191
|
+
captured_handle = captured_handle.change_direction(
|
192
|
+
self._conjugate_direction(
|
193
|
+
existing_captured_handle.direction,
|
194
|
+
captured_handle.direction,
|
195
|
+
str(captured_handle.handle),
|
196
|
+
)
|
168
197
|
)
|
169
198
|
elif captured_handle.handle.overlaps(existing_captured_handle.handle):
|
170
199
|
captured_handle = self._intersect_handles(
|
@@ -177,44 +206,43 @@ class CapturedVars:
|
|
177
206
|
|
178
207
|
def _conjugate_direction(
|
179
208
|
self,
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
return captured_handle.change_direction(PortDirection.Input)
|
188
|
-
raise ClassiqExpansionError(
|
189
|
-
ALREADY_FREED_MESSAGE.format(captured_handle.handle)
|
209
|
+
source_direction: PortDirection | bool,
|
210
|
+
target_direction: PortDirection,
|
211
|
+
var_name: str,
|
212
|
+
) -> PortDirection:
|
213
|
+
if isinstance(source_direction, bool):
|
214
|
+
source_direction = (
|
215
|
+
PortDirection.Inout if source_direction else PortDirection.Outin
|
190
216
|
)
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
PortDirection.
|
197
|
-
)
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
if captured_handle.direction in (
|
217
|
+
|
218
|
+
if source_direction == PortDirection.Input:
|
219
|
+
if target_direction == PortDirection.Output:
|
220
|
+
return PortDirection.Inout
|
221
|
+
if target_direction == PortDirection.Outin:
|
222
|
+
return PortDirection.Input
|
223
|
+
raise ClassiqExpansionError(UNINITIALIZED_VAR_MESSAGE.format(var_name))
|
224
|
+
|
225
|
+
if source_direction == PortDirection.Output:
|
226
|
+
if target_direction == PortDirection.Input:
|
227
|
+
return PortDirection.Outin
|
228
|
+
if target_direction in (
|
204
229
|
PortDirection.Output,
|
205
230
|
PortDirection.Outin,
|
206
231
|
):
|
207
|
-
raise ClassiqExpansionError(
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
PortDirection.Input,
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
)
|
217
|
-
|
232
|
+
raise ClassiqExpansionError(INITIALIZED_VAR_MESSAGE.format(var_name))
|
233
|
+
return PortDirection.Output
|
234
|
+
|
235
|
+
if source_direction == PortDirection.Inout:
|
236
|
+
if target_direction in (PortDirection.Input, PortDirection.Inout):
|
237
|
+
return target_direction
|
238
|
+
raise ClassiqExpansionError(INITIALIZED_VAR_MESSAGE.format(var_name))
|
239
|
+
|
240
|
+
if source_direction == PortDirection.Outin:
|
241
|
+
if target_direction in (PortDirection.Output, PortDirection.Outin):
|
242
|
+
return target_direction
|
243
|
+
raise ClassiqExpansionError(UNINITIALIZED_VAR_MESSAGE.format(var_name))
|
244
|
+
|
245
|
+
raise ClassiqInternalExpansionError(f"Unexpected direction {source_direction}")
|
218
246
|
|
219
247
|
def _intersect_handles(
|
220
248
|
self,
|
@@ -227,7 +255,7 @@ class CapturedVars:
|
|
227
255
|
PortDirection.Outin,
|
228
256
|
):
|
229
257
|
raise ClassiqExpansionError(
|
230
|
-
|
258
|
+
UNINITIALIZED_VAR_MESSAGE.format(captured_handle.handle)
|
231
259
|
)
|
232
260
|
return existing_captured_handle
|
233
261
|
|
@@ -237,7 +265,7 @@ class CapturedVars:
|
|
237
265
|
PortDirection.Outin,
|
238
266
|
):
|
239
267
|
raise ClassiqExpansionError(
|
240
|
-
|
268
|
+
INITIALIZED_VAR_MESSAGE.format(captured_handle.handle)
|
241
269
|
)
|
242
270
|
return captured_handle
|
243
271
|
|
@@ -316,16 +344,7 @@ class CapturedVars:
|
|
316
344
|
]
|
317
345
|
)
|
318
346
|
|
319
|
-
def filter_vars(
|
320
|
-
self,
|
321
|
-
current_function: "FunctionClosure",
|
322
|
-
current_declarations: Optional[list[VariableDeclarationStatement]] = None,
|
323
|
-
) -> "CapturedVars":
|
324
|
-
current_declared_vars = (
|
325
|
-
None
|
326
|
-
if current_declarations is None
|
327
|
-
else {decl.name for decl in current_declarations}
|
328
|
-
)
|
347
|
+
def filter_vars(self, current_function: "FunctionClosure") -> "CapturedVars":
|
329
348
|
return CapturedVars(
|
330
349
|
_captured_handles=[
|
331
350
|
captured_handle
|
@@ -333,7 +352,18 @@ class CapturedVars:
|
|
333
352
|
if not _same_closure(
|
334
353
|
captured_handle.defining_function, current_function
|
335
354
|
)
|
336
|
-
|
355
|
+
]
|
356
|
+
)
|
357
|
+
|
358
|
+
def filter_var_decls(
|
359
|
+
self, current_declarations: list[VariableDeclarationStatement]
|
360
|
+
) -> "CapturedVars":
|
361
|
+
current_declared_vars = {decl.name for decl in current_declarations}
|
362
|
+
return CapturedVars(
|
363
|
+
_captured_handles=[
|
364
|
+
captured_handle
|
365
|
+
for captured_handle in self._captured_handles
|
366
|
+
if (
|
337
367
|
current_declared_vars is not None
|
338
368
|
and captured_handle.handle.name not in current_declared_vars
|
339
369
|
)
|
@@ -376,8 +406,92 @@ class CapturedVars:
|
|
376
406
|
if not captured_handle.is_propagated
|
377
407
|
}
|
378
408
|
|
409
|
+
def init_var(self, var_name: str, defining_function: "FunctionClosure") -> None:
|
410
|
+
self._handle_states.append((var_name, defining_function, False))
|
411
|
+
|
412
|
+
def init_params(self, func: "FunctionClosure") -> None:
|
413
|
+
ports = {
|
414
|
+
param.name: param.direction
|
415
|
+
for param in func.positional_arg_declarations
|
416
|
+
if isinstance(param, PortDeclaration)
|
417
|
+
}
|
418
|
+
new_handle_states = [
|
419
|
+
handle_state
|
420
|
+
for handle_state in self._handle_states
|
421
|
+
if handle_state[0] not in ports
|
422
|
+
]
|
423
|
+
for var_name, direction in ports.items():
|
424
|
+
new_handle_states.append(
|
425
|
+
(
|
426
|
+
var_name,
|
427
|
+
func,
|
428
|
+
PortDirection.load(direction)
|
429
|
+
in (PortDirection.Input, PortDirection.Inout),
|
430
|
+
)
|
431
|
+
)
|
432
|
+
self._handle_states = new_handle_states
|
433
|
+
|
434
|
+
def _get_handle_states(self) -> list[HandleState]:
|
435
|
+
return self._handle_states + list(
|
436
|
+
{
|
437
|
+
(
|
438
|
+
captured_handle.handle.name,
|
439
|
+
captured_handle.defining_function.depth,
|
440
|
+
): (
|
441
|
+
captured_handle.handle.name,
|
442
|
+
captured_handle.defining_function,
|
443
|
+
captured_handle.direction
|
444
|
+
in (PortDirection.Output, PortDirection.Inout),
|
445
|
+
)
|
446
|
+
for captured_handle in self._captured_handles
|
447
|
+
}.values()
|
448
|
+
)
|
449
|
+
|
450
|
+
def set_parent(self, parent: "CapturedVars") -> None:
|
451
|
+
self._handle_states += parent._get_handle_states()
|
452
|
+
|
453
|
+
def get_state(self, var_name: str, defining_function: "FunctionClosure") -> bool:
|
454
|
+
for name, func, state in self._handle_states:
|
455
|
+
if name == var_name and _same_closure(func, defining_function):
|
456
|
+
return state
|
457
|
+
for captured_handle in self._captured_handles:
|
458
|
+
if captured_handle.handle.name == var_name and _same_closure(
|
459
|
+
captured_handle.defining_function, defining_function
|
460
|
+
):
|
461
|
+
return captured_handle.direction in (
|
462
|
+
PortDirection.Output,
|
463
|
+
PortDirection.Inout,
|
464
|
+
)
|
465
|
+
raise ClassiqInternalExpansionError(
|
466
|
+
f"Cannot find {var_name!r} from {defining_function.name!r}"
|
467
|
+
)
|
468
|
+
|
379
469
|
def clone(self) -> "CapturedVars":
|
380
|
-
return CapturedVars(
|
470
|
+
return CapturedVars(
|
471
|
+
_captured_handles=list(self._captured_handles),
|
472
|
+
_handle_states=list(self._handle_states),
|
473
|
+
)
|
474
|
+
|
475
|
+
def set(
|
476
|
+
self,
|
477
|
+
other: "CapturedVars",
|
478
|
+
source_func: "FunctionClosure",
|
479
|
+
target_func: "FunctionClosure",
|
480
|
+
) -> None:
|
481
|
+
self._captured_handles = []
|
482
|
+
for captured_handle in other._captured_handles:
|
483
|
+
if _same_closure(captured_handle.defining_function, source_func):
|
484
|
+
self._captured_handles.append(
|
485
|
+
captured_handle.change_defining_function(target_func)
|
486
|
+
)
|
487
|
+
else:
|
488
|
+
self._captured_handles.append(captured_handle)
|
489
|
+
self._handle_states = []
|
490
|
+
for var, defining_function, state in other._handle_states:
|
491
|
+
if _same_closure(defining_function, source_func):
|
492
|
+
self._handle_states.append((var, target_func, state))
|
493
|
+
else:
|
494
|
+
self._handle_states.append((var, defining_function, state))
|
381
495
|
|
382
496
|
|
383
497
|
def _same_closure(closure_1: "FunctionClosure", closure_2: "FunctionClosure") -> bool:
|
@@ -433,3 +547,19 @@ def validate_captured_directions(
|
|
433
547
|
raise ClassiqExpansionError(
|
434
548
|
f"Captured quantum variables {captured_outputs!r} cannot be used as outputs"
|
435
549
|
)
|
550
|
+
|
551
|
+
|
552
|
+
def validate_end_state(func: "FunctionClosure", captured_vars: CapturedVars) -> None:
|
553
|
+
for param in func.positional_arg_declarations:
|
554
|
+
if isinstance(param, PortDeclaration):
|
555
|
+
state = captured_vars.get_state(param.name, func)
|
556
|
+
expected_state = param.direction in (
|
557
|
+
PortDeclarationDirection.Output,
|
558
|
+
PortDeclarationDirection.Inout,
|
559
|
+
)
|
560
|
+
if state != expected_state:
|
561
|
+
status = "initialized" if expected_state else "uninitialized"
|
562
|
+
raise ClassiqExpansionError(
|
563
|
+
f"At the end of function {func.name}, variable {param.name!r} "
|
564
|
+
f"should be {status}"
|
565
|
+
)
|
@@ -83,18 +83,21 @@ class FunctionClosure(Closure):
|
|
83
83
|
scope: Scope,
|
84
84
|
body: Optional[Sequence[QuantumStatement]] = None,
|
85
85
|
positional_arg_declarations: Sequence[PositionalArg] = tuple(),
|
86
|
-
|
86
|
+
lambda_external_vars: Optional[CapturedVars] = None,
|
87
87
|
is_atomic: bool = False,
|
88
88
|
**kwargs: Any,
|
89
89
|
) -> Self:
|
90
90
|
blocks = {"body": body} if body is not None else {}
|
91
|
+
captured_vars = CapturedVars()
|
92
|
+
if lambda_external_vars is not None:
|
93
|
+
captured_vars.set_parent(lambda_external_vars)
|
91
94
|
return cls(
|
92
95
|
name,
|
93
96
|
blocks,
|
94
97
|
scope,
|
95
98
|
positional_arg_declarations,
|
96
|
-
|
97
|
-
|
99
|
+
captured_vars,
|
100
|
+
lambda_external_vars is not None,
|
98
101
|
is_atomic,
|
99
102
|
**kwargs,
|
100
103
|
)
|
@@ -4,6 +4,10 @@ from sympy import Equality
|
|
4
4
|
from sympy.core.numbers import Number
|
5
5
|
|
6
6
|
from classiq.interface.exceptions import ClassiqExpansionError
|
7
|
+
from classiq.interface.generator.arith.argument_utils import (
|
8
|
+
unsigned_integer_interpretation,
|
9
|
+
)
|
10
|
+
from classiq.interface.generator.arith.register_user_input import RegisterArithmeticInfo
|
7
11
|
from classiq.interface.generator.expressions.qmod_qscalar_proxy import (
|
8
12
|
QmodQNumProxy,
|
9
13
|
QmodQScalarProxy,
|
@@ -31,14 +35,15 @@ def resolve_num_condition(condition: Equality) -> tuple[QmodSizedProxy, str]:
|
|
31
35
|
def _calculate_ctrl_state(ctrl: QmodSizedProxy, ctrl_val: float) -> str:
|
32
36
|
is_signed, fraction_places = _get_numeric_attributes(ctrl)
|
33
37
|
|
34
|
-
|
35
|
-
|
36
|
-
_validate_control_value_sign(ctrl, integer_ctrl_val, is_signed)
|
37
|
-
_validate_control_var_qubits(
|
38
|
-
ctrl, integer_ctrl_val, is_signed, fraction_places, ctrl_val
|
38
|
+
reg = RegisterArithmeticInfo(
|
39
|
+
size=ctrl.size, is_signed=is_signed, fraction_places=fraction_places
|
39
40
|
)
|
41
|
+
uint_ctrl_val = unsigned_integer_interpretation(ctrl_val, reg)
|
40
42
|
|
41
|
-
|
43
|
+
_validate_control_value_sign(ctrl, ctrl_val, is_signed)
|
44
|
+
_validate_control_var_qubits(ctrl, uint_ctrl_val, fraction_places, ctrl_val)
|
45
|
+
|
46
|
+
return _to_twos_complement(uint_ctrl_val, ctrl.size)
|
42
47
|
|
43
48
|
|
44
49
|
def _get_numeric_attributes(ctrl: QmodSizedProxy) -> tuple[bool, int]:
|
@@ -49,20 +54,8 @@ def _get_numeric_attributes(ctrl: QmodSizedProxy) -> tuple[bool, int]:
|
|
49
54
|
)
|
50
55
|
|
51
56
|
|
52
|
-
def _get_integer_ctrl_val(
|
53
|
-
ctrl: QmodSizedProxy, ctrl_val: float, fraction_places: int
|
54
|
-
) -> int:
|
55
|
-
unfractioned_ctrl_val = ctrl_val * 2**fraction_places
|
56
|
-
if unfractioned_ctrl_val != int(unfractioned_ctrl_val):
|
57
|
-
raise ClassiqExpansionError(
|
58
|
-
f"Variable {str(ctrl)!r} doesne't have enough fraction digits to "
|
59
|
-
f"represent control value {ctrl_val}"
|
60
|
-
)
|
61
|
-
return int(unfractioned_ctrl_val)
|
62
|
-
|
63
|
-
|
64
57
|
def _validate_control_value_sign(
|
65
|
-
ctrl: QmodSizedProxy, ctrl_val:
|
58
|
+
ctrl: QmodSizedProxy, ctrl_val: float, is_signed: bool
|
66
59
|
) -> None:
|
67
60
|
if not is_signed and ctrl_val < 0:
|
68
61
|
raise ClassiqExpansionError(
|
@@ -74,18 +67,17 @@ def _validate_control_value_sign(
|
|
74
67
|
def _validate_control_var_qubits(
|
75
68
|
ctrl: QmodSizedProxy,
|
76
69
|
ctrl_val: int,
|
77
|
-
is_signed: bool,
|
78
70
|
fraction_places: int,
|
79
71
|
orig_ctrl_val: float,
|
80
72
|
) -> None:
|
81
|
-
required_qubits =
|
73
|
+
required_qubits = _min_unsigned_bit_length(ctrl_val)
|
82
74
|
fraction_places_message = (
|
83
75
|
f" with {fraction_places} fraction digits" if fraction_places else ""
|
84
76
|
)
|
85
77
|
if ctrl.size < required_qubits:
|
86
78
|
raise ClassiqExpansionError(
|
87
79
|
f"Variable {str(ctrl)!r} has {ctrl.size} qubits{fraction_places_message} but control value "
|
88
|
-
f"{str(orig_ctrl_val if fraction_places else
|
80
|
+
f"{str(orig_ctrl_val if fraction_places else int(orig_ctrl_val))!r} requires at least {required_qubits} qubits{fraction_places_message}"
|
89
81
|
)
|
90
82
|
|
91
83
|
|
@@ -116,22 +108,6 @@ def _min_unsigned_bit_length(number: int) -> int:
|
|
116
108
|
raise e
|
117
109
|
|
118
110
|
|
119
|
-
def _min_signed_bit_length(number: int) -> int:
|
120
|
-
pos_val = abs(number)
|
121
|
-
is_whole = pos_val & (pos_val - 1) == 0
|
122
|
-
if number <= 0 and is_whole:
|
123
|
-
return _min_unsigned_bit_length(pos_val)
|
124
|
-
return _min_unsigned_bit_length(pos_val) + 1
|
125
|
-
|
126
|
-
|
127
|
-
def _min_bit_length(number: int, is_signed: bool) -> int:
|
128
|
-
return (
|
129
|
-
_min_signed_bit_length(number)
|
130
|
-
if is_signed
|
131
|
-
else _min_unsigned_bit_length(number)
|
132
|
-
)
|
133
|
-
|
134
|
-
|
135
111
|
def _to_twos_complement(value: int, bits: int) -> str:
|
136
112
|
if value >= 0:
|
137
113
|
return bin(value)[2:].zfill(bits)[::-1]
|
@@ -28,6 +28,7 @@ from classiq.interface.source_reference import SourceReference
|
|
28
28
|
from classiq.model_expansions.capturing.captured_vars import (
|
29
29
|
CapturedVars,
|
30
30
|
validate_captured_directions,
|
31
|
+
validate_end_state,
|
31
32
|
)
|
32
33
|
from classiq.model_expansions.closure import (
|
33
34
|
Closure,
|
@@ -142,7 +143,9 @@ class OperationBuilder:
|
|
142
143
|
@contextmanager
|
143
144
|
def block_context(self, block_name: str) -> Iterator[None]:
|
144
145
|
self._blocks.append(block_name)
|
145
|
-
|
146
|
+
block = Block()
|
147
|
+
block.captured_vars.set_parent(self.current_operation.captured_vars)
|
148
|
+
self._operations[-1].blocks[block_name] = block
|
146
149
|
yield
|
147
150
|
captured_vars = self.current_block.captured_vars
|
148
151
|
if (
|
@@ -150,14 +153,12 @@ class OperationBuilder:
|
|
150
153
|
and self.current_operation.name != WITHIN_APPLY_NAME
|
151
154
|
):
|
152
155
|
validate_captured_directions(
|
153
|
-
captured_vars.
|
154
|
-
self.
|
156
|
+
captured_vars.filter_var_decls(
|
157
|
+
self.current_block.variable_declarations
|
155
158
|
),
|
156
159
|
report_outin=False,
|
157
160
|
)
|
158
|
-
self.current_operation.captured_vars.update(
|
159
|
-
captured_vars.filter_vars(self.current_function)
|
160
|
-
)
|
161
|
+
self.current_operation.captured_vars.update(captured_vars)
|
161
162
|
self._blocks.pop()
|
162
163
|
|
163
164
|
@contextmanager
|
@@ -167,11 +168,17 @@ class OperationBuilder:
|
|
167
168
|
context: OperationContext
|
168
169
|
if isinstance(original_operation, FunctionClosure):
|
169
170
|
context = FunctionContext.create(original_operation)
|
171
|
+
context.closure.captured_vars.init_params(original_operation)
|
170
172
|
else:
|
171
173
|
context = OperationContext(closure=original_operation)
|
174
|
+
context.closure.captured_vars.set_parent(self.current_block.captured_vars)
|
172
175
|
self._operations.append(context)
|
173
176
|
yield context
|
174
177
|
self._finalize_within_apply()
|
178
|
+
if isinstance(self.current_operation, FunctionClosure):
|
179
|
+
validate_end_state(
|
180
|
+
self.current_operation, self.current_operation.captured_vars
|
181
|
+
)
|
175
182
|
self._propagate_captured_vars()
|
176
183
|
self._operations.pop()
|
177
184
|
|
@@ -179,21 +186,19 @@ class OperationBuilder:
|
|
179
186
|
if self.current_operation.name != WITHIN_APPLY_NAME:
|
180
187
|
return
|
181
188
|
within_captured_vars = self._operations[-1].blocks["within"].captured_vars
|
182
|
-
self.current_operation.captured_vars.update(
|
183
|
-
within_captured_vars.filter_vars(self.current_function).negate()
|
184
|
-
)
|
189
|
+
self.current_operation.captured_vars.update(within_captured_vars.negate())
|
185
190
|
|
186
191
|
def _propagate_captured_vars(self) -> None:
|
187
192
|
captured_vars = self.current_operation.captured_vars
|
188
193
|
if isinstance(self.current_operation, FunctionClosure):
|
189
|
-
captured_vars = captured_vars.
|
190
|
-
|
194
|
+
captured_vars = captured_vars.filter_vars(
|
195
|
+
self.current_function
|
196
|
+
).set_propagated()
|
197
|
+
validate_captured_directions(captured_vars)
|
191
198
|
if len(self._operations) < 2:
|
192
199
|
return
|
193
200
|
parent_block = self._operations[-2].blocks[self._blocks[-1]]
|
194
|
-
parent_block.captured_vars.update(
|
195
|
-
captured_vars.filter_vars(self.parent_function)
|
196
|
-
)
|
201
|
+
parent_block.captured_vars.update(captured_vars)
|
197
202
|
|
198
203
|
@contextmanager
|
199
204
|
def source_ref_context(
|
@@ -24,9 +24,7 @@ from classiq.qmod.generative import generative_mode_context, set_frontend_interp
|
|
24
24
|
from classiq.qmod.model_state_container import QMODULE
|
25
25
|
from classiq.qmod.qmod_parameter import CParamStruct
|
26
26
|
from classiq.qmod.qmod_variable import QNum, _create_qvar_for_qtype
|
27
|
-
from classiq.qmod.quantum_callable import QCallable
|
28
27
|
from classiq.qmod.quantum_expandable import (
|
29
|
-
QExpandable,
|
30
28
|
QTerminalCallable,
|
31
29
|
)
|
32
30
|
from classiq.qmod.quantum_function import QFunc
|
@@ -147,12 +145,10 @@ def emit_generative_statements(
|
|
147
145
|
translate_ast_arg_to_python_qmod(param, arg)
|
148
146
|
for param, arg in zip(operation.positional_arg_declarations, args)
|
149
147
|
]
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
):
|
158
|
-
generative_function._py_callable(*python_qmod_args)
|
148
|
+
with _InterpreterExpandable(interpreter):
|
149
|
+
set_frontend_interpreter(interpreter)
|
150
|
+
for block_name, generative_function in operation.generative_blocks.items():
|
151
|
+
with interpreter._builder.block_context(
|
152
|
+
block_name
|
153
|
+
), generative_mode_context(True):
|
154
|
+
generative_function._py_callable(*python_qmod_args)
|
@@ -144,7 +144,7 @@ class BaseInterpreter:
|
|
144
144
|
|
145
145
|
def process_exception(self, e: Exception) -> None:
|
146
146
|
if not isinstance(e, (ClassiqError, ValidationError)):
|
147
|
-
raise ClassiqInternalExpansionError(str(e)) from
|
147
|
+
raise ClassiqInternalExpansionError(str(e)) from e
|
148
148
|
prefix = ""
|
149
149
|
if not isinstance(e, ClassiqExpansionError):
|
150
150
|
prefix = f"{type(e).__name__}: "
|
@@ -212,8 +212,15 @@ class BaseInterpreter:
|
|
212
212
|
|
213
213
|
@evaluate.register
|
214
214
|
def evaluate_field_access(self, field_access: FieldHandleBinding) -> Evaluated:
|
215
|
-
base_value = self.evaluate(field_access.base_handle)
|
216
|
-
|
215
|
+
base_value = self.evaluate(field_access.base_handle).as_type(QuantumSymbol)
|
216
|
+
fields = base_value.fields
|
217
|
+
field_name = field_access.field
|
218
|
+
if field_name not in fields:
|
219
|
+
raise ClassiqExpansionError(
|
220
|
+
f"Struct {base_value.quantum_type.type_name} has no field "
|
221
|
+
f"{field_name!r}. Available fields: {', '.join(fields.keys())}"
|
222
|
+
)
|
223
|
+
return Evaluated(value=fields[field_name])
|
217
224
|
|
218
225
|
@abstractmethod
|
219
226
|
def emit(self, statement: QuantumStatement) -> None:
|
@@ -251,8 +258,10 @@ class BaseInterpreter:
|
|
251
258
|
(func_def := self._expanded_functions.get(operation.closure_id))
|
252
259
|
is not None
|
253
260
|
):
|
254
|
-
|
255
|
-
operation.captured_vars.
|
261
|
+
cached_closure = self._top_level_scope[func_def.name].value
|
262
|
+
operation.captured_vars.set(
|
263
|
+
cached_closure.captured_vars, cached_closure, operation
|
264
|
+
)
|
256
265
|
else:
|
257
266
|
self._expand_body(operation)
|
258
267
|
|
@@ -101,15 +101,16 @@ class GenerativeInterpreter(BaseInterpreter):
|
|
101
101
|
closure_class = FunctionClosure
|
102
102
|
extra_args = {}
|
103
103
|
|
104
|
+
closure = closure_class.create(
|
105
|
+
name=func_decl.name,
|
106
|
+
positional_arg_declarations=func_decl.positional_arg_declarations,
|
107
|
+
body=function.body,
|
108
|
+
scope=Scope(parent=self._builder.current_scope),
|
109
|
+
lambda_external_vars=self._builder.current_block.captured_vars,
|
110
|
+
**extra_args,
|
111
|
+
)
|
104
112
|
return Evaluated(
|
105
|
-
value=
|
106
|
-
name=func_decl.name,
|
107
|
-
positional_arg_declarations=func_decl.positional_arg_declarations,
|
108
|
-
body=function.body,
|
109
|
-
scope=Scope(parent=self._builder.current_scope),
|
110
|
-
is_lambda=True,
|
111
|
-
**extra_args,
|
112
|
-
),
|
113
|
+
value=closure,
|
113
114
|
defining_function=self._builder.current_function,
|
114
115
|
)
|
115
116
|
|
@@ -13,7 +13,6 @@ from classiq.model_expansions.scope import QuantumSymbol
|
|
13
13
|
|
14
14
|
class AllocateEmitter(Emitter[Allocate]):
|
15
15
|
def emit(self, allocate: Allocate, /) -> None:
|
16
|
-
self._register_debug_info(allocate)
|
17
16
|
target: QuantumSymbol = self._interpreter.evaluate(allocate.target).as_type(
|
18
17
|
QuantumSymbol
|
19
18
|
)
|
@@ -25,8 +24,13 @@ class AllocateEmitter(Emitter[Allocate]):
|
|
25
24
|
|
26
25
|
size = self._get_var_size(target, allocate.size)
|
27
26
|
allocate = allocate.model_copy(
|
28
|
-
update=dict(
|
27
|
+
update=dict(
|
28
|
+
size=Expression(expr=str(size)),
|
29
|
+
target=target.handle,
|
30
|
+
back_ref=allocate.uuid,
|
31
|
+
)
|
29
32
|
)
|
33
|
+
self._register_debug_info(allocate)
|
30
34
|
self.emit_statement(allocate)
|
31
35
|
|
32
36
|
def _get_var_size(self, target: QuantumSymbol, size: Expression | None) -> int:
|