classiq 0.42.2__py3-none-any.whl → 0.43.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/__init__.py +2 -6
- classiq/_internals/api_wrapper.py +6 -12
- classiq/_internals/authentication/token_manager.py +5 -2
- classiq/_internals/jobs.py +5 -10
- classiq/analyzer/rb.py +3 -3
- classiq/applications/chemistry/chemistry_model_constructor.py +12 -8
- classiq/applications/combinatorial_helpers/pauli_helpers/pauli_utils.py +2 -2
- classiq/applications/finance/finance_model_constructor.py +16 -13
- classiq/applications/qsvm/__init__.py +1 -3
- classiq/applications/qsvm/qsvm_model_constructor.py +7 -6
- classiq/exceptions.py +9 -4
- classiq/execution/execution_session.py +5 -2
- classiq/execution/qnn.py +1 -1
- classiq/executor.py +0 -2
- classiq/interface/_version.py +1 -1
- classiq/interface/chemistry/operator.py +19 -5
- classiq/interface/executor/constants.py +1 -0
- classiq/interface/finance/function_input.py +16 -10
- classiq/interface/generator/application_apis/chemistry_declarations.py +2 -2
- classiq/interface/generator/application_apis/qsvm_declarations.py +4 -2
- classiq/interface/generator/arith/argument_utils.py +20 -3
- classiq/interface/generator/arith/arithmetic_expression_validator.py +3 -26
- classiq/interface/generator/arith/binary_ops.py +8 -14
- classiq/interface/generator/arith/extremum_operations.py +30 -0
- classiq/interface/generator/arith/number_utils.py +1 -1
- classiq/interface/generator/arith/unary_ops.py +1 -3
- classiq/interface/generator/compiler_keywords.py +1 -1
- classiq/interface/generator/expressions/atomic_expression_functions.py +13 -3
- classiq/interface/generator/expressions/enums/__init__.py +0 -20
- classiq/interface/generator/expressions/enums/finance_functions.py +11 -18
- classiq/interface/generator/expressions/non_symbolic_expr.py +119 -0
- classiq/interface/generator/expressions/qmod_qarray_proxy.py +52 -37
- classiq/interface/generator/expressions/qmod_qscalar_proxy.py +16 -11
- classiq/interface/generator/expressions/qmod_sized_proxy.py +5 -5
- classiq/interface/generator/function_param_list_without_self_reference.py +0 -10
- classiq/interface/generator/function_params.py +0 -4
- classiq/interface/generator/functions/__init__.py +0 -20
- classiq/interface/generator/functions/builtins/core_library/exponentiation_functions.py +2 -2
- classiq/interface/generator/functions/builtins/open_lib_functions.py +530 -1
- classiq/interface/generator/functions/classical_type.py +22 -69
- classiq/interface/generator/functions/port_declaration.py +0 -11
- classiq/interface/generator/model/__init__.py +0 -1
- classiq/interface/generator/model/model.py +9 -185
- classiq/interface/generator/synthesis_metadata/synthesis_execution_data.py +3 -1
- classiq/interface/generator/types/builtin_enum_declarations.py +69 -0
- classiq/interface/generator/types/builtin_struct_declarations/pauli_struct_declarations.py +2 -2
- classiq/interface/generator/types/enum_declaration.py +57 -0
- classiq/interface/jobs.py +36 -65
- classiq/interface/model/bind_operation.py +3 -0
- classiq/interface/model/classical_parameter_declaration.py +3 -0
- classiq/interface/model/handle_binding.py +7 -0
- classiq/interface/model/inplace_binary_operation.py +13 -15
- classiq/interface/model/model.py +8 -20
- classiq/interface/model/native_function_definition.py +0 -17
- classiq/interface/model/quantum_function_call.py +63 -182
- classiq/interface/model/quantum_type.py +71 -10
- classiq/interface/server/routes.py +0 -6
- classiq/qmod/__init__.py +3 -3
- classiq/qmod/builtins/__init__.py +10 -1
- classiq/qmod/builtins/classical_execution_primitives.py +4 -2
- classiq/qmod/builtins/enums.py +177 -0
- classiq/qmod/builtins/functions.py +1 -2
- classiq/qmod/builtins/operations.py +2 -4
- classiq/qmod/builtins/structs.py +16 -17
- classiq/qmod/declaration_inferrer.py +23 -20
- classiq/qmod/model_state_container.py +2 -0
- classiq/qmod/native/pretty_printer.py +31 -13
- classiq/qmod/pretty_print/pretty_printer.py +52 -27
- classiq/qmod/qmod_constant.py +7 -3
- classiq/qmod/qmod_parameter.py +2 -1
- classiq/qmod/qmod_struct.py +9 -33
- classiq/qmod/qmod_variable.py +55 -22
- classiq/qmod/quantum_callable.py +6 -1
- classiq/qmod/quantum_expandable.py +29 -11
- classiq/qmod/quantum_function.py +8 -4
- classiq/qmod/semantics/annotation.py +38 -0
- classiq/qmod/semantics/error_manager.py +49 -0
- classiq/qmod/semantics/static_semantics_visitor.py +308 -0
- classiq/qmod/semantics/validation/func_call_validation.py +149 -0
- classiq/qmod/semantics/validation/types_validation.py +21 -0
- classiq/qmod/symbolic.py +6 -6
- classiq/qmod/symbolic_expr.py +26 -11
- classiq/qmod/utilities.py +23 -1
- {classiq-0.42.2.dist-info → classiq-0.43.1.dist-info}/METADATA +2 -2
- {classiq-0.42.2.dist-info → classiq-0.43.1.dist-info}/RECORD +88 -103
- classiq/_internals/_qfunc_ext.py +0 -6
- classiq/applications/libraries/ampltitude_estimation_library.py +0 -11
- classiq/interface/generator/credit_risk_example/linear_gci.py +0 -122
- classiq/interface/generator/credit_risk_example/weighted_adder.py +0 -69
- classiq/interface/generator/expressions/enums/chemistry.py +0 -28
- classiq/interface/generator/expressions/enums/classical_enum.py +0 -20
- classiq/interface/generator/expressions/enums/ladder_operator.py +0 -6
- classiq/interface/generator/expressions/enums/optimizers.py +0 -9
- classiq/interface/generator/expressions/enums/pauli.py +0 -8
- classiq/interface/generator/expressions/enums/qsvm_feature_map_entanglement.py +0 -9
- classiq/interface/generator/functions/foreign_function_definition.py +0 -114
- classiq/interface/generator/functions/function_implementation.py +0 -107
- classiq/interface/generator/functions/native_function_definition.py +0 -155
- classiq/interface/generator/functions/quantum_function_declaration.py +0 -69
- classiq/interface/generator/functions/register.py +0 -44
- classiq/interface/generator/functions/register_mapping_data.py +0 -106
- classiq/interface/generator/inequality_mixer.py +0 -51
- classiq/interface/generator/model/classical_main_validator.py +0 -106
- classiq/interface/generator/range_mixer.py +0 -56
- classiq/interface/generator/state_propagator.py +0 -74
- classiq/interface/model/resolvers/function_call_resolver.py +0 -64
- classiq/interface/model/validations/__init__.py +0 -0
- classiq/interface/model/validations/handle_validation_base.py +0 -55
- classiq/interface/model/validations/handles_validator.py +0 -153
- classiq/interface/model/validations/port_to_wire_name_generator.py +0 -12
- /classiq/{interface/generator/credit_risk_example → qmod/semantics}/__init__.py +0 -0
- /classiq/{interface/model/resolvers → qmod/semantics/validation}/__init__.py +0 -0
- {classiq-0.42.2.dist-info → classiq-0.43.1.dist-info}/WHEEL +0 -0
classiq/qmod/quantum_function.py
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
import ast
|
2
2
|
import functools
|
3
|
+
from dataclasses import is_dataclass
|
4
|
+
from enum import EnumMeta
|
3
5
|
from inspect import isclass
|
4
6
|
from typing import Any, Callable, Dict, List, Optional, Tuple, get_origin
|
5
7
|
|
6
8
|
from classiq.interface.executor.execution_preferences import ExecutionPreferences
|
7
|
-
from classiq.interface.generator.functions.classical_type import CStructBase
|
8
9
|
from classiq.interface.generator.model.constraints import Constraints
|
9
10
|
from classiq.interface.generator.model.preferences.preferences import Preferences
|
10
11
|
from classiq.interface.model.model import Model, SerializedModel
|
@@ -15,7 +16,7 @@ from classiq.interface.model.quantum_function_declaration import (
|
|
15
16
|
|
16
17
|
from classiq.exceptions import ClassiqError
|
17
18
|
from classiq.qmod.classical_function import CFunc
|
18
|
-
from classiq.qmod.declaration_inferrer import
|
19
|
+
from classiq.qmod.declaration_inferrer import infer_func_decl
|
19
20
|
from classiq.qmod.qmod_constant import QConstant
|
20
21
|
from classiq.qmod.qmod_parameter import CArray, CParam
|
21
22
|
from classiq.qmod.qmod_variable import QVar
|
@@ -71,6 +72,7 @@ class QFunc(QExpandable):
|
|
71
72
|
preferences: Optional[Preferences] = None,
|
72
73
|
classical_execution_function: Optional[CFunc] = None,
|
73
74
|
) -> Model:
|
75
|
+
self._qmodule.enum_decls = dict()
|
74
76
|
self._qmodule.type_decls = dict()
|
75
77
|
self._qmodule.native_defs = dict()
|
76
78
|
self._qmodule.constants = dict()
|
@@ -89,6 +91,7 @@ class QFunc(QExpandable):
|
|
89
91
|
return Model(
|
90
92
|
constants=list(self._qmodule.constants.values()),
|
91
93
|
functions=list(self._qmodule.native_defs.values()),
|
94
|
+
enums=list(self._qmodule.enum_decls.values()),
|
92
95
|
types=list(self._qmodule.type_decls.values()),
|
93
96
|
**{key: value for key, value in model_extra_settings if value},
|
94
97
|
)
|
@@ -166,12 +169,13 @@ def _validate_no_gen_params(annotations: Dict[str, Any]) -> None:
|
|
166
169
|
or isclass(annotation)
|
167
170
|
and issubclass(annotation, CParam)
|
168
171
|
or isclass(annotation)
|
169
|
-
and
|
172
|
+
and is_dataclass(annotation)
|
173
|
+
or isclass(annotation)
|
174
|
+
and isinstance(annotation, EnumMeta)
|
170
175
|
or get_origin(annotation) is CArray
|
171
176
|
or (get_origin(annotation) or annotation) is QCallable
|
172
177
|
or (get_origin(annotation) or annotation) is QCallableList
|
173
178
|
or QVar.from_type_hint(annotation) is not None
|
174
|
-
or annotation in ENUM_TYPE_MAPPING
|
175
179
|
)
|
176
180
|
}
|
177
181
|
if _illegal_params:
|
@@ -0,0 +1,38 @@
|
|
1
|
+
from typing import List, Mapping
|
2
|
+
|
3
|
+
from classiq.interface.model.quantum_function_call import QuantumFunctionCall
|
4
|
+
from classiq.interface.model.quantum_function_declaration import (
|
5
|
+
QuantumFunctionDeclaration,
|
6
|
+
)
|
7
|
+
from classiq.interface.model.quantum_lambda_function import (
|
8
|
+
QuantumCallable,
|
9
|
+
QuantumLambdaFunction,
|
10
|
+
QuantumOperand,
|
11
|
+
)
|
12
|
+
|
13
|
+
from classiq.exceptions import ClassiqError
|
14
|
+
|
15
|
+
|
16
|
+
def annotate_function_call_decl(
|
17
|
+
fc: QuantumFunctionCall,
|
18
|
+
function_dict: Mapping[str, QuantumFunctionDeclaration],
|
19
|
+
) -> None:
|
20
|
+
if fc._func_decl is None:
|
21
|
+
func_decl = function_dict.get(fc.func_name)
|
22
|
+
if func_decl is None:
|
23
|
+
raise ClassiqError(
|
24
|
+
f"Error resolving function {fc.func_name}, the function is not found in included library."
|
25
|
+
)
|
26
|
+
fc.set_func_decl(func_decl)
|
27
|
+
|
28
|
+
for name, op in fc.operands.items():
|
29
|
+
op_decl = fc.func_decl.operand_declarations[name]
|
30
|
+
for qlambda in _get_lambda_defs(op):
|
31
|
+
if isinstance(qlambda, QuantumLambdaFunction):
|
32
|
+
qlambda.set_op_decl(op_decl)
|
33
|
+
|
34
|
+
|
35
|
+
def _get_lambda_defs(operand: QuantumOperand) -> List[QuantumCallable]:
|
36
|
+
if isinstance(operand, list):
|
37
|
+
return operand
|
38
|
+
return [operand]
|
@@ -0,0 +1,49 @@
|
|
1
|
+
from contextlib import contextmanager
|
2
|
+
from typing import Iterator, List, Type
|
3
|
+
|
4
|
+
from classiq.interface.ast_node import ASTNode
|
5
|
+
|
6
|
+
|
7
|
+
class ErrorManager:
|
8
|
+
def __new__(cls) -> "ErrorManager":
|
9
|
+
if not hasattr(cls, "_instance"):
|
10
|
+
cls._instance = super().__new__(cls)
|
11
|
+
return cls._instance
|
12
|
+
|
13
|
+
def __init__(self) -> None:
|
14
|
+
if hasattr(self, "_instantiated"):
|
15
|
+
return
|
16
|
+
self._instantiated = True
|
17
|
+
self._errors: List[str] = []
|
18
|
+
self._current_nodes_stack: List[ASTNode] = []
|
19
|
+
|
20
|
+
def add_error(self, error: str) -> None:
|
21
|
+
source_referenced_error = (
|
22
|
+
f"{error}\n\t\tat {self._current_nodes_stack[-1].source_ref}"
|
23
|
+
if self._current_nodes_stack
|
24
|
+
and self._current_nodes_stack[-1].source_ref is not None
|
25
|
+
else error
|
26
|
+
)
|
27
|
+
self._errors.append(source_referenced_error)
|
28
|
+
|
29
|
+
def get_errors(self) -> List[str]:
|
30
|
+
return self._errors
|
31
|
+
|
32
|
+
def clear(self) -> None:
|
33
|
+
self._current_nodes_stack = []
|
34
|
+
self._errors = []
|
35
|
+
|
36
|
+
def has_errors(self) -> bool:
|
37
|
+
return len(self._errors) > 0
|
38
|
+
|
39
|
+
def report_errors(self, error_type: Type[Exception]) -> None:
|
40
|
+
if self.has_errors():
|
41
|
+
errors = self._errors
|
42
|
+
self.clear()
|
43
|
+
raise error_type("\n\t" + "\n\t".join(errors))
|
44
|
+
|
45
|
+
@contextmanager
|
46
|
+
def node_context(self, node: ASTNode) -> Iterator[None]:
|
47
|
+
self._current_nodes_stack.append(node)
|
48
|
+
yield
|
49
|
+
self._current_nodes_stack.pop()
|
@@ -0,0 +1,308 @@
|
|
1
|
+
from contextlib import contextmanager
|
2
|
+
from typing import Any, Dict, Iterator, List, Mapping, Type, Union
|
3
|
+
|
4
|
+
from classiq.interface.generator.function_params import PortDirection
|
5
|
+
from classiq.interface.generator.functions.port_declaration import (
|
6
|
+
PortDeclarationDirection,
|
7
|
+
)
|
8
|
+
from classiq.interface.generator.visitor import Visitor
|
9
|
+
from classiq.interface.model.handle_binding import (
|
10
|
+
HandleBinding,
|
11
|
+
SlicedHandleBinding,
|
12
|
+
SubscriptHandleBinding,
|
13
|
+
)
|
14
|
+
from classiq.interface.model.model import Model
|
15
|
+
from classiq.interface.model.native_function_definition import NativeFunctionDefinition
|
16
|
+
from classiq.interface.model.port_declaration import PortDeclaration
|
17
|
+
from classiq.interface.model.quantum_function_call import QuantumFunctionCall
|
18
|
+
from classiq.interface.model.quantum_function_declaration import (
|
19
|
+
QuantumFunctionDeclaration,
|
20
|
+
QuantumOperandDeclaration,
|
21
|
+
)
|
22
|
+
from classiq.interface.model.quantum_lambda_function import (
|
23
|
+
QuantumLambdaFunction,
|
24
|
+
)
|
25
|
+
from classiq.interface.model.quantum_statement import QuantumOperation
|
26
|
+
from classiq.interface.model.validation_handle import HandleState
|
27
|
+
from classiq.interface.model.variable_declaration_statement import (
|
28
|
+
VariableDeclarationStatement,
|
29
|
+
)
|
30
|
+
from classiq.interface.model.within_apply_operation import WithinApply
|
31
|
+
|
32
|
+
from classiq.exceptions import ClassiqSemanticError
|
33
|
+
from classiq.qmod.semantics.annotation import annotate_function_call_decl
|
34
|
+
from classiq.qmod.semantics.error_manager import ErrorManager
|
35
|
+
from classiq.qmod.semantics.validation.func_call_validation import (
|
36
|
+
validate_call_arguments,
|
37
|
+
)
|
38
|
+
from classiq.qmod.semantics.validation.types_validation import check_duplicate_types
|
39
|
+
|
40
|
+
|
41
|
+
class StaticScope:
|
42
|
+
|
43
|
+
def __init__(
|
44
|
+
self,
|
45
|
+
parameters: List[str],
|
46
|
+
operands: Dict[str, QuantumOperandDeclaration],
|
47
|
+
variables_to_states: Dict[str, HandleState],
|
48
|
+
) -> None:
|
49
|
+
self.parameters = parameters
|
50
|
+
self.operands = operands
|
51
|
+
self.variable_states = variables_to_states
|
52
|
+
|
53
|
+
|
54
|
+
class StaticSemanticsVisitor(Visitor):
|
55
|
+
def __init__(
|
56
|
+
self,
|
57
|
+
functions_dict: Dict[str, QuantumFunctionDeclaration],
|
58
|
+
constants: List[str],
|
59
|
+
) -> None:
|
60
|
+
self._scope: List[StaticScope] = []
|
61
|
+
self._error_manager = ErrorManager()
|
62
|
+
self._functions_dict = functions_dict
|
63
|
+
self._constants = constants
|
64
|
+
|
65
|
+
@property
|
66
|
+
def current_scope(self) -> StaticScope:
|
67
|
+
return self._scope[-1]
|
68
|
+
|
69
|
+
@contextmanager
|
70
|
+
def scoped_visit(self, scope: StaticScope) -> Iterator[None]:
|
71
|
+
self._scope.append(scope)
|
72
|
+
yield
|
73
|
+
self._scope.pop()
|
74
|
+
|
75
|
+
def visit_Model(self, model: Model) -> None:
|
76
|
+
check_duplicate_types([*model.enums, *model.types])
|
77
|
+
self.visit_BaseModel(model)
|
78
|
+
|
79
|
+
def visit_NativeFunctionDefinition(
|
80
|
+
self, func_def: NativeFunctionDefinition
|
81
|
+
) -> None:
|
82
|
+
if len(func_def.body) == 0:
|
83
|
+
return
|
84
|
+
scope = StaticScope(
|
85
|
+
parameters=list(func_def.param_decls.keys()) + self._constants,
|
86
|
+
operands=dict(func_def.operand_declarations),
|
87
|
+
variables_to_states=initialize_variables_to_state(
|
88
|
+
list(func_def.port_declarations.values())
|
89
|
+
),
|
90
|
+
)
|
91
|
+
with self.scoped_visit(scope):
|
92
|
+
self.visit(func_def.body)
|
93
|
+
with self._error_manager.node_context(func_def.body[-1]):
|
94
|
+
for port_decl in func_def.port_declarations.values():
|
95
|
+
handle_state = self.current_scope.variable_states[port_decl.name]
|
96
|
+
expected_terminal_state = EXPECTED_TERMINAL_STATES.get(
|
97
|
+
port_decl.direction
|
98
|
+
)
|
99
|
+
if (
|
100
|
+
expected_terminal_state is not None
|
101
|
+
and handle_state is not expected_terminal_state
|
102
|
+
):
|
103
|
+
self._error_manager.add_error(
|
104
|
+
f"At the end of the function, port `{port_decl.name}` is expected to be {expected_terminal_state.name.lower()} but it isn't"
|
105
|
+
)
|
106
|
+
|
107
|
+
def visit_WithinApply(self, within_apply: WithinApply) -> None:
|
108
|
+
initial_variables_to_state = self.current_scope.variable_states.copy()
|
109
|
+
scope = StaticScope(
|
110
|
+
parameters=self.current_scope.parameters,
|
111
|
+
operands=self.current_scope.operands,
|
112
|
+
variables_to_states=self.current_scope.variable_states.copy(),
|
113
|
+
)
|
114
|
+
with self.scoped_visit(scope):
|
115
|
+
self.visit(within_apply.compute)
|
116
|
+
compute_captured_variables = {
|
117
|
+
var
|
118
|
+
for var, state in self.current_scope.variable_states.items()
|
119
|
+
if var in initial_variables_to_state
|
120
|
+
and state != initial_variables_to_state[var]
|
121
|
+
}
|
122
|
+
self.visit(within_apply.action)
|
123
|
+
variables_to_state = self.current_scope.variable_states.copy()
|
124
|
+
self.current_scope.variable_states.update(
|
125
|
+
{
|
126
|
+
var: state
|
127
|
+
for var, state in variables_to_state.items()
|
128
|
+
if var in self.current_scope.variable_states
|
129
|
+
and var not in compute_captured_variables
|
130
|
+
}
|
131
|
+
)
|
132
|
+
|
133
|
+
def visit_QuantumOperation(self, op: QuantumOperation) -> None:
|
134
|
+
with self._error_manager.node_context(op):
|
135
|
+
if isinstance(op, QuantumFunctionCall):
|
136
|
+
annotate_function_call_decl(
|
137
|
+
op,
|
138
|
+
{
|
139
|
+
**self._functions_dict,
|
140
|
+
**self.current_scope.operands,
|
141
|
+
},
|
142
|
+
)
|
143
|
+
validate_call_arguments(
|
144
|
+
op,
|
145
|
+
{
|
146
|
+
**self._functions_dict,
|
147
|
+
**self.current_scope.operands,
|
148
|
+
},
|
149
|
+
)
|
150
|
+
self._handle_inputs(op.wiring_inputs)
|
151
|
+
self._handle_outputs(op.wiring_outputs)
|
152
|
+
self._handle_inouts(op.wiring_inouts)
|
153
|
+
self.generic_visit(op)
|
154
|
+
|
155
|
+
def visit_VariableDeclarationStatement(
|
156
|
+
self, declaration: VariableDeclarationStatement
|
157
|
+
) -> None:
|
158
|
+
handle_wiring_state = self.current_scope.variable_states.get(declaration.name)
|
159
|
+
if handle_wiring_state is not None:
|
160
|
+
self._error_manager.add_error(
|
161
|
+
f"Trying to declare a variable of the same name as previously declared variable {declaration.name}"
|
162
|
+
)
|
163
|
+
return
|
164
|
+
|
165
|
+
self.current_scope.variable_states[declaration.name] = HandleState.UNINITIALIZED
|
166
|
+
|
167
|
+
def visit_QuantumLambdaFunction(self, lambda_func: QuantumLambdaFunction) -> None:
|
168
|
+
assert lambda_func.func_decl is not None
|
169
|
+
renamed_parameters = [
|
170
|
+
lambda_func.rename_params.get(param, param)
|
171
|
+
for param in self.current_scope.parameters
|
172
|
+
+ list(lambda_func.func_decl.param_decls.keys())
|
173
|
+
]
|
174
|
+
ports = list(lambda_func.func_decl.port_declarations.values())
|
175
|
+
for i, port in enumerate(ports):
|
176
|
+
ports[i] = port.copy(
|
177
|
+
update={"name": lambda_func.rename_params.get(port.name, port.name)}
|
178
|
+
)
|
179
|
+
variables_to_states = self.current_scope.variable_states.copy()
|
180
|
+
original_operands = {
|
181
|
+
**dict(lambda_func.func_decl.operand_declarations),
|
182
|
+
**self.current_scope.operands,
|
183
|
+
}
|
184
|
+
renamed_operands: Dict[str, QuantumOperandDeclaration] = {}
|
185
|
+
for operand_name, operand_decl in original_operands.items():
|
186
|
+
renamed_name = lambda_func.rename_params.get(operand_name, operand_name)
|
187
|
+
renamed_operands[renamed_name] = operand_decl.copy(
|
188
|
+
update={"name": renamed_name}
|
189
|
+
)
|
190
|
+
scope = StaticScope(
|
191
|
+
parameters=renamed_parameters,
|
192
|
+
operands=renamed_operands,
|
193
|
+
variables_to_states={
|
194
|
+
**variables_to_states,
|
195
|
+
**initialize_variables_to_state(ports),
|
196
|
+
},
|
197
|
+
)
|
198
|
+
with self.scoped_visit(scope):
|
199
|
+
self.generic_visit(lambda_func)
|
200
|
+
|
201
|
+
def _handle_inputs(self, inputs: Mapping[str, HandleBinding]) -> None:
|
202
|
+
for handle_binding in inputs.values():
|
203
|
+
handle_wiring_state = self.current_scope.variable_states[
|
204
|
+
handle_binding.name
|
205
|
+
]
|
206
|
+
if handle_wiring_state is not HandleState.INITIALIZED:
|
207
|
+
self._error_manager.add_error(
|
208
|
+
f"Trying to access handle {handle_binding.name!r} as input but it is in incorrect state"
|
209
|
+
)
|
210
|
+
continue
|
211
|
+
|
212
|
+
self.current_scope.variable_states[handle_binding.name] = (
|
213
|
+
HandleState.UNINITIALIZED
|
214
|
+
)
|
215
|
+
|
216
|
+
def _handle_outputs(self, outputs: Mapping[str, HandleBinding]) -> None:
|
217
|
+
for handle_binding in outputs.values():
|
218
|
+
handle_wiring_state = self.current_scope.variable_states[
|
219
|
+
handle_binding.name
|
220
|
+
]
|
221
|
+
|
222
|
+
if handle_wiring_state is not HandleState.UNINITIALIZED:
|
223
|
+
self._error_manager.add_error(
|
224
|
+
f"Trying to access handle {handle_binding.name!r} as output but it is in incorrect state"
|
225
|
+
)
|
226
|
+
continue
|
227
|
+
|
228
|
+
self.current_scope.variable_states[handle_binding.name] = (
|
229
|
+
HandleState.INITIALIZED
|
230
|
+
)
|
231
|
+
|
232
|
+
def _handle_inouts(
|
233
|
+
self,
|
234
|
+
inouts: Mapping[
|
235
|
+
str, Union[SlicedHandleBinding, SubscriptHandleBinding, HandleBinding]
|
236
|
+
],
|
237
|
+
) -> None:
|
238
|
+
sliced_handles = set()
|
239
|
+
whole_handles = set()
|
240
|
+
|
241
|
+
for handle_binding in inouts.values():
|
242
|
+
handle_wiring_state = self.current_scope.variable_states[
|
243
|
+
handle_binding.name
|
244
|
+
]
|
245
|
+
|
246
|
+
if handle_wiring_state is not HandleState.INITIALIZED:
|
247
|
+
self._error_manager.add_error(
|
248
|
+
f"Trying to access handle {handle_binding.name!r} as inout but it is in incorrect state"
|
249
|
+
)
|
250
|
+
|
251
|
+
if isinstance(
|
252
|
+
handle_binding, (SlicedHandleBinding, SubscriptHandleBinding)
|
253
|
+
):
|
254
|
+
sliced_handles.add(handle_binding.name)
|
255
|
+
else:
|
256
|
+
whole_handles.add(handle_binding.name)
|
257
|
+
|
258
|
+
for handle in sliced_handles & whole_handles:
|
259
|
+
self._error_manager.add_error(
|
260
|
+
f"Invalid use of inout handle {handle!r}, used both in slice or subscript and whole"
|
261
|
+
)
|
262
|
+
|
263
|
+
|
264
|
+
def resolve_function_calls(
|
265
|
+
root: Any,
|
266
|
+
quantum_function_dict: Mapping[str, QuantumFunctionDeclaration],
|
267
|
+
) -> None:
|
268
|
+
StaticSemanticsVisitor(
|
269
|
+
{
|
270
|
+
**QuantumFunctionDeclaration.BUILTIN_FUNCTION_DECLARATIONS,
|
271
|
+
**quantum_function_dict,
|
272
|
+
},
|
273
|
+
[],
|
274
|
+
).visit(root)
|
275
|
+
|
276
|
+
|
277
|
+
def static_semantics_analysis_pass(
|
278
|
+
model: Model, error_type: Type[Exception] = ClassiqSemanticError
|
279
|
+
) -> None:
|
280
|
+
StaticSemanticsVisitor(
|
281
|
+
{
|
282
|
+
**QuantumFunctionDeclaration.BUILTIN_FUNCTION_DECLARATIONS,
|
283
|
+
**model.function_dict,
|
284
|
+
},
|
285
|
+
[const.name for const in model.constants],
|
286
|
+
).visit(model)
|
287
|
+
ErrorManager().report_errors(error_type)
|
288
|
+
|
289
|
+
|
290
|
+
EXPECTED_TERMINAL_STATES: Dict[PortDeclarationDirection, HandleState] = {
|
291
|
+
PortDeclarationDirection.Output: HandleState.INITIALIZED,
|
292
|
+
PortDeclarationDirection.Inout: HandleState.INITIALIZED,
|
293
|
+
}
|
294
|
+
|
295
|
+
|
296
|
+
def initialize_variables_to_state(
|
297
|
+
port_declarations: List[PortDeclaration],
|
298
|
+
) -> Dict[str, HandleState]:
|
299
|
+
variables_to_state: Dict[str, HandleState] = dict()
|
300
|
+
|
301
|
+
for port_decl in port_declarations:
|
302
|
+
variables_to_state[port_decl.name] = (
|
303
|
+
HandleState.INITIALIZED
|
304
|
+
if port_decl.direction.includes_port_direction(PortDirection.Input)
|
305
|
+
else HandleState.UNINITIALIZED
|
306
|
+
)
|
307
|
+
|
308
|
+
return variables_to_state
|
@@ -0,0 +1,149 @@
|
|
1
|
+
import re
|
2
|
+
from typing import Mapping, Set
|
3
|
+
|
4
|
+
from classiq.interface.generator.functions.port_declaration import (
|
5
|
+
PortDeclarationDirection,
|
6
|
+
)
|
7
|
+
from classiq.interface.model.quantum_function_call import QuantumFunctionCall
|
8
|
+
from classiq.interface.model.quantum_function_declaration import (
|
9
|
+
QuantumFunctionDeclaration,
|
10
|
+
QuantumOperandDeclaration,
|
11
|
+
)
|
12
|
+
from classiq.interface.model.quantum_lambda_function import (
|
13
|
+
QuantumLambdaFunction,
|
14
|
+
QuantumOperand,
|
15
|
+
)
|
16
|
+
|
17
|
+
from classiq.exceptions import ClassiqError
|
18
|
+
from classiq.qmod.semantics.error_manager import ErrorManager
|
19
|
+
|
20
|
+
|
21
|
+
def validate_call_arguments(
|
22
|
+
fc: QuantumFunctionCall,
|
23
|
+
function_dict: Mapping[str, QuantumFunctionDeclaration],
|
24
|
+
) -> None:
|
25
|
+
_check_params_against_declaration(
|
26
|
+
set(fc.params.keys()),
|
27
|
+
set(fc.func_decl.param_decls.keys()),
|
28
|
+
fc.func_decl.name,
|
29
|
+
)
|
30
|
+
_check_ports_against_declaration(fc, fc.func_decl)
|
31
|
+
_check_params_against_declaration(
|
32
|
+
set(fc.operands.keys()),
|
33
|
+
set(fc.func_decl.operand_declarations.keys()),
|
34
|
+
fc.func_name,
|
35
|
+
)
|
36
|
+
_check_operands_against_declaration(fc, fc.func_decl, function_dict)
|
37
|
+
|
38
|
+
|
39
|
+
def _check_ports_against_declaration(
|
40
|
+
call: QuantumFunctionCall, decl: QuantumFunctionDeclaration
|
41
|
+
) -> None:
|
42
|
+
call_input_names = set(call.inputs.keys())
|
43
|
+
|
44
|
+
_check_params_against_declaration(
|
45
|
+
call_input_names,
|
46
|
+
decl.ports_by_declaration_direction(PortDeclarationDirection.Input),
|
47
|
+
call.func_name,
|
48
|
+
)
|
49
|
+
|
50
|
+
call_output_names = set(call.outputs.keys())
|
51
|
+
|
52
|
+
_check_params_against_declaration(
|
53
|
+
call_output_names,
|
54
|
+
decl.ports_by_declaration_direction(PortDeclarationDirection.Output),
|
55
|
+
call.func_name,
|
56
|
+
)
|
57
|
+
|
58
|
+
inout_params = set(call.inouts.keys())
|
59
|
+
|
60
|
+
_check_params_against_declaration(
|
61
|
+
inout_params,
|
62
|
+
decl.ports_by_declaration_direction(PortDeclarationDirection.Inout),
|
63
|
+
call.func_name,
|
64
|
+
)
|
65
|
+
|
66
|
+
|
67
|
+
def _check_operand_against_declaration(
|
68
|
+
call: QuantumFunctionCall,
|
69
|
+
operand_decl: QuantumOperandDeclaration,
|
70
|
+
operand_argument: QuantumOperand,
|
71
|
+
function_dict: Mapping[str, QuantumFunctionDeclaration],
|
72
|
+
in_list: bool = False,
|
73
|
+
) -> None:
|
74
|
+
if isinstance(operand_argument, list):
|
75
|
+
if in_list:
|
76
|
+
ErrorManager().add_error(
|
77
|
+
f"{str(operand_argument)!r} argument to {call.func_decl.name!r} is not "
|
78
|
+
f"a valid operand. Nested operand lists are not permitted"
|
79
|
+
)
|
80
|
+
return
|
81
|
+
for arg in operand_argument:
|
82
|
+
_check_operand_against_declaration(
|
83
|
+
call, operand_decl, arg, function_dict, in_list=True
|
84
|
+
)
|
85
|
+
return
|
86
|
+
operand_arg_decl: QuantumFunctionDeclaration
|
87
|
+
if isinstance(operand_argument, str):
|
88
|
+
if operand_argument not in function_dict:
|
89
|
+
ErrorManager().add_error(
|
90
|
+
f"{operand_argument!r} argument to {call.func_decl.name!r} is not a "
|
91
|
+
f"registered function"
|
92
|
+
)
|
93
|
+
return
|
94
|
+
operand_arg_decl = function_dict[operand_argument]
|
95
|
+
elif isinstance(operand_argument, QuantumLambdaFunction):
|
96
|
+
if operand_argument.func_decl is None:
|
97
|
+
return
|
98
|
+
operand_arg_decl = operand_argument.func_decl
|
99
|
+
else:
|
100
|
+
raise ClassiqError(
|
101
|
+
f"{str(operand_argument)!r} argument to {call.func_decl.name!r} is not a "
|
102
|
+
f"valid operand"
|
103
|
+
)
|
104
|
+
num_arg_parameters = len(operand_arg_decl.get_positional_arg_decls())
|
105
|
+
num_decl_parameters = len(operand_decl.get_positional_arg_decls())
|
106
|
+
if num_arg_parameters != num_decl_parameters:
|
107
|
+
ErrorManager().add_error(
|
108
|
+
f"Signature of argument {operand_argument!r} to {call.func_decl.name!r} "
|
109
|
+
f"does not match the signature of parameter {operand_decl.name!r}. "
|
110
|
+
f"{operand_decl.name!r} accepts {num_decl_parameters} parameters but "
|
111
|
+
f"{operand_argument!r} accepts {num_arg_parameters} parameters"
|
112
|
+
)
|
113
|
+
|
114
|
+
|
115
|
+
def _check_operands_against_declaration(
|
116
|
+
call: QuantumFunctionCall,
|
117
|
+
decl: QuantumFunctionDeclaration,
|
118
|
+
function_dict: Mapping[str, QuantumFunctionDeclaration],
|
119
|
+
) -> None:
|
120
|
+
for operand_parameter, operand_argument in call.operands.items():
|
121
|
+
_check_operand_against_declaration(
|
122
|
+
call,
|
123
|
+
decl.operand_declarations[operand_parameter],
|
124
|
+
operand_argument,
|
125
|
+
function_dict,
|
126
|
+
)
|
127
|
+
|
128
|
+
|
129
|
+
def _check_params_against_declaration(
|
130
|
+
call_params: Set[str],
|
131
|
+
param_decls: Set[str],
|
132
|
+
callee_name: str,
|
133
|
+
) -> None:
|
134
|
+
unknown_params = call_params - param_decls
|
135
|
+
if any(re.match(r"arg\d+", param) for param in unknown_params):
|
136
|
+
error_msg = (
|
137
|
+
f"Unsupported passing of named function {callee_name!r} as an operand."
|
138
|
+
"\nSuggestion: replace the named function with lambda function."
|
139
|
+
)
|
140
|
+
else:
|
141
|
+
error_msg = f"Unknown parameters {unknown_params} in call to {callee_name!r}."
|
142
|
+
if unknown_params:
|
143
|
+
ErrorManager().add_error(error_msg)
|
144
|
+
|
145
|
+
missing_params = param_decls - call_params
|
146
|
+
if missing_params:
|
147
|
+
ErrorManager().add_error(
|
148
|
+
f"Missing parameters {missing_params} in call to {callee_name!r}."
|
149
|
+
)
|
@@ -0,0 +1,21 @@
|
|
1
|
+
from typing import Sequence, Union
|
2
|
+
|
3
|
+
from classiq import EnumDeclaration, StructDeclaration
|
4
|
+
from classiq.qmod.semantics.error_manager import ErrorManager
|
5
|
+
|
6
|
+
|
7
|
+
def check_duplicate_types(
|
8
|
+
types: Sequence[Union[EnumDeclaration, StructDeclaration]]
|
9
|
+
) -> None:
|
10
|
+
known_types = {
|
11
|
+
type_.name for type_ in EnumDeclaration.BUILTIN_ENUM_DECLARATIONS.values()
|
12
|
+
}
|
13
|
+
known_types |= {
|
14
|
+
type_.name for type_ in StructDeclaration.BUILTIN_STRUCT_DECLARATIONS.values()
|
15
|
+
}
|
16
|
+
for type_ in types:
|
17
|
+
if type_.name in known_types:
|
18
|
+
with ErrorManager().node_context(type_):
|
19
|
+
ErrorManager().add_error(f"Type {type_.name!r} already exists")
|
20
|
+
else:
|
21
|
+
known_types.add(type_.name)
|
classiq/qmod/symbolic.py
CHANGED
@@ -25,12 +25,12 @@ from classiq.qmod.qmod_variable import QNum
|
|
25
25
|
from classiq.qmod.symbolic_expr import SymbolicExpr
|
26
26
|
from classiq.qmod.symbolic_type import SymbolicTypes
|
27
27
|
|
28
|
-
pi = SymbolicExpr("pi")
|
29
|
-
E = SymbolicExpr("E")
|
30
|
-
I = SymbolicExpr("I") # noqa: E741
|
31
|
-
GoldenRatio = SymbolicExpr("GoldenRatio")
|
32
|
-
EulerGamma = SymbolicExpr("EulerGamma")
|
33
|
-
Catalan = SymbolicExpr("Catalan")
|
28
|
+
pi = SymbolicExpr("pi", False)
|
29
|
+
E = SymbolicExpr("E", False)
|
30
|
+
I = SymbolicExpr("I", False) # noqa: E741
|
31
|
+
GoldenRatio = SymbolicExpr("GoldenRatio", False)
|
32
|
+
EulerGamma = SymbolicExpr("EulerGamma", False)
|
33
|
+
Catalan = SymbolicExpr("Catalan", False)
|
34
34
|
|
35
35
|
T = TypeVar("T", bound=CParam)
|
36
36
|
|