classiq 0.69.0__py3-none-any.whl → 0.71.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/analyzer/analyzer.py +0 -18
- classiq/analyzer/url_utils.py +9 -4
- classiq/applications/combinatorial_helpers/pyomo_utils.py +2 -0
- classiq/interface/_version.py +1 -1
- classiq/interface/backend/quantum_backend_providers.py +6 -0
- classiq/interface/chemistry/operator.py +1 -21
- classiq/interface/debug_info/debug_info.py +4 -0
- classiq/interface/executor/quantum_instruction_set.py +1 -0
- classiq/interface/generator/arith/arithmetic.py +21 -6
- classiq/interface/generator/circuit_code/circuit_code.py +4 -0
- classiq/interface/generator/circuit_code/types_and_constants.py +1 -0
- classiq/interface/generator/expressions/atomic_expression_functions.py +1 -2
- classiq/interface/generator/expressions/expression_constants.py +0 -3
- classiq/interface/generator/expressions/expression_types.py +12 -4
- classiq/interface/generator/expressions/proxies/__init__.py +0 -0
- classiq/interface/generator/expressions/proxies/classical/__init__.py +0 -0
- classiq/interface/generator/expressions/proxies/classical/classical_array_proxy.py +79 -0
- classiq/interface/generator/expressions/proxies/classical/classical_proxy.py +26 -0
- classiq/interface/generator/expressions/proxies/classical/classical_scalar_proxy.py +32 -0
- classiq/interface/generator/expressions/proxies/classical/classical_struct_proxy.py +35 -0
- classiq/interface/generator/expressions/proxies/classical/utils.py +34 -0
- classiq/interface/generator/expressions/proxies/quantum/__init__.py +0 -0
- classiq/interface/generator/expressions/{qmod_qarray_proxy.py → proxies/quantum/qmod_qarray_proxy.py} +3 -1
- classiq/interface/generator/expressions/{qmod_qscalar_proxy.py → proxies/quantum/qmod_qscalar_proxy.py} +3 -1
- classiq/interface/generator/expressions/{qmod_qstruct_proxy.py → proxies/quantum/qmod_qstruct_proxy.py} +3 -1
- classiq/interface/generator/functions/classical_type.py +24 -30
- classiq/interface/generator/functions/type_name.py +42 -2
- classiq/interface/generator/functions/type_qualifier.py +7 -0
- classiq/interface/generator/generated_circuit_data.py +22 -4
- classiq/interface/generator/model/preferences/preferences.py +1 -0
- classiq/interface/generator/quantum_function_call.py +8 -1
- classiq/interface/generator/quantum_program.py +0 -1
- classiq/interface/generator/synthesis_execution_parameter.py +1 -0
- classiq/interface/generator/types/compilation_metadata.py +1 -0
- classiq/interface/ide/visual_model.py +1 -0
- classiq/interface/interface_version.py +1 -1
- classiq/interface/model/allocate.py +7 -0
- classiq/interface/model/block.py +12 -0
- classiq/interface/model/classical_if.py +4 -0
- classiq/interface/model/inplace_binary_operation.py +4 -0
- classiq/interface/model/model.py +3 -1
- classiq/interface/model/native_function_definition.py +0 -10
- classiq/interface/model/phase_operation.py +4 -0
- classiq/interface/model/port_declaration.py +3 -0
- classiq/interface/model/power.py +4 -0
- classiq/interface/model/quantum_expressions/quantum_expression.py +4 -0
- classiq/interface/model/quantum_function_call.py +4 -0
- classiq/interface/model/quantum_function_declaration.py +1 -1
- classiq/interface/model/quantum_statement.py +5 -0
- classiq/interface/model/quantum_type.py +37 -3
- classiq/interface/model/repeat.py +4 -0
- classiq/interface/model/statement_block.py +3 -0
- classiq/interface/model/variable_declaration_statement.py +5 -0
- classiq/model_expansions/atomic_expression_functions_defs.py +9 -3
- classiq/model_expansions/capturing/captured_vars.py +156 -34
- classiq/model_expansions/evaluators/arg_type_match.py +4 -2
- classiq/model_expansions/evaluators/classical_expression.py +2 -2
- classiq/model_expansions/evaluators/classical_type_inference.py +70 -0
- classiq/model_expansions/evaluators/control.py +1 -1
- classiq/model_expansions/evaluators/parameter_types.py +72 -16
- classiq/model_expansions/evaluators/quantum_type_utils.py +7 -57
- classiq/model_expansions/expression_evaluator.py +3 -12
- classiq/model_expansions/function_builder.py +2 -8
- classiq/model_expansions/generative_functions.py +39 -3
- classiq/model_expansions/interpreters/base_interpreter.py +3 -4
- classiq/model_expansions/quantum_operations/arithmetic/__init__.py +0 -0
- classiq/model_expansions/quantum_operations/arithmetic/explicit_boolean_expressions.py +60 -0
- classiq/model_expansions/quantum_operations/assignment_result_processor.py +9 -0
- classiq/model_expansions/quantum_operations/call_emitter.py +46 -6
- classiq/model_expansions/quantum_operations/emitter.py +41 -0
- classiq/model_expansions/quantum_operations/expression_evaluator.py +4 -0
- classiq/model_expansions/quantum_operations/quantum_function_call.py +0 -22
- classiq/model_expansions/scope.py +7 -14
- classiq/model_expansions/scope_initialization.py +32 -39
- classiq/model_expansions/transformers/model_renamer.py +13 -4
- classiq/model_expansions/visitors/variable_references.py +8 -4
- classiq/open_library/functions/__init__.py +2 -0
- classiq/open_library/functions/lookup_table.py +58 -0
- classiq/qmod/__init__.py +3 -1
- classiq/qmod/declaration_inferrer.py +55 -25
- classiq/qmod/native/pretty_printer.py +25 -3
- classiq/qmod/pretty_print/pretty_printer.py +31 -14
- classiq/qmod/python_classical_type.py +12 -1
- classiq/qmod/qfunc.py +33 -8
- classiq/qmod/qmod_parameter.py +8 -0
- classiq/qmod/qmod_variable.py +189 -151
- classiq/qmod/quantum_function.py +3 -4
- classiq/qmod/semantics/annotation/call_annotation.py +0 -28
- classiq/qmod/semantics/annotation/qstruct_annotator.py +21 -1
- classiq/qmod/semantics/validation/main_validation.py +1 -1
- classiq/qmod/semantics/validation/type_hints.py +38 -0
- classiq/qmod/utilities.py +38 -1
- {classiq-0.69.0.dist-info → classiq-0.71.0.dist-info}/METADATA +10 -12
- {classiq-0.69.0.dist-info → classiq-0.71.0.dist-info}/RECORD +97 -82
- {classiq-0.69.0.dist-info → classiq-0.71.0.dist-info}/WHEEL +1 -1
- /classiq/interface/generator/expressions/{qmod_struct_instance.py → proxies/classical/qmod_struct_instance.py} +0 -0
- /classiq/interface/generator/expressions/{qmod_sized_proxy.py → proxies/quantum/qmod_sized_proxy.py} +0 -0
@@ -1,5 +1,4 @@
|
|
1
1
|
import ast
|
2
|
-
import re
|
3
2
|
from collections.abc import Mapping
|
4
3
|
from enum import EnumMeta
|
5
4
|
from typing import Any, Optional
|
@@ -12,12 +11,11 @@ from classiq.interface.generator.expressions.evaluated_expression import (
|
|
12
11
|
EvaluatedExpression,
|
13
12
|
)
|
14
13
|
from classiq.interface.generator.expressions.expression import Expression
|
15
|
-
from classiq.interface.generator.expressions.expression_constants import (
|
16
|
-
CPARAM_EXECUTION_SUFFIX_PATTERN,
|
17
|
-
)
|
18
14
|
from classiq.interface.generator.expressions.expression_types import ExpressionValue
|
19
15
|
from classiq.interface.generator.expressions.non_symbolic_expr import NonSymbolicExpr
|
20
|
-
from classiq.interface.generator.expressions.qmod_sized_proxy import
|
16
|
+
from classiq.interface.generator.expressions.proxies.quantum.qmod_sized_proxy import (
|
17
|
+
QmodSizedProxy,
|
18
|
+
)
|
21
19
|
from classiq.interface.generator.expressions.sympy_supported_expressions import (
|
22
20
|
SYMPY_SUPPORTED_EXPRESSIONS,
|
23
21
|
)
|
@@ -113,13 +111,6 @@ def _validate_undefined_vars(
|
|
113
111
|
- uninitialized_locals
|
114
112
|
)
|
115
113
|
|
116
|
-
# Ignore expanded execution parameters
|
117
|
-
undefined_vars = {
|
118
|
-
var
|
119
|
-
for var in undefined_vars
|
120
|
-
if not re.search(CPARAM_EXECUTION_SUFFIX_PATTERN, var)
|
121
|
-
}
|
122
|
-
|
123
114
|
if len(undefined_vars) == 1:
|
124
115
|
undefined_var = undefined_vars.__iter__().__next__()
|
125
116
|
raise ClassiqExpansionError(f"Variable {undefined_var!r} is undefined")
|
@@ -15,7 +15,6 @@ from classiq.interface.model.model import MAIN_FUNCTION_NAME
|
|
15
15
|
from classiq.interface.model.native_function_definition import (
|
16
16
|
NativeFunctionDefinition,
|
17
17
|
)
|
18
|
-
from classiq.interface.model.port_declaration import PortDeclaration
|
19
18
|
from classiq.interface.model.quantum_function_declaration import (
|
20
19
|
PositionalArg,
|
21
20
|
)
|
@@ -210,19 +209,14 @@ class OperationBuilder:
|
|
210
209
|
self._current_source_ref = previous_source_ref
|
211
210
|
|
212
211
|
def create_definition(
|
213
|
-
self, function_context: FunctionContext
|
212
|
+
self, function_context: FunctionContext, params: Sequence[PositionalArg]
|
214
213
|
) -> NativeFunctionDefinition:
|
215
214
|
name = self._get_expanded_function_name(function_context)
|
216
|
-
new_parameters: list[PortDeclaration] = [
|
217
|
-
param
|
218
|
-
for param in function_context.positional_arg_declarations
|
219
|
-
if isinstance(param, PortDeclaration)
|
220
|
-
]
|
221
215
|
|
222
216
|
return NativeFunctionDefinition(
|
223
217
|
name=name,
|
224
218
|
body=function_context.body,
|
225
|
-
positional_arg_declarations=
|
219
|
+
positional_arg_declarations=params,
|
226
220
|
)
|
227
221
|
|
228
222
|
def _get_expanded_function_name(self, function_context: FunctionContext) -> str:
|
@@ -1,9 +1,20 @@
|
|
1
1
|
from collections.abc import Mapping
|
2
|
+
from sys import exc_info
|
3
|
+
from types import TracebackType
|
2
4
|
from typing import TYPE_CHECKING, Any
|
3
5
|
|
4
|
-
from classiq.interface.
|
6
|
+
from classiq.interface.exceptions import (
|
7
|
+
ClassiqExpansionError,
|
8
|
+
)
|
9
|
+
from classiq.interface.generator.expressions.proxies.classical.classical_proxy import (
|
10
|
+
ClassicalProxy,
|
11
|
+
)
|
12
|
+
from classiq.interface.generator.expressions.proxies.classical.qmod_struct_instance import (
|
5
13
|
QmodStructInstance,
|
6
14
|
)
|
15
|
+
from classiq.interface.generator.expressions.proxies.classical.utils import (
|
16
|
+
get_proxy_type,
|
17
|
+
)
|
7
18
|
from classiq.interface.generator.functions.type_name import Struct
|
8
19
|
from classiq.interface.helpers.pydantic_model_helpers import nameables_to_dict
|
9
20
|
from classiq.interface.model.native_function_definition import NativeFunctionDefinition
|
@@ -22,7 +33,7 @@ from classiq.model_expansions.closure import (
|
|
22
33
|
from classiq.model_expansions.scope import Evaluated, QuantumSymbol
|
23
34
|
from classiq.qmod.generative import generative_mode_context, set_frontend_interpreter
|
24
35
|
from classiq.qmod.model_state_container import QMODULE
|
25
|
-
from classiq.qmod.qmod_parameter import CParamStruct
|
36
|
+
from classiq.qmod.qmod_parameter import CParamStruct, create_param
|
26
37
|
from classiq.qmod.qmod_variable import QNum, _create_qvar_for_qtype
|
27
38
|
from classiq.qmod.quantum_expandable import (
|
28
39
|
QTerminalCallable,
|
@@ -37,6 +48,23 @@ if TYPE_CHECKING:
|
|
37
48
|
)
|
38
49
|
|
39
50
|
|
51
|
+
def _unwrap_traceback_frame(e: Exception) -> Exception:
|
52
|
+
fallback_error = ClassiqExpansionError(str(e))
|
53
|
+
traceback = exc_info()[2]
|
54
|
+
if traceback is None:
|
55
|
+
return fallback_error
|
56
|
+
back_frame = traceback.tb_frame.f_back
|
57
|
+
if back_frame is None:
|
58
|
+
return fallback_error
|
59
|
+
back_tb = TracebackType(
|
60
|
+
tb_next=None,
|
61
|
+
tb_frame=back_frame,
|
62
|
+
tb_lasti=back_frame.f_lasti,
|
63
|
+
tb_lineno=back_frame.f_lineno,
|
64
|
+
)
|
65
|
+
return e.with_traceback(back_tb)
|
66
|
+
|
67
|
+
|
40
68
|
class LenList(list):
|
41
69
|
@property
|
42
70
|
def len(self) -> int:
|
@@ -45,7 +73,10 @@ class LenList(list):
|
|
45
73
|
def __getitem__(self, item: Any) -> Any:
|
46
74
|
if isinstance(item, QNum):
|
47
75
|
return SymbolicExpr(f"{self}[{item}]", True)
|
48
|
-
|
76
|
+
try:
|
77
|
+
return super().__getitem__(item)
|
78
|
+
except (IndexError, TypeError) as e:
|
79
|
+
raise _unwrap_traceback_frame(e) from None
|
49
80
|
|
50
81
|
@classmethod
|
51
82
|
def wrap(cls, obj: Any) -> Any:
|
@@ -90,6 +121,11 @@ def translate_ast_arg_to_python_qmod(param: PositionalArg, evaluated: Evaluated)
|
|
90
121
|
struct_type=Struct(name=classical_value.struct_declaration.name),
|
91
122
|
qmodule=QMODULE,
|
92
123
|
)
|
124
|
+
if isinstance(classical_value, ClassicalProxy):
|
125
|
+
return create_param(
|
126
|
+
str(classical_value.handle), get_proxy_type(classical_value), QMODULE
|
127
|
+
)
|
128
|
+
|
93
129
|
return LenList.wrap(classical_value)
|
94
130
|
|
95
131
|
|
@@ -52,7 +52,6 @@ from classiq.model_expansions.scope import Evaluated, QuantumSymbol, Scope
|
|
52
52
|
from classiq.model_expansions.scope_initialization import (
|
53
53
|
add_entry_point_params_to_scope,
|
54
54
|
init_builtin_types,
|
55
|
-
init_exec_params,
|
56
55
|
init_top_level_scope,
|
57
56
|
)
|
58
57
|
from classiq.model_expansions.utils.counted_name_allocator import CountedNameAllocator
|
@@ -77,7 +76,6 @@ class BaseInterpreter:
|
|
77
76
|
|
78
77
|
init_builtin_types()
|
79
78
|
init_top_level_scope(model, self._top_level_scope)
|
80
|
-
self._exec_params = init_exec_params(model, self._top_level_scope)
|
81
79
|
self._functions_compilation_metadata: dict[str, CompilationMetadata] = dict(
|
82
80
|
self._model.functions_compilation_metadata
|
83
81
|
)
|
@@ -96,7 +94,9 @@ class BaseInterpreter:
|
|
96
94
|
)
|
97
95
|
context = self._expand_operation(main_closure)
|
98
96
|
self._expanded_functions[main_closure.closure_id] = (
|
99
|
-
self._builder.create_definition(
|
97
|
+
self._builder.create_definition(
|
98
|
+
cast(FunctionContext, context), main_closure.positional_arg_declarations
|
99
|
+
)
|
100
100
|
)
|
101
101
|
|
102
102
|
def _get_main_closure(self, main_func: FunctionClosure) -> FunctionClosure:
|
@@ -139,7 +139,6 @@ class BaseInterpreter:
|
|
139
139
|
qstructs=list(QMODULE.qstruct_decls.values()),
|
140
140
|
debug_info=self._model.debug_info,
|
141
141
|
functions_compilation_metadata=self._expanded_functions_compilation_metadata,
|
142
|
-
execution_parameters=self._exec_params,
|
143
142
|
)
|
144
143
|
|
145
144
|
def process_exception(self, e: Exception) -> None:
|
File without changes
|
@@ -0,0 +1,60 @@
|
|
1
|
+
from classiq.interface.exceptions import ClassiqValueError
|
2
|
+
from classiq.interface.generator.arith.arithmetic import is_bool
|
3
|
+
from classiq.interface.generator.expressions.expression import Expression
|
4
|
+
from classiq.interface.model.quantum_expressions.arithmetic_operation import (
|
5
|
+
ArithmeticOperation,
|
6
|
+
ArithmeticOperationKind,
|
7
|
+
)
|
8
|
+
from classiq.interface.model.quantum_type import QuantumBit, QuantumNumeric
|
9
|
+
|
10
|
+
from classiq.model_expansions.scope import QuantumSymbol
|
11
|
+
|
12
|
+
|
13
|
+
def validate_assignment_bool_expression(
|
14
|
+
result_symbol: QuantumSymbol, expr: str, op_kind: ArithmeticOperationKind
|
15
|
+
) -> None:
|
16
|
+
if not is_bool(expr):
|
17
|
+
return
|
18
|
+
_validate_target_type(result_symbol, expr, op_kind)
|
19
|
+
|
20
|
+
|
21
|
+
def _validate_target_type(
|
22
|
+
target_symbol: QuantumSymbol, expr: str, op_kind: ArithmeticOperationKind
|
23
|
+
) -> None:
|
24
|
+
supported_types = _supported_types()
|
25
|
+
if target_symbol.quantum_type.qmod_type_name not in supported_types:
|
26
|
+
raise ClassiqValueError(
|
27
|
+
f'The expression has been evaluated to "{expr}" which is a Boolean value. '
|
28
|
+
f"Cannot perform {op_kind.value} operation of Boolean expression to result variable '{target_symbol.handle}' of type {target_symbol.quantum_type.qmod_type_name}. "
|
29
|
+
f"Boolean expressions can only be applied on {' or '.join(supported_types)}."
|
30
|
+
)
|
31
|
+
|
32
|
+
|
33
|
+
def convert_assignment_bool_expression(op: ArithmeticOperation) -> None:
|
34
|
+
if not is_bool(op.expression.expr):
|
35
|
+
return
|
36
|
+
op.expression = op.expression.model_copy(
|
37
|
+
update=dict(expr="1" if op.expression.expr == "True" else "0")
|
38
|
+
)
|
39
|
+
|
40
|
+
|
41
|
+
def convert_inplace_op_bool_expression(
|
42
|
+
op: ArithmeticOperation, target: QuantumSymbol
|
43
|
+
) -> None:
|
44
|
+
if not is_bool(op.expression.expr):
|
45
|
+
return
|
46
|
+
_validate_target_type(target, op.expression.expr, op.operation_kind)
|
47
|
+
op.expression = op.expression.model_copy(
|
48
|
+
update=dict(expr="1" if op.expression.expr == "True" else "0")
|
49
|
+
)
|
50
|
+
|
51
|
+
|
52
|
+
def _supported_types() -> tuple[str, ...]:
|
53
|
+
return (
|
54
|
+
QuantumBit().qmod_type_name,
|
55
|
+
QuantumNumeric(
|
56
|
+
size=Expression(expr="1"),
|
57
|
+
is_signed=Expression(expr="False"),
|
58
|
+
fraction_digits=Expression(expr="0"),
|
59
|
+
).qmod_type_name,
|
60
|
+
)
|
@@ -11,6 +11,10 @@ from classiq.interface.model.quantum_expressions.quantum_expression import (
|
|
11
11
|
)
|
12
12
|
|
13
13
|
from classiq.model_expansions.evaluators.quantum_type_utils import copy_type_information
|
14
|
+
from classiq.model_expansions.quantum_operations.arithmetic.explicit_boolean_expressions import (
|
15
|
+
convert_assignment_bool_expression,
|
16
|
+
validate_assignment_bool_expression,
|
17
|
+
)
|
14
18
|
from classiq.model_expansions.quantum_operations.emitter import Emitter
|
15
19
|
from classiq.model_expansions.scope import QuantumSymbol
|
16
20
|
from classiq.model_expansions.transformers.ast_renamer import rename_variables
|
@@ -24,6 +28,7 @@ class AssignmentResultProcessor(Emitter[QuantumAssignmentOperation]):
|
|
24
28
|
):
|
25
29
|
direction = PortDeclarationDirection.Output
|
26
30
|
self._update_result_type(op)
|
31
|
+
convert_assignment_bool_expression(op)
|
27
32
|
else:
|
28
33
|
direction = PortDeclarationDirection.Inout
|
29
34
|
self._capture_handle(op.result_var, direction)
|
@@ -47,6 +52,10 @@ class AssignmentResultProcessor(Emitter[QuantumAssignmentOperation]):
|
|
47
52
|
self._machine_precision,
|
48
53
|
)
|
49
54
|
result_symbol = self._interpreter.evaluate(op.result_var).as_type(QuantumSymbol)
|
55
|
+
|
56
|
+
validate_assignment_bool_expression(
|
57
|
+
result_symbol, op.expression.expr, op.operation_kind
|
58
|
+
) # must be here, otherwise copy_type_information will throw a non-indicative error
|
50
59
|
copy_type_information(
|
51
60
|
result_type, result_symbol.quantum_type, str(op.result_var)
|
52
61
|
)
|
@@ -2,17 +2,29 @@ from collections.abc import Sequence
|
|
2
2
|
from itertools import chain, combinations
|
3
3
|
from typing import (
|
4
4
|
TYPE_CHECKING,
|
5
|
+
Any,
|
5
6
|
Generic,
|
6
7
|
cast,
|
7
8
|
)
|
8
9
|
from uuid import UUID
|
9
10
|
|
11
|
+
import sympy
|
12
|
+
|
10
13
|
from classiq.interface.debug_info.debug_info import FunctionDebugInfo
|
11
14
|
from classiq.interface.exceptions import ClassiqExpansionError
|
15
|
+
from classiq.interface.generator.expressions.proxies.classical.classical_proxy import (
|
16
|
+
ClassicalProxy,
|
17
|
+
)
|
18
|
+
from classiq.interface.generator.expressions.proxies.classical.qmod_struct_instance import (
|
19
|
+
QmodStructInstance,
|
20
|
+
)
|
12
21
|
from classiq.interface.generator.functions.port_declaration import (
|
13
22
|
PortDeclarationDirection,
|
14
23
|
)
|
15
24
|
from classiq.interface.generator.generated_circuit_data import OperationLevel
|
25
|
+
from classiq.interface.model.classical_parameter_declaration import (
|
26
|
+
ClassicalParameterDeclaration,
|
27
|
+
)
|
16
28
|
from classiq.interface.model.handle_binding import HandleBinding
|
17
29
|
from classiq.interface.model.native_function_definition import NativeFunctionDefinition
|
18
30
|
from classiq.interface.model.port_declaration import PortDeclaration
|
@@ -75,6 +87,16 @@ def _validate_cloning(evaluated_args: list[Evaluated]) -> None:
|
|
75
87
|
)
|
76
88
|
|
77
89
|
|
90
|
+
def _is_symbolic(arg: Any) -> bool:
|
91
|
+
if isinstance(arg, list):
|
92
|
+
return any(_is_symbolic(item) for item in arg)
|
93
|
+
if isinstance(arg, QmodStructInstance):
|
94
|
+
return any(_is_symbolic(item) for item in arg.fields.values())
|
95
|
+
if isinstance(arg, sympy.Basic):
|
96
|
+
return len(arg.free_symbols) > 0
|
97
|
+
return isinstance(arg, ClassicalProxy)
|
98
|
+
|
99
|
+
|
78
100
|
class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSplitter):
|
79
101
|
def __init__(self, interpreter: "BaseInterpreter") -> None:
|
80
102
|
Emitter.__init__(self, interpreter)
|
@@ -197,7 +219,7 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
|
|
197
219
|
].occurrences_number += 1
|
198
220
|
return function_def
|
199
221
|
|
200
|
-
function_def = self._create_function_definition(function_context)
|
222
|
+
function_def = self._create_function_definition(function_context, args)
|
201
223
|
self._expanded_functions[closure_id] = function_def
|
202
224
|
self._top_level_scope[function_def.name] = Evaluated(
|
203
225
|
value=function_context.closure.with_new_declaration(function_def)
|
@@ -210,14 +232,23 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
|
|
210
232
|
return function_def
|
211
233
|
|
212
234
|
def _create_function_definition(
|
213
|
-
self, function_context: FunctionContext
|
235
|
+
self, function_context: FunctionContext, args: list[Evaluated]
|
214
236
|
) -> NativeFunctionDefinition:
|
215
|
-
|
237
|
+
params = [
|
238
|
+
param
|
239
|
+
for arg, param in zip(args, function_context.positional_arg_declarations)
|
240
|
+
if isinstance(param, PortDeclaration)
|
241
|
+
or (
|
242
|
+
isinstance(param, ClassicalParameterDeclaration)
|
243
|
+
and _is_symbolic(arg.value)
|
244
|
+
)
|
245
|
+
]
|
246
|
+
func_def = self._builder.create_definition(function_context, params)
|
216
247
|
|
217
248
|
captured_vars = function_context.closure.captured_vars.filter_vars(
|
218
249
|
function_context.closure
|
219
250
|
)
|
220
|
-
captured_ports = captured_vars.
|
251
|
+
captured_ports = captured_vars.get_captured_parameters()
|
221
252
|
if len(captured_ports) == 0:
|
222
253
|
return func_def
|
223
254
|
func_def.positional_arg_declarations = list(
|
@@ -239,15 +270,22 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
|
|
239
270
|
closure: FunctionClosure,
|
240
271
|
) -> None:
|
241
272
|
for parameter, argument in zip(parameters, arguments):
|
273
|
+
param_handle = HandleBinding(name=parameter.name)
|
242
274
|
if isinstance(argument.value, QuantumSymbol):
|
243
275
|
assert isinstance(parameter, PortDeclaration)
|
244
276
|
closure.scope[parameter.name] = Evaluated(
|
245
277
|
QuantumSymbol(
|
246
|
-
handle=
|
278
|
+
handle=param_handle,
|
247
279
|
quantum_type=parameter.quantum_type,
|
248
280
|
),
|
249
281
|
defining_function=closure,
|
250
282
|
)
|
283
|
+
elif _is_symbolic(argument.value):
|
284
|
+
assert isinstance(parameter, ClassicalParameterDeclaration)
|
285
|
+
closure.scope[parameter.name] = Evaluated(
|
286
|
+
value=parameter.classical_type.get_classical_proxy(param_handle),
|
287
|
+
defining_function=closure,
|
288
|
+
)
|
251
289
|
else:
|
252
290
|
closure.scope[parameter.name] = argument
|
253
291
|
|
@@ -264,7 +302,9 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
|
|
264
302
|
return [arg.emit() for arg in evaluated_args]
|
265
303
|
|
266
304
|
positional_args = [
|
267
|
-
arg.emit()
|
305
|
+
arg.emit()
|
306
|
+
for arg in evaluated_args
|
307
|
+
if isinstance(arg.value, QuantumSymbol) or _is_symbolic(arg.value)
|
268
308
|
]
|
269
309
|
|
270
310
|
return positional_args
|
@@ -19,6 +19,13 @@ from classiq.interface.generator.expressions.evaluated_expression import (
|
|
19
19
|
EvaluatedExpression,
|
20
20
|
)
|
21
21
|
from classiq.interface.generator.expressions.expression import Expression
|
22
|
+
from classiq.interface.generator.expressions.proxies.classical.classical_proxy import (
|
23
|
+
ClassicalProxy,
|
24
|
+
)
|
25
|
+
from classiq.interface.generator.expressions.proxies.classical.utils import (
|
26
|
+
get_proxy_type,
|
27
|
+
)
|
28
|
+
from classiq.interface.generator.functions.classical_type import ClassicalType
|
22
29
|
from classiq.interface.generator.functions.port_declaration import (
|
23
30
|
PortDeclarationDirection,
|
24
31
|
)
|
@@ -145,6 +152,7 @@ class Emitter(Generic[QuantumStatementT], ABC):
|
|
145
152
|
return new_expression
|
146
153
|
|
147
154
|
def emit_statement(self, statement: QuantumStatement) -> None:
|
155
|
+
self._update_captured_classical_vars(statement)
|
148
156
|
if isinstance(statement, QuantumOperation):
|
149
157
|
self._update_captured_vars(statement)
|
150
158
|
if statement.uuid not in self._interpreter._model.debug_info:
|
@@ -153,6 +161,11 @@ class Emitter(Generic[QuantumStatementT], ABC):
|
|
153
161
|
)
|
154
162
|
self._builder.emit_statement(statement)
|
155
163
|
|
164
|
+
def _update_captured_classical_vars(self, stmt: QuantumStatement) -> None:
|
165
|
+
for expr in stmt.expressions:
|
166
|
+
for var_name, var_type in self._get_classical_vars_in_expression(expr):
|
167
|
+
self._capture_classical_var(var_name, var_type)
|
168
|
+
|
156
169
|
def _update_captured_vars(self, op: QuantumOperation) -> None:
|
157
170
|
handles = (
|
158
171
|
[(handle, PortDeclarationDirection.Input) for handle in op.inputs]
|
@@ -178,6 +191,18 @@ class Emitter(Generic[QuantumStatementT], ABC):
|
|
178
191
|
direction=direction,
|
179
192
|
)
|
180
193
|
|
194
|
+
def _capture_classical_var(self, var_name: str, var_type: ClassicalType) -> None:
|
195
|
+
if var_name not in self._current_scope:
|
196
|
+
return
|
197
|
+
defining_function = self._current_scope[var_name].defining_function
|
198
|
+
if defining_function is None:
|
199
|
+
raise ClassiqInternalExpansionError
|
200
|
+
self._builder.current_block.captured_vars.capture_classical_var(
|
201
|
+
var_name=var_name,
|
202
|
+
var_type=var_type,
|
203
|
+
defining_function=defining_function,
|
204
|
+
)
|
205
|
+
|
181
206
|
def _get_symbols_in_expression(self, expr: Expression) -> list[QuantumSymbol]:
|
182
207
|
vrc = VarRefCollector(ignore_duplicated_handles=True)
|
183
208
|
vrc.visit(ast.parse(expr.expr))
|
@@ -187,3 +212,19 @@ class Emitter(Generic[QuantumStatementT], ABC):
|
|
187
212
|
if isinstance(self._current_scope[handle.name].value, QuantumSymbol)
|
188
213
|
)
|
189
214
|
return [self._interpreter.evaluate(handle).value for handle in handles]
|
215
|
+
|
216
|
+
def _get_classical_vars_in_expression(
|
217
|
+
self, expr: Expression
|
218
|
+
) -> list[tuple[str, ClassicalType]]:
|
219
|
+
vrc = VarRefCollector(ignore_duplicated_handles=True, ignore_sympy_symbols=True)
|
220
|
+
vrc.visit(ast.parse(expr.expr))
|
221
|
+
return list(
|
222
|
+
{
|
223
|
+
handle.name: get_proxy_type(proxy)
|
224
|
+
for handle in vrc.var_handles
|
225
|
+
if handle.name in self._current_scope
|
226
|
+
and isinstance(
|
227
|
+
proxy := self._current_scope[handle.name].value, ClassicalProxy
|
228
|
+
)
|
229
|
+
}.items()
|
230
|
+
)
|
@@ -26,6 +26,10 @@ class ExpressionEvaluator(Emitter[QuantumOperation]):
|
|
26
26
|
)
|
27
27
|
for symbol in self._get_symbols_in_expression(evaluated_expression):
|
28
28
|
self._capture_handle(symbol.handle, PortDeclarationDirection.Inout)
|
29
|
+
for var_name, var_type in self._get_classical_vars_in_expression(
|
30
|
+
evaluated_expression
|
31
|
+
):
|
32
|
+
self._capture_classical_var(var_name, var_type)
|
29
33
|
op = op.model_copy(
|
30
34
|
update={self._expression_name: evaluated_expression, "back_ref": op.uuid}
|
31
35
|
)
|
@@ -1,9 +1,5 @@
|
|
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
|
7
3
|
from classiq.interface.model.quantum_function_call import QuantumFunctionCall
|
8
4
|
|
9
5
|
from classiq.model_expansions.closure import FunctionClosure
|
@@ -17,21 +13,12 @@ if TYPE_CHECKING:
|
|
17
13
|
from classiq.model_expansions.interpreters.base_interpreter import BaseInterpreter
|
18
14
|
|
19
15
|
|
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
|
-
|
26
16
|
class QuantumFunctionCallEmitter(CallEmitter[QuantumFunctionCall]):
|
27
17
|
def __init__(self, interpreter: "BaseInterpreter") -> None:
|
28
18
|
super().__init__(interpreter)
|
29
19
|
self._model = self._interpreter._model
|
30
20
|
|
31
21
|
def emit(self, call: QuantumFunctionCall, /) -> bool:
|
32
|
-
if call.function == "allocate": # FIXME: Remove compatibility (CAD-25935)
|
33
|
-
self._allocate_compatibility(call)
|
34
|
-
return True
|
35
22
|
function = self._interpreter.evaluate(call.function).as_type(FunctionClosure)
|
36
23
|
args = call.positional_args
|
37
24
|
with ErrorManager().call(function.name):
|
@@ -40,15 +27,6 @@ class QuantumFunctionCallEmitter(CallEmitter[QuantumFunctionCall]):
|
|
40
27
|
)
|
41
28
|
return True
|
42
29
|
|
43
|
-
def _allocate_compatibility(self, call: QuantumFunctionCall) -> None:
|
44
|
-
if len(call.positional_args) != 2:
|
45
|
-
raise ClassiqValueError(ALLOCATE_COMPATIBILITY_ERROR_MESSAGE)
|
46
|
-
size, target = call.positional_args
|
47
|
-
if not isinstance(size, Expression) or not isinstance(target, HandleBinding):
|
48
|
-
raise ClassiqValueError(ALLOCATE_COMPATIBILITY_ERROR_MESSAGE)
|
49
|
-
allocate = Allocate(size=size, target=target, source_ref=call.source_ref)
|
50
|
-
self._interpreter.emit_statement(allocate)
|
51
|
-
|
52
30
|
|
53
31
|
class DeclarativeQuantumFunctionCallEmitter(
|
54
32
|
QuantumFunctionCallEmitter, DeclarativeCallEmitter
|
@@ -1,13 +1,10 @@
|
|
1
1
|
import itertools
|
2
|
-
import re
|
3
2
|
from collections import UserDict
|
4
3
|
from collections.abc import Iterator
|
5
4
|
from dataclasses import dataclass
|
6
5
|
from functools import singledispatch
|
7
6
|
from typing import TYPE_CHECKING, Any, Optional, TypeVar, Union
|
8
7
|
|
9
|
-
from sympy import Symbol
|
10
|
-
|
11
8
|
from classiq.interface.exceptions import (
|
12
9
|
ClassiqExpansionError,
|
13
10
|
ClassiqInternalExpansionError,
|
@@ -16,10 +13,7 @@ from classiq.interface.generator.expressions.evaluated_expression import (
|
|
16
13
|
EvaluatedExpression,
|
17
14
|
)
|
18
15
|
from classiq.interface.generator.expressions.expression import Expression
|
19
|
-
from classiq.interface.generator.expressions.
|
20
|
-
CPARAM_EXECUTION_SUFFIX_PATTERN,
|
21
|
-
)
|
22
|
-
from classiq.interface.generator.expressions.qmod_struct_instance import (
|
16
|
+
from classiq.interface.generator.expressions.proxies.classical.qmod_struct_instance import (
|
23
17
|
QmodStructInstance,
|
24
18
|
)
|
25
19
|
from classiq.interface.generator.functions.type_name import TypeName
|
@@ -72,9 +66,9 @@ class QuantumSymbol:
|
|
72
66
|
array_length = self.quantum_type.length_value
|
73
67
|
if start < 0 or end > array_length:
|
74
68
|
raise ClassiqExpansionError(
|
75
|
-
f"Slice [{start}:{end}] is out of bounds
|
76
|
-
f"{self.quantum_type.type_name} {str(self.handle)!r} of
|
77
|
-
f"{array_length}"
|
69
|
+
f"Slice [{start}:{end}] is out of bounds for "
|
70
|
+
f"{self.quantum_type.type_name.lower()} {str(self.handle)!r} (of "
|
71
|
+
f"length {array_length})"
|
78
72
|
)
|
79
73
|
return QuantumSymbol(
|
80
74
|
handle=SlicedHandleBinding(
|
@@ -96,8 +90,9 @@ class QuantumSymbol:
|
|
96
90
|
array_length = self.quantum_type.length_value
|
97
91
|
if index < 0 or index >= array_length:
|
98
92
|
raise ClassiqExpansionError(
|
99
|
-
f"
|
100
|
-
f"{str(self.handle)!r} of
|
93
|
+
f"Index {index} is out of bounds for "
|
94
|
+
f"{self.quantum_type.type_name.lower()} {str(self.handle)!r} (of "
|
95
|
+
f"length {array_length})"
|
101
96
|
)
|
102
97
|
return QuantumSymbol(
|
103
98
|
handle=SubscriptHandleBinding(
|
@@ -203,8 +198,6 @@ class Scope(EvaluatedUserDict):
|
|
203
198
|
return self.data[name]
|
204
199
|
if self._parent is not None:
|
205
200
|
return self._parent[name]
|
206
|
-
if re.search(CPARAM_EXECUTION_SUFFIX_PATTERN, name):
|
207
|
-
return Evaluated(value=Symbol(name))
|
208
201
|
raise ClassiqExpansionError(f"Variable {name!r} is undefined")
|
209
202
|
|
210
203
|
def __contains__(self, item: Any) -> bool:
|
@@ -1,16 +1,20 @@
|
|
1
1
|
from collections.abc import Sequence
|
2
|
-
from typing import Any
|
3
2
|
|
4
|
-
from classiq.interface.exceptions import ClassiqError
|
3
|
+
from classiq.interface.exceptions import ClassiqError, ClassiqInternalExpansionError
|
5
4
|
from classiq.interface.generator.constant import Constant
|
5
|
+
from classiq.interface.generator.functions.classical_type import (
|
6
|
+
ClassicalArray,
|
7
|
+
ClassicalList,
|
8
|
+
)
|
6
9
|
from classiq.interface.generator.functions.concrete_types import ConcreteClassicalType
|
10
|
+
from classiq.interface.model.classical_parameter_declaration import (
|
11
|
+
ClassicalParameterDeclaration,
|
12
|
+
)
|
7
13
|
from classiq.interface.model.handle_binding import HandleBinding
|
8
14
|
from classiq.interface.model.model import MAIN_FUNCTION_NAME, Model
|
9
15
|
from classiq.interface.model.native_function_definition import NativeFunctionDefinition
|
10
16
|
from classiq.interface.model.port_declaration import PortDeclaration
|
11
|
-
from classiq.interface.model.quantum_function_declaration import
|
12
|
-
PositionalArg,
|
13
|
-
)
|
17
|
+
from classiq.interface.model.quantum_function_declaration import PositionalArg
|
14
18
|
|
15
19
|
from classiq.model_expansions.closure import FunctionClosure, GenerativeFunctionClosure
|
16
20
|
from classiq.model_expansions.evaluators.classical_expression import (
|
@@ -101,17 +105,24 @@ def add_entry_point_params_to_scope(
|
|
101
105
|
parameters: Sequence[PositionalArg], main_closure: FunctionClosure
|
102
106
|
) -> None:
|
103
107
|
for parameter in parameters:
|
104
|
-
if
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
108
|
+
if isinstance(parameter, PortDeclaration):
|
109
|
+
main_closure.scope[parameter.name] = Evaluated(
|
110
|
+
value=QuantumSymbol(
|
111
|
+
handle=HandleBinding(name=parameter.name),
|
112
|
+
quantum_type=evaluate_type_in_quantum_symbol(
|
113
|
+
parameter.quantum_type, main_closure.scope, parameter.name
|
114
|
+
),
|
111
115
|
),
|
112
|
-
|
113
|
-
|
114
|
-
)
|
116
|
+
defining_function=main_closure,
|
117
|
+
)
|
118
|
+
elif isinstance(parameter, ClassicalParameterDeclaration):
|
119
|
+
param_val = parameter.classical_type.get_classical_proxy(
|
120
|
+
handle=HandleBinding(name=parameter.name)
|
121
|
+
)
|
122
|
+
main_closure.scope[parameter.name] = Evaluated(
|
123
|
+
value=param_val,
|
124
|
+
defining_function=main_closure,
|
125
|
+
)
|
115
126
|
|
116
127
|
|
117
128
|
def init_top_level_scope(model: Model, scope: Scope) -> None:
|
@@ -125,27 +136,9 @@ def init_builtin_types() -> None:
|
|
125
136
|
QMODULE.type_decls |= BUILTIN_STRUCT_DECLARATIONS
|
126
137
|
|
127
138
|
|
128
|
-
def
|
129
|
-
if
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
def init_exec_params(model: Model, scope: Scope) -> dict[str, ConcreteClassicalType]:
|
137
|
-
if model.execution_parameters is not None:
|
138
|
-
exec_params = model.execution_parameters
|
139
|
-
else:
|
140
|
-
exec_params = {
|
141
|
-
param.name: param.classical_type
|
142
|
-
for param in model.function_dict.get(
|
143
|
-
"_dec_main", model.main_func
|
144
|
-
).param_decls
|
145
|
-
}
|
146
|
-
for param_name, param_type in exec_params.items():
|
147
|
-
param_val = param_type.as_symbolic(param_name)
|
148
|
-
scope[param_name] = Evaluated(value=param_val)
|
149
|
-
if isinstance(param_val, list):
|
150
|
-
_add_exec_param_parts_to_scope(param_val, scope)
|
151
|
-
return exec_params
|
139
|
+
def _get_shape(classical_type: ConcreteClassicalType) -> tuple[int, ...]:
|
140
|
+
if isinstance(classical_type, ClassicalList):
|
141
|
+
raise ClassiqInternalExpansionError("Unexpected classical list")
|
142
|
+
if isinstance(classical_type, ClassicalArray):
|
143
|
+
return classical_type.size, *_get_shape(classical_type.element_type)
|
144
|
+
return ()
|