classiq 0.65.4__py3-none-any.whl → 0.66.1__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/_internals/api_wrapper.py +43 -0
- classiq/applications/qnn/qlayer.py +65 -3
- classiq/execution/execution_session.py +0 -2
- classiq/execution/iqcc.py +66 -1
- classiq/interface/_version.py +1 -1
- classiq/interface/ast_node.py +15 -1
- classiq/interface/backend/backend_preferences.py +0 -14
- classiq/interface/debug_info/debug_info.py +2 -0
- classiq/interface/execution/iqcc.py +25 -0
- classiq/interface/generator/expressions/qmod_qarray_proxy.py +1 -13
- classiq/interface/generator/visitor.py +7 -4
- classiq/interface/model/classical_if.py +4 -0
- classiq/interface/model/control.py +4 -0
- classiq/interface/model/invert.py +4 -0
- classiq/interface/model/model.py +3 -1
- classiq/interface/model/model_visitor.py +14 -0
- classiq/interface/model/power.py +4 -0
- classiq/interface/model/quantum_statement.py +3 -3
- classiq/interface/model/repeat.py +4 -0
- classiq/interface/model/within_apply_operation.py +4 -0
- classiq/interface/server/routes.py +6 -0
- classiq/model_expansions/closure.py +0 -11
- classiq/model_expansions/evaluators/quantum_type_utils.py +6 -6
- classiq/model_expansions/expression_evaluator.py +10 -1
- classiq/model_expansions/interpreters/base_interpreter.py +28 -18
- classiq/model_expansions/interpreters/frontend_generative_interpreter.py +58 -1
- classiq/model_expansions/interpreters/generative_interpreter.py +7 -13
- classiq/model_expansions/quantum_operations/allocate.py +69 -0
- classiq/model_expansions/quantum_operations/call_emitter.py +7 -6
- classiq/model_expansions/quantum_operations/declarative_call_emitter.py +4 -4
- classiq/model_expansions/quantum_operations/emitter.py +2 -15
- classiq/model_expansions/quantum_operations/quantum_function_call.py +22 -0
- classiq/model_expansions/quantum_operations/shallow_emitter.py +21 -35
- classiq/model_expansions/scope_initialization.py +49 -34
- classiq/model_expansions/transformers/model_renamer.py +98 -0
- classiq/model_expansions/transformers/var_splitter.py +7 -82
- 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/__init__.py +1 -1
- classiq/qmod/builtins/functions/__init__.py +0 -2
- classiq/qmod/builtins/functions/allocation.py +1 -26
- classiq/qmod/builtins/operations.py +12 -6
- classiq/qmod/generative.py +6 -4
- classiq/qmod/native/pretty_printer.py +3 -2
- classiq/qmod/pretty_print/pretty_printer.py +3 -1
- classiq/qmod/qmod_variable.py +6 -1
- classiq/qmod/semantics/annotation/call_annotation.py +30 -2
- classiq/qmod/semantics/annotation/qstruct_annotator.py +2 -2
- classiq/qmod/semantics/error_manager.py +20 -6
- classiq/qmod/semantics/static_semantics_visitor.py +3 -40
- classiq/qmod/semantics/validation/constants_validation.py +2 -3
- classiq/qmod/semantics/validation/function_name_collisions_validation.py +6 -9
- classiq/qmod/semantics/validation/main_validation.py +2 -3
- classiq/qmod/semantics/validation/model_validation.py +25 -0
- classiq/qmod/semantics/validation/signature_validation.py +24 -0
- classiq/qmod/semantics/validation/types_validation.py +45 -46
- classiq/qmod/utilities.py +12 -0
- {classiq-0.65.4.dist-info → classiq-0.66.1.dist-info}/METADATA +1 -1
- {classiq-0.65.4.dist-info → classiq-0.66.1.dist-info}/RECORD +61 -56
- classiq/model_expansions/expression_renamer.py +0 -76
- {classiq-0.65.4.dist-info → classiq-0.66.1.dist-info}/WHEEL +0 -0
@@ -67,7 +67,7 @@ def set_size(quantum_type: QuantumType, size: int, param_name: str) -> None:
|
|
67
67
|
|
68
68
|
if quantum_type.has_size_in_bits and quantum_type.size_in_bits != size:
|
69
69
|
raise ClassiqExpansionError(
|
70
|
-
f"Size mismatch for
|
70
|
+
f"Size mismatch for variable {param_name!r} between declared size {quantum_type.size_in_bits} and assigned size {size}"
|
71
71
|
)
|
72
72
|
|
73
73
|
if isinstance(quantum_type, QuantumNumeric):
|
@@ -79,7 +79,7 @@ def set_size(quantum_type: QuantumType, size: int, param_name: str) -> None:
|
|
79
79
|
if quantum_type.has_length:
|
80
80
|
if size % quantum_type.length_value != 0:
|
81
81
|
raise ClassiqExpansionError(
|
82
|
-
f"Size mismatch for
|
82
|
+
f"Size mismatch for variable {param_name!r}. Cannot fit {size} "
|
83
83
|
f"qubits into an array of {quantum_type.length_value} elements."
|
84
84
|
)
|
85
85
|
set_size(
|
@@ -123,7 +123,7 @@ def set_fraction_digits(
|
|
123
123
|
and quantum_numeric.fraction_digits_value != fraction_digits
|
124
124
|
):
|
125
125
|
raise ClassiqExpansionError(
|
126
|
-
f"Fraction digits mismatch for
|
126
|
+
f"Fraction digits mismatch for variable {param_name!r} between declared "
|
127
127
|
f"fraction digits {quantum_numeric.fraction_digits_value!r} and assigned fraction "
|
128
128
|
f"digits {fraction_digits!r}"
|
129
129
|
)
|
@@ -145,7 +145,7 @@ def set_is_signed(
|
|
145
145
|
and quantum_numeric.sign_value != is_signed
|
146
146
|
):
|
147
147
|
raise ClassiqExpansionError(
|
148
|
-
f"Sign mismatch for
|
148
|
+
f"Sign mismatch for variable {param_name!r} between declared sign {quantum_numeric.sign_value!r} and assigned sign {is_signed!r}"
|
149
149
|
)
|
150
150
|
|
151
151
|
if not (
|
@@ -175,7 +175,7 @@ def set_length_by_size(
|
|
175
175
|
|
176
176
|
if quantum_array.has_size_in_bits and quantum_array.size_in_bits != size:
|
177
177
|
raise ClassiqExpansionError(
|
178
|
-
f"Size mismatch for
|
178
|
+
f"Size mismatch for variable {param_name!r} between declared size "
|
179
179
|
f"{quantum_array.size_in_bits} ({quantum_array.length_value} elements of "
|
180
180
|
f"size {quantum_array.element_type.size_in_bits}) and assigned size {size}."
|
181
181
|
)
|
@@ -188,7 +188,7 @@ def set_length_by_size(
|
|
188
188
|
|
189
189
|
if size % element_size != 0:
|
190
190
|
raise ClassiqExpansionError(
|
191
|
-
f"Size mismatch for
|
191
|
+
f"Size mismatch for variable {param_name!r}. Cannot fit elements of type "
|
192
192
|
f"{quantum_array.element_type.qmod_type_name} (size {element_size}) into "
|
193
193
|
f"{size} qubits."
|
194
194
|
)
|
@@ -4,7 +4,7 @@ from collections.abc import Mapping
|
|
4
4
|
from enum import EnumMeta
|
5
5
|
from typing import Any, Optional
|
6
6
|
|
7
|
-
from sympy import sympify
|
7
|
+
from sympy import SympifyError, sympify
|
8
8
|
|
9
9
|
from classiq.interface.exceptions import ClassiqExpansionError
|
10
10
|
from classiq.interface.generator.constant import Constant
|
@@ -16,6 +16,8 @@ from classiq.interface.generator.expressions.expression_constants import (
|
|
16
16
|
CPARAM_EXECUTION_SUFFIX_PATTERN,
|
17
17
|
)
|
18
18
|
from classiq.interface.generator.expressions.expression_types import ExpressionValue
|
19
|
+
from classiq.interface.generator.expressions.non_symbolic_expr import NonSymbolicExpr
|
20
|
+
from classiq.interface.generator.expressions.qmod_sized_proxy import QmodSizedProxy
|
19
21
|
from classiq.interface.generator.expressions.sympy_supported_expressions import (
|
20
22
|
SYMPY_SUPPORTED_EXPRESSIONS,
|
21
23
|
)
|
@@ -81,6 +83,13 @@ def evaluate(
|
|
81
83
|
f"{', '.join(e.obj.__members__)}"
|
82
84
|
) from e
|
83
85
|
raise
|
86
|
+
except SympifyError as e:
|
87
|
+
expr = e.expr
|
88
|
+
if isinstance(expr, QmodSizedProxy) and isinstance(expr, NonSymbolicExpr):
|
89
|
+
raise ClassiqExpansionError(
|
90
|
+
f"{expr.type_name} {str(expr)!r} does not support arithmetic operations"
|
91
|
+
) from e
|
92
|
+
raise
|
84
93
|
|
85
94
|
return EvaluatedExpression(
|
86
95
|
value=sympy_to_python(sympify_result, locals=model_locals)
|
@@ -4,18 +4,19 @@ from collections import defaultdict
|
|
4
4
|
from collections.abc import Sequence
|
5
5
|
from contextlib import nullcontext
|
6
6
|
from functools import singledispatchmethod
|
7
|
-
from typing import Any,
|
7
|
+
from typing import Any, cast
|
8
8
|
|
9
9
|
import sympy
|
10
10
|
from pydantic import ValidationError
|
11
11
|
|
12
|
+
from classiq.interface.debug_info.debug_info import FunctionDebugInfo
|
12
13
|
from classiq.interface.exceptions import (
|
13
14
|
ClassiqError,
|
14
15
|
ClassiqExpansionError,
|
15
16
|
ClassiqInternalExpansionError,
|
16
17
|
)
|
17
|
-
from classiq.interface.generator.constant import Constant
|
18
18
|
from classiq.interface.generator.expressions.expression import Expression
|
19
|
+
from classiq.interface.generator.generated_circuit_data import OperationLevel
|
19
20
|
from classiq.interface.generator.types.compilation_metadata import CompilationMetadata
|
20
21
|
from classiq.interface.model.handle_binding import (
|
21
22
|
FieldHandleBinding,
|
@@ -42,7 +43,6 @@ from classiq.model_expansions.debug_flag import debug_mode
|
|
42
43
|
from classiq.model_expansions.evaluators.classical_expression import (
|
43
44
|
evaluate_classical_expression,
|
44
45
|
)
|
45
|
-
from classiq.model_expansions.expression_renamer import ExpressionRenamer
|
46
46
|
from classiq.model_expansions.function_builder import (
|
47
47
|
FunctionContext,
|
48
48
|
OperationBuilder,
|
@@ -50,10 +50,9 @@ from classiq.model_expansions.function_builder import (
|
|
50
50
|
)
|
51
51
|
from classiq.model_expansions.scope import Evaluated, QuantumSymbol, Scope
|
52
52
|
from classiq.model_expansions.scope_initialization import (
|
53
|
-
add_constants_to_scope,
|
54
53
|
add_entry_point_params_to_scope,
|
55
|
-
get_main_renamer,
|
56
54
|
init_builtin_types,
|
55
|
+
init_exec_params,
|
57
56
|
init_top_level_scope,
|
58
57
|
)
|
59
58
|
from classiq.model_expansions.utils.counted_name_allocator import CountedNameAllocator
|
@@ -62,10 +61,12 @@ from classiq.qmod.builtins.enums import BUILTIN_ENUM_DECLARATIONS
|
|
62
61
|
from classiq.qmod.builtins.structs import BUILTIN_STRUCT_DECLARATIONS
|
63
62
|
from classiq.qmod.model_state_container import QMODULE
|
64
63
|
from classiq.qmod.semantics.error_manager import ErrorManager
|
64
|
+
from classiq.qmod.semantics.validation.model_validation import validate_model
|
65
65
|
|
66
66
|
|
67
67
|
class BaseInterpreter:
|
68
68
|
def __init__(self, model: Model) -> None:
|
69
|
+
validate_model(model)
|
69
70
|
self._model = model
|
70
71
|
self._top_level_scope = Scope()
|
71
72
|
self._counted_name_allocator = CountedNameAllocator()
|
@@ -76,6 +77,7 @@ class BaseInterpreter:
|
|
76
77
|
|
77
78
|
init_builtin_types()
|
78
79
|
init_top_level_scope(model, self._top_level_scope)
|
80
|
+
self._exec_params = init_exec_params(model, self._top_level_scope)
|
79
81
|
self._functions_compilation_metadata: dict[str, CompilationMetadata] = dict(
|
80
82
|
self._model.functions_compilation_metadata
|
81
83
|
)
|
@@ -85,9 +87,6 @@ class BaseInterpreter:
|
|
85
87
|
self._counted_name_allocator = CountedNameAllocator()
|
86
88
|
self._error_manager: ErrorManager = ErrorManager()
|
87
89
|
|
88
|
-
def get_main_renamer(self) -> Optional[ExpressionRenamer]:
|
89
|
-
return get_main_renamer(self._get_function_declarations())
|
90
|
-
|
91
90
|
def _expand_main_func(self) -> None:
|
92
91
|
main_closure = self._get_main_closure(
|
93
92
|
self._top_level_scope[MAIN_FUNCTION_NAME].value
|
@@ -105,7 +104,6 @@ class BaseInterpreter:
|
|
105
104
|
name=main_func.name,
|
106
105
|
positional_arg_declarations=main_func.positional_arg_declarations,
|
107
106
|
scope=Scope(parent=self._top_level_scope),
|
108
|
-
expr_renamer=self.get_main_renamer(),
|
109
107
|
_depth=0,
|
110
108
|
body=main_func.body,
|
111
109
|
)
|
@@ -117,12 +115,7 @@ class BaseInterpreter:
|
|
117
115
|
except Exception as e:
|
118
116
|
if isinstance(e, ClassiqInternalExpansionError) or debug_mode.get():
|
119
117
|
raise e
|
120
|
-
|
121
|
-
raise ClassiqInternalExpansionError(str(e)) from None
|
122
|
-
prefix = ""
|
123
|
-
if not isinstance(e, ClassiqExpansionError):
|
124
|
-
prefix = f"{type(e).__name__}: "
|
125
|
-
self._error_manager.add_error(f"{prefix}{e}")
|
118
|
+
self.process_exception(e)
|
126
119
|
finally:
|
127
120
|
self._error_manager.report_errors(ClassiqExpansionError)
|
128
121
|
|
@@ -146,8 +139,17 @@ class BaseInterpreter:
|
|
146
139
|
qstructs=list(QMODULE.qstruct_decls.values()),
|
147
140
|
debug_info=self._model.debug_info,
|
148
141
|
functions_compilation_metadata=self._expanded_functions_compilation_metadata,
|
142
|
+
execution_parameters=self._exec_params,
|
149
143
|
)
|
150
144
|
|
145
|
+
def process_exception(self, e: Exception) -> None:
|
146
|
+
if not isinstance(e, (ClassiqError, ValidationError)):
|
147
|
+
raise ClassiqInternalExpansionError(str(e)) from None
|
148
|
+
prefix = ""
|
149
|
+
if not isinstance(e, ClassiqExpansionError):
|
150
|
+
prefix = f"{type(e).__name__}: "
|
151
|
+
self._error_manager.add_error(f"{prefix}{e}")
|
152
|
+
|
151
153
|
@singledispatchmethod
|
152
154
|
def evaluate(self, expression: Any) -> Evaluated:
|
153
155
|
raise NotImplementedError(f"Cannot evaluate {expression!r}")
|
@@ -229,6 +231,17 @@ class BaseInterpreter:
|
|
229
231
|
if source_ref is not None
|
230
232
|
else nullcontext()
|
231
233
|
)
|
234
|
+
if statement.uuid not in self._model.debug_info:
|
235
|
+
self._model.debug_info[statement.uuid] = FunctionDebugInfo(
|
236
|
+
name="",
|
237
|
+
parameters={},
|
238
|
+
level=OperationLevel.QMOD_STATEMENT,
|
239
|
+
statement_type=None,
|
240
|
+
is_allocate_or_free=False,
|
241
|
+
is_inverse=False,
|
242
|
+
port_to_passed_variable_map={},
|
243
|
+
node=statement._as_back_ref(),
|
244
|
+
)
|
232
245
|
with error_context, self._builder.source_ref_context(source_ref):
|
233
246
|
self.emit(statement)
|
234
247
|
|
@@ -258,6 +271,3 @@ class BaseInterpreter:
|
|
258
271
|
for func in self._top_level_scope.values()
|
259
272
|
if isinstance(func_closure := func.value, FunctionClosure)
|
260
273
|
]
|
261
|
-
|
262
|
-
def add_constant(self, constant: Constant) -> None:
|
263
|
-
add_constants_to_scope([constant], self._top_level_scope)
|
@@ -1,4 +1,11 @@
|
|
1
|
+
import inspect
|
2
|
+
import os
|
3
|
+
|
4
|
+
from pydantic import ValidationError
|
5
|
+
|
6
|
+
from classiq.interface.exceptions import ClassiqError
|
1
7
|
from classiq.interface.model.quantum_function_call import QuantumFunctionCall
|
8
|
+
from classiq.interface.source_reference import SourceReference
|
2
9
|
|
3
10
|
from classiq.model_expansions.closure import FunctionClosure, GenerativeFunctionClosure
|
4
11
|
from classiq.model_expansions.interpreters.generative_interpreter import (
|
@@ -8,6 +15,7 @@ from classiq.model_expansions.quantum_operations.quantum_function_call import (
|
|
8
15
|
DeclarativeQuantumFunctionCallEmitter,
|
9
16
|
)
|
10
17
|
from classiq.model_expansions.scope import Scope
|
18
|
+
from classiq.qmod.model_state_container import QMODULE
|
11
19
|
|
12
20
|
|
13
21
|
class FrontendGenerativeInterpreter(GenerativeInterpreter):
|
@@ -20,9 +28,58 @@ class FrontendGenerativeInterpreter(GenerativeInterpreter):
|
|
20
28
|
name=main_func.name,
|
21
29
|
positional_arg_declarations=main_func.positional_arg_declarations,
|
22
30
|
scope=Scope(parent=self._top_level_scope),
|
23
|
-
expr_renamer=self.get_main_renamer(),
|
24
31
|
_depth=0,
|
25
32
|
generative_blocks={"body": main_func.generative_blocks["body"]},
|
26
33
|
)
|
27
34
|
|
28
35
|
return super()._get_main_closure(main_func)
|
36
|
+
|
37
|
+
def process_exception(self, e: Exception) -> None:
|
38
|
+
if not isinstance(e, (ClassiqError, ValidationError)):
|
39
|
+
frame = inspect.trace()[-1]
|
40
|
+
module = inspect.getmodule(frame[0])
|
41
|
+
if module is None or not module.__name__.startswith("classiq."):
|
42
|
+
file_name = os.path.split(frame.filename)[-1]
|
43
|
+
if (
|
44
|
+
frame.positions is not None
|
45
|
+
and frame.positions.lineno is not None
|
46
|
+
and frame.positions.col_offset is not None
|
47
|
+
and frame.positions.end_lineno is not None
|
48
|
+
and frame.positions.end_col_offset is not None
|
49
|
+
):
|
50
|
+
source_ref = SourceReference(
|
51
|
+
start_line=frame.positions.lineno - 1,
|
52
|
+
start_column=frame.positions.col_offset - 1,
|
53
|
+
end_line=frame.positions.end_lineno - 1,
|
54
|
+
end_column=frame.positions.end_col_offset - 1,
|
55
|
+
file_name=file_name,
|
56
|
+
)
|
57
|
+
else:
|
58
|
+
source_ref = SourceReference(
|
59
|
+
start_line=frame.lineno - 1,
|
60
|
+
start_column=frame.lineno - 1,
|
61
|
+
end_line=-1,
|
62
|
+
end_column=-1,
|
63
|
+
file_name=file_name,
|
64
|
+
)
|
65
|
+
e_str = f": {e}" if str(e) else ""
|
66
|
+
self._error_manager.add_error(
|
67
|
+
f"{type(e).__name__}{e_str}",
|
68
|
+
source_ref=source_ref,
|
69
|
+
function=frame.function,
|
70
|
+
)
|
71
|
+
return
|
72
|
+
|
73
|
+
super().process_exception(e)
|
74
|
+
|
75
|
+
def add_purely_declarative_function(self, function: FunctionClosure) -> None:
|
76
|
+
functions_to_add = [function.name] + QMODULE.function_dependencies[
|
77
|
+
function.name
|
78
|
+
]
|
79
|
+
for func in functions_to_add:
|
80
|
+
if func not in self._expanded_functions and func in QMODULE.native_defs:
|
81
|
+
self._expanded_functions[func] = QMODULE.native_defs[func]
|
82
|
+
if func in QMODULE.functions_compilation_metadata:
|
83
|
+
self._expanded_functions_compilation_metadata[func] = (
|
84
|
+
QMODULE.functions_compilation_metadata[func]
|
85
|
+
)
|
@@ -4,6 +4,7 @@ from typing import Any
|
|
4
4
|
import numpy as np
|
5
5
|
from numpy.random import permutation
|
6
6
|
|
7
|
+
from classiq.interface.generator.constant import Constant
|
7
8
|
from classiq.interface.generator.expressions.expression import Expression
|
8
9
|
from classiq.interface.generator.functions.builtins.internal_operators import (
|
9
10
|
CONTROL_OPERATOR_NAME,
|
@@ -53,14 +54,16 @@ from classiq.model_expansions.quantum_operations import (
|
|
53
54
|
RepeatEmitter,
|
54
55
|
VariableDeclarationStatementEmitter,
|
55
56
|
)
|
57
|
+
from classiq.model_expansions.quantum_operations.allocate import AllocateEmitter
|
56
58
|
from classiq.model_expansions.quantum_operations.shallow_emitter import ShallowEmitter
|
57
59
|
from classiq.model_expansions.scope import Evaluated, Scope
|
58
60
|
from classiq.model_expansions.scope_initialization import (
|
61
|
+
add_constants_to_scope,
|
59
62
|
add_functions_to_scope,
|
60
63
|
add_generative_functions_to_scope,
|
61
64
|
)
|
62
65
|
from classiq.qmod.builtins.functions import permute
|
63
|
-
from classiq.qmod.model_state_container import
|
66
|
+
from classiq.qmod.model_state_container import ModelStateContainer
|
64
67
|
from classiq.qmod.quantum_function import GenerativeQFunc
|
65
68
|
|
66
69
|
|
@@ -123,7 +126,7 @@ class GenerativeInterpreter(BaseInterpreter):
|
|
123
126
|
|
124
127
|
@emit.register
|
125
128
|
def emit_allocate(self, allocate: Allocate) -> None:
|
126
|
-
|
129
|
+
AllocateEmitter(self).emit(allocate)
|
127
130
|
|
128
131
|
@emit.register
|
129
132
|
def emit_bind(self, bind: BindOperation) -> None:
|
@@ -236,14 +239,5 @@ class GenerativeInterpreter(BaseInterpreter):
|
|
236
239
|
qmodule.functions_compilation_metadata[dec_func_name]
|
237
240
|
)
|
238
241
|
|
239
|
-
def
|
240
|
-
|
241
|
-
function.name
|
242
|
-
]
|
243
|
-
for func in functions_to_add:
|
244
|
-
if func not in self._expanded_functions and func in QMODULE.native_defs:
|
245
|
-
self._expanded_functions[func] = QMODULE.native_defs[func]
|
246
|
-
if func in QMODULE.functions_compilation_metadata:
|
247
|
-
self._expanded_functions_compilation_metadata[func] = (
|
248
|
-
QMODULE.functions_compilation_metadata[func]
|
249
|
-
)
|
242
|
+
def add_constant(self, constant: Constant) -> None:
|
243
|
+
add_constants_to_scope([constant], self._top_level_scope)
|
@@ -0,0 +1,69 @@
|
|
1
|
+
from classiq.interface.debug_info.debug_info import FunctionDebugInfo
|
2
|
+
from classiq.interface.exceptions import ClassiqValueError
|
3
|
+
from classiq.interface.generator.expressions.expression import Expression
|
4
|
+
from classiq.interface.generator.generated_circuit_data import OperationLevel
|
5
|
+
from classiq.interface.model.allocate import Allocate
|
6
|
+
from classiq.interface.model.handle_binding import NestedHandleBinding
|
7
|
+
from classiq.interface.model.quantum_type import QuantumBitvector
|
8
|
+
|
9
|
+
from classiq.model_expansions.evaluators.quantum_type_utils import copy_type_information
|
10
|
+
from classiq.model_expansions.quantum_operations.emitter import Emitter
|
11
|
+
from classiq.model_expansions.scope import QuantumSymbol
|
12
|
+
|
13
|
+
|
14
|
+
class AllocateEmitter(Emitter[Allocate]):
|
15
|
+
def emit(self, allocate: Allocate, /) -> None:
|
16
|
+
self._register_debug_info(allocate)
|
17
|
+
target: QuantumSymbol = self._interpreter.evaluate(allocate.target).as_type(
|
18
|
+
QuantumSymbol
|
19
|
+
)
|
20
|
+
|
21
|
+
if isinstance(target.handle, NestedHandleBinding):
|
22
|
+
raise ClassiqValueError(
|
23
|
+
f"Cannot allocate partial quantum variable {str(target.handle)!r}"
|
24
|
+
)
|
25
|
+
|
26
|
+
size = self._get_var_size(target, allocate.size)
|
27
|
+
allocate = allocate.model_copy(
|
28
|
+
update=dict(size=Expression(expr=str(size)), target=target.handle)
|
29
|
+
)
|
30
|
+
self.emit_statement(allocate)
|
31
|
+
|
32
|
+
def _get_var_size(self, target: QuantumSymbol, size: Expression | None) -> int:
|
33
|
+
if size is None:
|
34
|
+
if not target.quantum_type.is_evaluated:
|
35
|
+
raise ClassiqValueError(
|
36
|
+
f"Could not infer the size of variable {str(target.handle)!r}"
|
37
|
+
)
|
38
|
+
return target.quantum_type.size_in_bits
|
39
|
+
|
40
|
+
size_value = self._interpreter.evaluate(size).value
|
41
|
+
if not isinstance(size_value, (int, float)):
|
42
|
+
raise ClassiqValueError(
|
43
|
+
f"The number of allocated qubits must be an integer. Got "
|
44
|
+
f"{str(size_value)!r}"
|
45
|
+
)
|
46
|
+
size_value = int(size_value)
|
47
|
+
copy_type_information(
|
48
|
+
QuantumBitvector(length=Expression(expr=str(size_value))),
|
49
|
+
target.quantum_type,
|
50
|
+
str(target.handle),
|
51
|
+
)
|
52
|
+
return size_value
|
53
|
+
|
54
|
+
def _register_debug_info(self, allocate: Allocate) -> None:
|
55
|
+
if (
|
56
|
+
allocate.uuid in self._debug_info
|
57
|
+
and self._debug_info[allocate.uuid].name != ""
|
58
|
+
):
|
59
|
+
return
|
60
|
+
parameters: dict[str, str] = {}
|
61
|
+
if allocate.size is not None:
|
62
|
+
parameters["num_qubits"] = allocate.size.expr
|
63
|
+
self._debug_info[allocate.uuid] = FunctionDebugInfo(
|
64
|
+
name="allocate",
|
65
|
+
parameters=parameters,
|
66
|
+
level=OperationLevel.QMOD_STATEMENT,
|
67
|
+
is_allocate_or_free=True,
|
68
|
+
port_to_passed_variable_map={"ARG": str(allocate.target)},
|
69
|
+
)
|
@@ -44,7 +44,10 @@ from classiq.model_expansions.quantum_operations.emitter import (
|
|
44
44
|
)
|
45
45
|
from classiq.model_expansions.scope import Evaluated, QuantumSymbol, Scope
|
46
46
|
from classiq.model_expansions.transformers.var_splitter import VarSplitter
|
47
|
-
from classiq.qmod.builtins.functions import
|
47
|
+
from classiq.qmod.builtins.functions import free
|
48
|
+
from classiq.qmod.semantics.validation.signature_validation import (
|
49
|
+
validate_function_signature,
|
50
|
+
)
|
48
51
|
|
49
52
|
if TYPE_CHECKING:
|
50
53
|
from classiq.model_expansions.interpreters.base_interpreter import BaseInterpreter
|
@@ -119,10 +122,7 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
|
|
119
122
|
new_call = QuantumFunctionCall(
|
120
123
|
function=new_declaration.name, positional_args=new_positional_args
|
121
124
|
)
|
122
|
-
is_allocate_or_free =
|
123
|
-
new_call.func_name == allocate.func_decl.name
|
124
|
-
or new_call.func_name == free.func_decl.name
|
125
|
-
)
|
125
|
+
is_allocate_or_free = new_call.func_name == free.func_decl.name
|
126
126
|
parameters = {
|
127
127
|
arg_decl.name: FunctionDebugInfo.param_controller(value=evaluated_arg.value)
|
128
128
|
for arg_decl, evaluated_arg in zip(new_positional_arg_decls, evaluated_args)
|
@@ -139,7 +139,7 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
|
|
139
139
|
level=OperationLevel.QMOD_FUNCTION_CALL,
|
140
140
|
parameters=(
|
141
141
|
parameters
|
142
|
-
if propagated_debug_info is None
|
142
|
+
if propagated_debug_info is None or propagated_debug_info.name == ""
|
143
143
|
else propagated_debug_info.parameters
|
144
144
|
),
|
145
145
|
is_allocate_or_free=is_allocate_or_free,
|
@@ -255,6 +255,7 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
|
|
255
255
|
different from the call scope. For example, the former uses r,s and the latter
|
256
256
|
uses p, q.
|
257
257
|
"""
|
258
|
+
validate_function_signature(function.positional_arg_declarations)
|
258
259
|
# The signature scope is passed as a separate argument to avoid contaminating the statement execution scope
|
259
260
|
return NamedParamsQuantumFunctionDeclaration(
|
260
261
|
name=function.name,
|
@@ -13,17 +13,17 @@ from classiq.model_expansions.scope import Evaluated
|
|
13
13
|
from classiq.qmod.model_state_container import QMODULE
|
14
14
|
|
15
15
|
if TYPE_CHECKING:
|
16
|
-
from classiq.model_expansions.interpreters.
|
17
|
-
|
16
|
+
from classiq.model_expansions.interpreters.frontend_generative_interpreter import (
|
17
|
+
FrontendGenerativeInterpreter,
|
18
18
|
)
|
19
19
|
|
20
20
|
|
21
21
|
class DeclarativeCallEmitter(
|
22
22
|
Generic[QuantumStatementT], CallEmitter[QuantumStatementT]
|
23
23
|
):
|
24
|
-
_interpreter: "
|
24
|
+
_interpreter: "FrontendGenerativeInterpreter"
|
25
25
|
|
26
|
-
def __init__(self, interpreter: "
|
26
|
+
def __init__(self, interpreter: "FrontendGenerativeInterpreter") -> None:
|
27
27
|
super().__init__(interpreter)
|
28
28
|
|
29
29
|
def should_expand_function(
|
@@ -1,5 +1,4 @@
|
|
1
|
-
import
|
2
|
-
from abc import abstractmethod
|
1
|
+
from abc import ABC, abstractmethod
|
3
2
|
from typing import (
|
4
3
|
TYPE_CHECKING,
|
5
4
|
Generic,
|
@@ -37,7 +36,6 @@ from classiq.model_expansions.sympy_conversion.sympy_to_python import (
|
|
37
36
|
translate_sympy_quantum_expression,
|
38
37
|
)
|
39
38
|
from classiq.model_expansions.utils.counted_name_allocator import CountedNameAllocator
|
40
|
-
from classiq.model_expansions.visitors.variable_references import VarRefCollector
|
41
39
|
from classiq.qmod.quantum_function import GenerativeQFunc
|
42
40
|
|
43
41
|
if TYPE_CHECKING:
|
@@ -46,7 +44,7 @@ if TYPE_CHECKING:
|
|
46
44
|
QuantumStatementT = TypeVar("QuantumStatementT", bound=QuantumStatement)
|
47
45
|
|
48
46
|
|
49
|
-
class Emitter(Generic[QuantumStatementT]):
|
47
|
+
class Emitter(Generic[QuantumStatementT], ABC):
|
50
48
|
def __init__(self, interpreter: "BaseInterpreter") -> None:
|
51
49
|
self._interpreter = interpreter
|
52
50
|
|
@@ -168,14 +166,3 @@ class Emitter(Generic[QuantumStatementT]):
|
|
168
166
|
defining_function=defining_function,
|
169
167
|
direction=direction,
|
170
168
|
)
|
171
|
-
|
172
|
-
def capture_handles_in_expression(self, expr: Expression) -> None:
|
173
|
-
vrc = VarRefCollector(ignore_duplicated_handles=True)
|
174
|
-
vrc.visit(ast.parse(expr.expr))
|
175
|
-
handles = dict.fromkeys(
|
176
|
-
handle
|
177
|
-
for handle in vrc.var_handles
|
178
|
-
if isinstance(self._current_scope[handle.name].value, QuantumSymbol)
|
179
|
-
)
|
180
|
-
for handle in handles:
|
181
|
-
self._capture_handle(handle, PortDeclarationDirection.Inout)
|
@@ -1,5 +1,9 @@
|
|
1
1
|
from typing import TYPE_CHECKING
|
2
2
|
|
3
|
+
from classiq.interface.exceptions import ClassiqValueError
|
4
|
+
from classiq.interface.generator.expressions.expression import Expression
|
5
|
+
from classiq.interface.model.allocate import Allocate
|
6
|
+
from classiq.interface.model.handle_binding import HandleBinding
|
3
7
|
from classiq.interface.model.quantum_function_call import QuantumFunctionCall
|
4
8
|
|
5
9
|
from classiq.model_expansions.closure import FunctionClosure
|
@@ -13,12 +17,21 @@ if TYPE_CHECKING:
|
|
13
17
|
from classiq.model_expansions.interpreters.base_interpreter import BaseInterpreter
|
14
18
|
|
15
19
|
|
20
|
+
ALLOCATE_COMPATIBILITY_ERROR_MESSAGE = (
|
21
|
+
"'allocate' expects two argument: The number of qubits to allocate (integer) and "
|
22
|
+
"the variable to be allocated (quantum variable)"
|
23
|
+
)
|
24
|
+
|
25
|
+
|
16
26
|
class QuantumFunctionCallEmitter(CallEmitter[QuantumFunctionCall]):
|
17
27
|
def __init__(self, interpreter: "BaseInterpreter") -> None:
|
18
28
|
super().__init__(interpreter)
|
19
29
|
self._model = self._interpreter._model
|
20
30
|
|
21
31
|
def emit(self, call: QuantumFunctionCall, /) -> None:
|
32
|
+
if call.function == "allocate": # FIXME: Remove compatibility (CAD-25935)
|
33
|
+
self._allocate_compatibility(call)
|
34
|
+
return
|
22
35
|
function = self._interpreter.evaluate(call.function).as_type(FunctionClosure)
|
23
36
|
args = call.positional_args
|
24
37
|
with ErrorManager().call(function.name):
|
@@ -26,6 +39,15 @@ class QuantumFunctionCallEmitter(CallEmitter[QuantumFunctionCall]):
|
|
26
39
|
function, args, self._debug_info.get(call.uuid)
|
27
40
|
)
|
28
41
|
|
42
|
+
def _allocate_compatibility(self, call: QuantumFunctionCall) -> None:
|
43
|
+
if len(call.positional_args) != 2:
|
44
|
+
raise ClassiqValueError(ALLOCATE_COMPATIBILITY_ERROR_MESSAGE)
|
45
|
+
size, target = call.positional_args
|
46
|
+
if not isinstance(size, Expression) or not isinstance(target, HandleBinding):
|
47
|
+
raise ClassiqValueError(ALLOCATE_COMPATIBILITY_ERROR_MESSAGE)
|
48
|
+
allocate = Allocate(size=size, target=target, source_ref=call.source_ref)
|
49
|
+
self._interpreter.emit_statement(allocate)
|
50
|
+
|
29
51
|
|
30
52
|
class DeclarativeQuantumFunctionCallEmitter(
|
31
53
|
QuantumFunctionCallEmitter, DeclarativeCallEmitter
|
@@ -6,7 +6,6 @@ from classiq.interface.generator.expressions.expression import Expression
|
|
6
6
|
from classiq.interface.generator.functions.port_declaration import (
|
7
7
|
PortDeclarationDirection,
|
8
8
|
)
|
9
|
-
from classiq.interface.model.allocate import Allocate
|
10
9
|
from classiq.interface.model.handle_binding import HandleBinding
|
11
10
|
from classiq.interface.model.quantum_expressions.arithmetic_operation import (
|
12
11
|
ArithmeticOperation,
|
@@ -18,10 +17,7 @@ from classiq.interface.model.quantum_expressions.quantum_expression import (
|
|
18
17
|
from classiq.interface.model.quantum_statement import QuantumOperation, QuantumStatement
|
19
18
|
|
20
19
|
from classiq.model_expansions.closure import Closure
|
21
|
-
from classiq.model_expansions.evaluators.quantum_type_utils import
|
22
|
-
copy_type_information,
|
23
|
-
set_size,
|
24
|
-
)
|
20
|
+
from classiq.model_expansions.evaluators.quantum_type_utils import copy_type_information
|
25
21
|
from classiq.model_expansions.quantum_operations.emitter import Emitter
|
26
22
|
from classiq.model_expansions.scope import QuantumSymbol, Scope
|
27
23
|
from classiq.model_expansions.transformers.ast_renamer import rename_variables
|
@@ -65,7 +61,8 @@ class ShallowEmitter(Emitter[QuantumOperation]):
|
|
65
61
|
for expression_name in expressions:
|
66
62
|
expression = getattr(op, expression_name)
|
67
63
|
expression = self._evaluate_expression(expression, preserve_bool_ops=True)
|
68
|
-
self.
|
64
|
+
for symbol in self._get_symbols_in_expression(expression):
|
65
|
+
self._capture_handle(symbol.handle, PortDeclarationDirection.Inout)
|
69
66
|
expanded_components[expression_name] = expression
|
70
67
|
|
71
68
|
for handle_name in handles:
|
@@ -77,8 +74,6 @@ class ShallowEmitter(Emitter[QuantumOperation]):
|
|
77
74
|
op = op.model_copy(update=expanded_components)
|
78
75
|
if isinstance(op, QuantumAssignmentOperation):
|
79
76
|
self._post_process_assignment(op)
|
80
|
-
if isinstance(op, Allocate):
|
81
|
-
self._post_process_allocate(op)
|
82
77
|
self._builder.emit_statement(op)
|
83
78
|
|
84
79
|
def _post_process_assignment(self, op: QuantumAssignmentOperation) -> None:
|
@@ -92,19 +87,6 @@ class ShallowEmitter(Emitter[QuantumOperation]):
|
|
92
87
|
direction = PortDeclarationDirection.Inout
|
93
88
|
self._capture_handle(op.result_var, direction)
|
94
89
|
|
95
|
-
def _post_process_allocate(self, allocate: Allocate) -> None:
|
96
|
-
target_symbol = self._interpreter.evaluate(allocate.target).value
|
97
|
-
if not isinstance(target_symbol, QuantumSymbol):
|
98
|
-
return
|
99
|
-
self._capture_handle(target_symbol.handle, PortDeclarationDirection.Output)
|
100
|
-
if allocate.size is None or not allocate.size.is_evaluated():
|
101
|
-
return
|
102
|
-
set_size(
|
103
|
-
target_symbol.quantum_type,
|
104
|
-
allocate.size.to_int_value(),
|
105
|
-
str(target_symbol.handle),
|
106
|
-
)
|
107
|
-
|
108
90
|
def _split_components(
|
109
91
|
self, op: QuantumOperation
|
110
92
|
) -> tuple[list[str], list[str], list[str]]:
|
@@ -152,25 +134,19 @@ class ShallowEmitter(Emitter[QuantumOperation]):
|
|
152
134
|
}
|
153
135
|
|
154
136
|
def _update_result_type(self, op: ArithmeticOperation) -> None:
|
155
|
-
expr =
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
symbol
|
160
|
-
for handle in vrc.var_handles
|
161
|
-
if isinstance(
|
162
|
-
symbol := self._interpreter.evaluate(handle).value, QuantumSymbol
|
163
|
-
)
|
164
|
-
]
|
165
|
-
expr = rename_variables(
|
166
|
-
expr,
|
137
|
+
expr = self._evaluate_expression(op.expression)
|
138
|
+
symbols = self._get_symbols_in_expression(expr)
|
139
|
+
expr_str = rename_variables(
|
140
|
+
expr.expr,
|
167
141
|
{str(symbol.handle): symbol.handle.identifier for symbol in symbols}
|
168
142
|
| {symbol.handle.qmod_expr: symbol.handle.identifier for symbol in symbols},
|
169
143
|
)
|
170
144
|
for symbol in symbols:
|
171
|
-
|
145
|
+
expr_str = expr_str.replace(
|
146
|
+
symbol.handle.qmod_expr, symbol.handle.identifier
|
147
|
+
)
|
172
148
|
result_type = compute_arithmetic_result_type(
|
173
|
-
|
149
|
+
expr_str,
|
174
150
|
{symbol.handle.identifier: symbol.quantum_type for symbol in symbols},
|
175
151
|
self._machine_precision,
|
176
152
|
)
|
@@ -178,3 +154,13 @@ class ShallowEmitter(Emitter[QuantumOperation]):
|
|
178
154
|
copy_type_information(
|
179
155
|
result_type, result_symbol.quantum_type, str(op.result_var)
|
180
156
|
)
|
157
|
+
|
158
|
+
def _get_symbols_in_expression(self, expr: Expression) -> list[QuantumSymbol]:
|
159
|
+
vrc = VarRefCollector(ignore_duplicated_handles=True)
|
160
|
+
vrc.visit(ast.parse(expr.expr))
|
161
|
+
handles = dict.fromkeys(
|
162
|
+
handle
|
163
|
+
for handle in vrc.var_handles
|
164
|
+
if isinstance(self._current_scope[handle.name].value, QuantumSymbol)
|
165
|
+
)
|
166
|
+
return [self._interpreter.evaluate(handle).value for handle in handles]
|