classiq 0.89.0__py3-none-any.whl → 0.91.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.
Potentially problematic release.
This version of classiq might be problematic. Click here for more details.
- classiq/__init__.py +1 -0
- classiq/_internals/api_wrapper.py +16 -32
- classiq/analyzer/show_interactive_hack.py +26 -1
- classiq/applications/chemistry/chemistry_model_constructor.py +14 -2
- classiq/applications/combinatorial_helpers/pyomo_utils.py +9 -6
- classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +2 -2
- classiq/applications/combinatorial_optimization/combinatorial_problem.py +16 -8
- classiq/evaluators/classical_expression.py +63 -41
- classiq/evaluators/control.py +31 -52
- classiq/evaluators/expression_evaluator.py +31 -127
- classiq/evaluators/parameter_types.py +200 -104
- classiq/evaluators/qmod_annotated_expression.py +3 -1
- classiq/evaluators/qmod_expression_visitors/qmod_expression_simplifier.py +10 -3
- classiq/evaluators/qmod_node_evaluators/classical_function_evaluation.py +12 -37
- classiq/evaluators/qmod_node_evaluators/constant_evaluation.py +8 -17
- classiq/evaluators/qmod_node_evaluators/measurement_evaluation.py +1 -1
- classiq/evaluators/qmod_node_evaluators/min_max_evaluation.py +7 -1
- classiq/evaluators/qmod_node_evaluators/name_evaluation.py +0 -1
- classiq/evaluators/qmod_node_evaluators/numeric_attrs_utils.py +9 -1
- classiq/evaluators/qmod_node_evaluators/utils.py +33 -8
- classiq/evaluators/qmod_type_inference/classical_type_inference.py +4 -7
- classiq/interface/_version.py +1 -1
- classiq/interface/analyzer/analysis_params.py +1 -25
- classiq/interface/analyzer/result.py +4 -0
- classiq/interface/chemistry/ground_state_problem.py +16 -2
- classiq/interface/executor/optimizer_preferences.py +0 -112
- classiq/interface/executor/result.py +22 -3
- classiq/interface/generator/application_apis/chemistry_declarations.py +3 -1
- classiq/interface/generator/arith/arithmetic_expression_validator.py +2 -7
- classiq/interface/generator/expressions/evaluated_expression.py +3 -13
- classiq/interface/generator/expressions/expression_types.py +10 -22
- classiq/interface/generator/expressions/proxies/classical/classical_array_proxy.py +3 -8
- classiq/interface/generator/expressions/proxies/classical/classical_proxy.py +2 -2
- classiq/interface/generator/expressions/proxies/classical/classical_struct_proxy.py +1 -2
- classiq/interface/generator/functions/classical_type.py +2 -5
- classiq/interface/generator/functions/concrete_types.py +1 -1
- classiq/interface/generator/functions/type_name.py +0 -12
- classiq/interface/generator/generated_circuit_data.py +4 -0
- classiq/interface/generator/preferences/qasm_to_qmod_params.py +14 -0
- classiq/interface/helpers/model_normalizer.py +0 -6
- classiq/interface/ide/visual_model.py +1 -0
- classiq/interface/model/handle_binding.py +1 -1
- classiq/interface/model/port_declaration.py +2 -1
- classiq/interface/model/quantum_expressions/arithmetic_operation.py +16 -12
- classiq/interface/model/quantum_type.py +1 -40
- classiq/interface/server/routes.py +2 -3
- classiq/model_expansions/capturing/captured_vars.py +10 -3
- classiq/model_expansions/closure.py +8 -0
- classiq/model_expansions/function_builder.py +18 -2
- classiq/model_expansions/interpreters/base_interpreter.py +84 -22
- classiq/model_expansions/interpreters/frontend_generative_interpreter.py +1 -11
- classiq/model_expansions/interpreters/generative_interpreter.py +67 -20
- classiq/model_expansions/quantum_operations/allocate.py +92 -21
- classiq/model_expansions/quantum_operations/assignment_result_processor.py +28 -27
- classiq/model_expansions/quantum_operations/call_emitter.py +32 -26
- classiq/model_expansions/quantum_operations/classical_var_emitter.py +6 -2
- classiq/model_expansions/quantum_operations/emitter.py +50 -70
- classiq/model_expansions/quantum_operations/expression_evaluator.py +62 -7
- classiq/model_expansions/quantum_operations/quantum_function_call.py +4 -5
- classiq/model_expansions/quantum_operations/variable_decleration.py +16 -11
- classiq/model_expansions/scope.py +39 -41
- classiq/model_expansions/scope_initialization.py +3 -6
- classiq/model_expansions/transformers/model_renamer.py +35 -64
- classiq/model_expansions/transformers/type_modifier_inference.py +6 -6
- classiq/model_expansions/utils/handles_collector.py +7 -0
- classiq/model_expansions/visitors/boolean_expression_transformers.py +7 -31
- classiq/model_expansions/visitors/symbolic_param_inference.py +9 -3
- classiq/open_library/functions/state_preparation.py +3 -3
- classiq/qmod/builtins/functions/allocation.py +8 -8
- classiq/qmod/builtins/functions/arithmetic.py +1 -1
- classiq/qmod/builtins/functions/chemistry.py +64 -0
- classiq/qmod/builtins/functions/exponentiation.py +7 -13
- classiq/qmod/builtins/functions/qsvm.py +1 -1
- classiq/qmod/builtins/operations.py +42 -10
- classiq/qmod/generative.py +2 -4
- classiq/qmod/native/pretty_printer.py +1 -1
- classiq/qmod/pretty_print/pretty_printer.py +1 -1
- classiq/qmod/qmod_constant.py +1 -1
- classiq/qmod/qmod_parameter.py +2 -2
- classiq/qmod/qmod_variable.py +18 -19
- classiq/qmod/quantum_expandable.py +1 -1
- classiq/qmod/semantics/error_manager.py +34 -15
- classiq/qmod/symbolic.py +15 -4
- classiq/qmod/utilities.py +4 -1
- classiq/synthesis.py +38 -3
- classiq/visualization.py +1 -1
- {classiq-0.89.0.dist-info → classiq-0.91.0.dist-info}/METADATA +1 -1
- {classiq-0.89.0.dist-info → classiq-0.91.0.dist-info}/RECORD +89 -107
- classiq/evaluators/arg_type_match.py +0 -168
- classiq/evaluators/classical_type_inference.py +0 -121
- classiq/interface/combinatorial_optimization/optimization_problem.py +0 -17
- classiq/interface/combinatorial_optimization/result.py +0 -9
- classiq/interface/generator/expressions/handle_identifier.py +0 -6
- classiq/interface/generator/expressions/proxies/classical/any_classical_value.py +0 -41
- classiq/interface/generator/expressions/proxies/quantum/__init__.py +0 -0
- classiq/interface/generator/expressions/proxies/quantum/qmod_qarray_proxy.py +0 -80
- classiq/interface/generator/expressions/proxies/quantum/qmod_qscalar_proxy.py +0 -77
- classiq/interface/generator/expressions/proxies/quantum/qmod_qstruct_proxy.py +0 -38
- classiq/interface/generator/expressions/proxies/quantum/qmod_sized_proxy.py +0 -39
- classiq/interface/generator/expressions/type_proxy.py +0 -10
- classiq/model_expansions/atomic_expression_functions_defs.py +0 -413
- classiq/model_expansions/model_tables.py +0 -18
- classiq/model_expansions/sympy_conversion/__init__.py +0 -0
- classiq/model_expansions/sympy_conversion/expression_to_sympy.py +0 -181
- classiq/model_expansions/sympy_conversion/sympy_to_python.py +0 -132
- classiq/model_expansions/transformers/ast_renamer.py +0 -26
- classiq/model_expansions/utils/sympy_utils.py +0 -24
- {classiq-0.89.0.dist-info → classiq-0.91.0.dist-info}/WHEEL +0 -0
|
@@ -1,144 +1,48 @@
|
|
|
1
|
-
import
|
|
2
|
-
from collections.abc import Mapping
|
|
3
|
-
from enum import EnumMeta
|
|
1
|
+
from enum import Enum
|
|
4
2
|
from typing import Any
|
|
5
3
|
|
|
6
|
-
|
|
4
|
+
import sympy
|
|
7
5
|
|
|
8
|
-
from classiq.interface.exceptions import
|
|
6
|
+
from classiq.interface.exceptions import ClassiqInternalExpansionError
|
|
9
7
|
from classiq.interface.generator.constant import Constant
|
|
10
|
-
from classiq.interface.generator.expressions.
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
from classiq.interface.generator.expressions.expression import Expression
|
|
14
|
-
from classiq.interface.generator.expressions.expression_types import ExpressionValue
|
|
15
|
-
from classiq.interface.generator.expressions.non_symbolic_expr import NonSymbolicExpr
|
|
16
|
-
from classiq.interface.generator.expressions.proxies.quantum.qmod_sized_proxy import (
|
|
17
|
-
QmodSizedProxy,
|
|
18
|
-
)
|
|
19
|
-
from classiq.interface.generator.expressions.sympy_supported_expressions import (
|
|
20
|
-
SYMPY_SUPPORTED_EXPRESSIONS,
|
|
8
|
+
from classiq.interface.generator.expressions.expression_types import RuntimeConstant
|
|
9
|
+
from classiq.interface.generator.expressions.proxies.classical.qmod_struct_instance import (
|
|
10
|
+
QmodStructInstance,
|
|
21
11
|
)
|
|
22
12
|
|
|
23
|
-
from classiq.
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
13
|
+
from classiq.evaluators.classical_expression import evaluate_classical_expression
|
|
14
|
+
from classiq.model_expansions.scope import Evaluated, Scope
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def qmod_val_to_python(val: RuntimeConstant) -> Any:
|
|
18
|
+
if isinstance(val, (int, float, bool, complex, Enum)):
|
|
19
|
+
return val
|
|
20
|
+
if isinstance(val, list):
|
|
21
|
+
return [qmod_val_to_python(item) for item in val]
|
|
22
|
+
if isinstance(val, QmodStructInstance):
|
|
23
|
+
return {
|
|
24
|
+
field_name: qmod_val_to_python(field_val)
|
|
25
|
+
for field_name, field_val in val.fields.items()
|
|
26
|
+
}
|
|
27
|
+
if isinstance(val, sympy.Expr):
|
|
28
|
+
return val.evalf()
|
|
29
|
+
raise ClassiqInternalExpansionError(
|
|
30
|
+
f"Could not convert Qmod value {str(val)!r} of type {type(val).__name__} to Python"
|
|
31
|
+
)
|
|
34
32
|
|
|
35
33
|
|
|
36
|
-
def evaluate_constants(constants: list[Constant]) ->
|
|
37
|
-
result
|
|
34
|
+
def evaluate_constants(constants: list[Constant]) -> Scope:
|
|
35
|
+
result = Scope()
|
|
38
36
|
for constant in constants:
|
|
39
|
-
|
|
37
|
+
expr_val = evaluate_classical_expression(constant.value, result).value
|
|
38
|
+
result[constant.name] = Evaluated(value=expr_val)
|
|
39
|
+
|
|
40
40
|
return result
|
|
41
41
|
|
|
42
42
|
|
|
43
43
|
def evaluate_constants_as_python(constants: list[Constant]) -> dict[str, Any]:
|
|
44
44
|
evaluated = evaluate_constants(constants)
|
|
45
45
|
return {
|
|
46
|
-
constant.name: qmod_val_to_python(
|
|
47
|
-
evaluated[constant.name].value, constant.const_type
|
|
48
|
-
)
|
|
46
|
+
constant.name: qmod_val_to_python(evaluated[constant.name].value)
|
|
49
47
|
for constant in constants
|
|
50
48
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
def _quick_eval(expr: str) -> Any:
|
|
54
|
-
try:
|
|
55
|
-
return int(expr)
|
|
56
|
-
except ValueError:
|
|
57
|
-
pass
|
|
58
|
-
try:
|
|
59
|
-
return float(expr)
|
|
60
|
-
except ValueError:
|
|
61
|
-
pass
|
|
62
|
-
return None
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
def evaluate(
|
|
66
|
-
expr: Expression, locals_dict: Mapping[str, EvaluatedExpression]
|
|
67
|
-
) -> EvaluatedExpression:
|
|
68
|
-
val = _quick_eval(expr.expr)
|
|
69
|
-
if val is not None:
|
|
70
|
-
return EvaluatedExpression(value=val)
|
|
71
|
-
|
|
72
|
-
model_locals: dict[str, ExpressionValue] = {}
|
|
73
|
-
model_locals.update(ATOMIC_EXPRESSION_FUNCTIONS)
|
|
74
|
-
model_locals.update(
|
|
75
|
-
{
|
|
76
|
-
enum_decl.name: enum_decl.create_enum()
|
|
77
|
-
for enum_decl in (QMODULE.enum_decls | BUILTIN_ENUM_DECLARATIONS).values()
|
|
78
|
-
}
|
|
79
|
-
)
|
|
80
|
-
# locals override builtin-functions
|
|
81
|
-
model_locals.update({name: expr.value for name, expr in locals_dict.items()})
|
|
82
|
-
|
|
83
|
-
_validate_undefined_vars(expr.expr, model_locals)
|
|
84
|
-
|
|
85
|
-
sympy_expr = translate_to_sympy(expr.expr)
|
|
86
|
-
try:
|
|
87
|
-
sympify_result = sympify(sympy_expr, locals=model_locals)
|
|
88
|
-
except (TypeError, IndexError) as e:
|
|
89
|
-
raise ClassiqExpansionError(str(e)) from e
|
|
90
|
-
except AttributeError as e:
|
|
91
|
-
if isinstance(e.obj, EnumMeta):
|
|
92
|
-
raise ClassiqExpansionError(
|
|
93
|
-
f"Enum {e.obj.__name__} has no member {e.name!r}. Available members: "
|
|
94
|
-
f"{', '.join(e.obj.__members__)}"
|
|
95
|
-
) from e
|
|
96
|
-
raise
|
|
97
|
-
except SympifyError as e:
|
|
98
|
-
expr = e.expr
|
|
99
|
-
if isinstance(expr, QmodSizedProxy) and isinstance(expr, NonSymbolicExpr):
|
|
100
|
-
raise ClassiqExpansionError(
|
|
101
|
-
f"{expr.type_name} {str(expr)!r} does not support arithmetic operations"
|
|
102
|
-
) from e
|
|
103
|
-
raise
|
|
104
|
-
|
|
105
|
-
return EvaluatedExpression(
|
|
106
|
-
value=sympy_to_python(sympify_result, locals=model_locals)
|
|
107
|
-
)
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
def _validate_undefined_vars(
|
|
111
|
-
expr: str, model_locals: dict[str, ExpressionValue]
|
|
112
|
-
) -> None:
|
|
113
|
-
id_visitor = _VarsCollector()
|
|
114
|
-
id_visitor.visit(ast.parse(expr))
|
|
115
|
-
identifiers = id_visitor.vars
|
|
116
|
-
undefined_vars = (
|
|
117
|
-
identifiers
|
|
118
|
-
- model_locals.keys()
|
|
119
|
-
- set(SYMPY_SUPPORTED_EXPRESSIONS)
|
|
120
|
-
- set(symbolic.__all__)
|
|
121
|
-
)
|
|
122
|
-
|
|
123
|
-
if len(undefined_vars) == 1:
|
|
124
|
-
undefined_var = undefined_vars.__iter__().__next__()
|
|
125
|
-
raise ClassiqExpansionError(f"Variable {undefined_var!r} is undefined")
|
|
126
|
-
elif len(undefined_vars) > 1:
|
|
127
|
-
raise ClassiqExpansionError(f"Variables {list(undefined_vars)} are undefined")
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
class _VarsCollector(ast.NodeTransformer):
|
|
131
|
-
def __init__(self) -> None:
|
|
132
|
-
self.vars: set[str] = set()
|
|
133
|
-
|
|
134
|
-
def visit_Name(self, node: ast.Name) -> None:
|
|
135
|
-
self.vars.add(node.id)
|
|
136
|
-
|
|
137
|
-
def visit_Call(self, node: ast.Call) -> None:
|
|
138
|
-
func = node.func
|
|
139
|
-
self.visit(func)
|
|
140
|
-
if not isinstance(func, ast.Name) or func.id != "struct_literal":
|
|
141
|
-
for arg in node.args:
|
|
142
|
-
self.visit(arg)
|
|
143
|
-
for kw in node.keywords:
|
|
144
|
-
self.visit(kw)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import
|
|
1
|
+
from typing import TYPE_CHECKING, Any, NoReturn, TypeVar, Union
|
|
2
2
|
|
|
3
3
|
import sympy
|
|
4
4
|
|
|
@@ -6,14 +6,17 @@ from classiq.interface.exceptions import (
|
|
|
6
6
|
ClassiqExpansionError,
|
|
7
7
|
ClassiqInternalExpansionError,
|
|
8
8
|
)
|
|
9
|
-
from classiq.interface.generator.expressions.
|
|
10
|
-
|
|
11
|
-
AnyClassicalValue,
|
|
9
|
+
from classiq.interface.generator.expressions.evaluated_expression import (
|
|
10
|
+
EvaluatedExpression,
|
|
12
11
|
)
|
|
12
|
+
from classiq.interface.generator.expressions.expression import Expression
|
|
13
|
+
from classiq.interface.generator.expressions.expression_types import RuntimeConstant
|
|
13
14
|
from classiq.interface.generator.functions.classical_type import (
|
|
15
|
+
Bool,
|
|
14
16
|
ClassicalArray,
|
|
15
17
|
ClassicalTuple,
|
|
16
18
|
ClassicalType,
|
|
19
|
+
Integer,
|
|
17
20
|
)
|
|
18
21
|
from classiq.interface.generator.functions.concrete_types import ConcreteQuantumType
|
|
19
22
|
from classiq.interface.generator.functions.port_declaration import (
|
|
@@ -22,12 +25,14 @@ from classiq.interface.generator.functions.port_declaration import (
|
|
|
22
25
|
from classiq.interface.generator.functions.type_name import (
|
|
23
26
|
TypeName,
|
|
24
27
|
)
|
|
28
|
+
from classiq.interface.helpers.backward_compatibility import zip_strict
|
|
25
29
|
from classiq.interface.model.classical_parameter_declaration import (
|
|
26
30
|
ClassicalParameterDeclaration,
|
|
27
31
|
)
|
|
28
32
|
from classiq.interface.model.handle_binding import HandleBinding
|
|
29
33
|
from classiq.interface.model.port_declaration import PortDeclaration
|
|
30
34
|
from classiq.interface.model.quantum_function_declaration import (
|
|
35
|
+
AnonQuantumOperandDeclaration,
|
|
31
36
|
PositionalArg,
|
|
32
37
|
QuantumOperandDeclaration,
|
|
33
38
|
)
|
|
@@ -38,71 +43,62 @@ from classiq.interface.model.quantum_type import (
|
|
|
38
43
|
QuantumType,
|
|
39
44
|
)
|
|
40
45
|
|
|
41
|
-
from classiq.evaluators.arg_type_match import check_type_match
|
|
42
46
|
from classiq.evaluators.classical_expression import (
|
|
43
47
|
evaluate_classical_expression,
|
|
44
48
|
)
|
|
45
|
-
from classiq.evaluators.
|
|
49
|
+
from classiq.evaluators.qmod_annotated_expression import QmodAnnotatedExpression
|
|
50
|
+
from classiq.evaluators.qmod_node_evaluators.utils import QmodType, get_sympy_val
|
|
51
|
+
from classiq.evaluators.qmod_type_inference.classical_type_inference import (
|
|
46
52
|
infer_classical_type,
|
|
53
|
+
inject_classical_type_attributes,
|
|
47
54
|
)
|
|
48
55
|
from classiq.evaluators.qmod_type_inference.quantum_type_inference import (
|
|
49
56
|
inject_quantum_type_attributes,
|
|
57
|
+
validate_quantum_type_attributes,
|
|
50
58
|
)
|
|
59
|
+
from classiq.evaluators.type_type_match import check_signature_match
|
|
51
60
|
from classiq.model_expansions.closure import FunctionClosure
|
|
52
61
|
from classiq.model_expansions.scope import (
|
|
62
|
+
ClassicalSymbol,
|
|
53
63
|
Evaluated,
|
|
54
64
|
QuantumSymbol,
|
|
55
65
|
QuantumVariable,
|
|
56
66
|
Scope,
|
|
57
67
|
)
|
|
68
|
+
from classiq.model_expansions.visitors.symbolic_param_inference import (
|
|
69
|
+
set_generative_recursively,
|
|
70
|
+
)
|
|
58
71
|
|
|
59
72
|
|
|
60
73
|
def evaluate_parameter_types_from_args(
|
|
61
|
-
closure: FunctionClosure,
|
|
74
|
+
closure: FunctionClosure, arguments: list[Evaluated]
|
|
62
75
|
) -> list[PositionalArg]:
|
|
63
76
|
parameters = closure.positional_arg_declarations
|
|
64
|
-
|
|
65
|
-
|
|
77
|
+
if len(parameters) != len(arguments):
|
|
78
|
+
raise ClassiqExpansionError(
|
|
79
|
+
f"Function {closure.name!r} takes {len(parameters)} arguments but "
|
|
80
|
+
f"{len(arguments)} were given"
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
for parameter, argument in zip_strict(parameters, arguments, strict=True):
|
|
84
|
+
if isinstance(parameter, ClassicalParameterDeclaration):
|
|
85
|
+
arg_val = argument.value
|
|
86
|
+
if not isinstance(arg_val, QmodAnnotatedExpression):
|
|
87
|
+
closure.scope[parameter.name] = argument
|
|
66
88
|
|
|
67
|
-
|
|
68
|
-
|
|
89
|
+
evaluated_params = [
|
|
90
|
+
_evaluate_type_from_arg(parameter, argument, closure)
|
|
91
|
+
for parameter, argument in zip_strict(parameters, arguments, strict=True)
|
|
92
|
+
]
|
|
69
93
|
|
|
70
94
|
parameter_names = {parameter.name for parameter in parameters}
|
|
71
|
-
for parameter in parameters:
|
|
95
|
+
for parameter, argument in zip_strict(parameters, arguments, strict=True):
|
|
72
96
|
if isinstance(parameter, QuantumOperandDeclaration):
|
|
73
|
-
parameter_value = closure.scope[parameter.name].value
|
|
74
97
|
_update_operand_signature_environment(
|
|
75
|
-
|
|
98
|
+
argument.value, parameter_names, closure
|
|
76
99
|
)
|
|
77
100
|
|
|
78
|
-
return
|
|
79
|
-
_evaluate_type_from_arg(
|
|
80
|
-
parameter,
|
|
81
|
-
argument,
|
|
82
|
-
Scope(parent=closure.scope | signature_scope),
|
|
83
|
-
closure.name,
|
|
84
|
-
)
|
|
85
|
-
for parameter, argument in zip(parameters, arguments)
|
|
86
|
-
]
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
def _update_scope(
|
|
90
|
-
parameter: PositionalArg, argument: Evaluated, closure: FunctionClosure
|
|
91
|
-
) -> None:
|
|
92
|
-
if not isinstance(parameter, PortDeclaration):
|
|
93
|
-
closure.scope[parameter.name] = argument
|
|
94
|
-
return
|
|
95
|
-
if parameter.direction is PortDeclarationDirection.Output:
|
|
96
|
-
return
|
|
97
|
-
quantum_var = argument.as_type(QuantumVariable)
|
|
98
|
-
casted_argument = _cast(
|
|
99
|
-
parameter.quantum_type,
|
|
100
|
-
quantum_var,
|
|
101
|
-
parameter.name,
|
|
102
|
-
)
|
|
103
|
-
closure.scope[parameter.name] = Evaluated(
|
|
104
|
-
value=casted_argument, defining_function=closure
|
|
105
|
-
)
|
|
101
|
+
return evaluated_params
|
|
106
102
|
|
|
107
103
|
|
|
108
104
|
NestedFunctionClosureT = Union[FunctionClosure, list["NestedFunctionClosureT"]]
|
|
@@ -130,60 +126,162 @@ def _update_operand_signature_environment(
|
|
|
130
126
|
)
|
|
131
127
|
|
|
132
128
|
|
|
133
|
-
def _cast(
|
|
134
|
-
parameter_type: QuantumType, argument: QuantumVariable, param_name: str
|
|
135
|
-
) -> QuantumSymbol:
|
|
136
|
-
updated_type = inject_quantum_type_attributes(argument.quantum_type, parameter_type)
|
|
137
|
-
if updated_type is None:
|
|
138
|
-
raise ClassiqExpansionError(
|
|
139
|
-
f"Argument {str(argument)!r} of type "
|
|
140
|
-
f"{argument.quantum_type.qmod_type_name} is incompatible with parameter "
|
|
141
|
-
f"{param_name!r} of type {parameter_type.qmod_type_name}"
|
|
142
|
-
)
|
|
143
|
-
return QuantumSymbol(
|
|
144
|
-
handle=HandleBinding(name=param_name), quantum_type=updated_type
|
|
145
|
-
)
|
|
146
|
-
|
|
147
|
-
|
|
148
129
|
def _evaluate_type_from_arg(
|
|
149
130
|
parameter: PositionalArg,
|
|
150
131
|
argument: Evaluated,
|
|
151
|
-
|
|
152
|
-
function_name: str,
|
|
132
|
+
closure: FunctionClosure,
|
|
153
133
|
) -> PositionalArg:
|
|
154
134
|
# FIXME: Remove suzuki_trotter overloading (CLS-2912)
|
|
155
|
-
if
|
|
135
|
+
if closure.name == "suzuki_trotter" and parameter.name == "pauli_operator":
|
|
156
136
|
return parameter
|
|
157
137
|
if isinstance(parameter, ClassicalParameterDeclaration):
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
)
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
138
|
+
return _evaluate_classical_type_from_arg(parameter, argument, closure)
|
|
139
|
+
if isinstance(parameter, PortDeclaration):
|
|
140
|
+
return _evaluate_quantum_type_from_arg(parameter, argument, closure)
|
|
141
|
+
if TYPE_CHECKING:
|
|
142
|
+
assert isinstance(parameter, QuantumOperandDeclaration)
|
|
143
|
+
if parameter.is_list:
|
|
144
|
+
return _evaluate_op_list_type_from_arg(parameter, argument, closure)
|
|
145
|
+
else:
|
|
146
|
+
return _evaluate_op_type_from_arg(parameter, argument, closure)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def _evaluate_classical_type_from_arg(
|
|
150
|
+
parameter: ClassicalParameterDeclaration,
|
|
151
|
+
argument: Evaluated,
|
|
152
|
+
closure: FunctionClosure,
|
|
153
|
+
) -> ClassicalParameterDeclaration:
|
|
154
|
+
unified_scope = closure.scope | closure.signature_scope
|
|
155
|
+
updated_classical_type = evaluate_type_in_classical_symbol(
|
|
156
|
+
parameter.classical_type.model_copy(), unified_scope, parameter.name
|
|
157
|
+
)
|
|
158
|
+
arg_val = argument.value
|
|
159
|
+
if isinstance(arg_val, QmodAnnotatedExpression):
|
|
160
|
+
arg_type = arg_val.get_classical_type(arg_val.root)
|
|
161
|
+
else:
|
|
162
|
+
arg_type = infer_classical_type(arg_val)
|
|
163
|
+
injected_classical_type = inject_classical_type_attributes(
|
|
164
|
+
arg_type.without_symbolic_attributes(), updated_classical_type
|
|
165
|
+
)
|
|
166
|
+
if injected_classical_type is None:
|
|
167
|
+
_raise_argument_type_error(
|
|
168
|
+
arg_val, arg_type, parameter.name, updated_classical_type
|
|
164
169
|
)
|
|
170
|
+
if parameter.classical_type.is_purely_generative:
|
|
171
|
+
set_generative_recursively(injected_classical_type)
|
|
172
|
+
closure.scope[parameter.name] = Evaluated(
|
|
173
|
+
value=(
|
|
174
|
+
ClassicalSymbol(
|
|
175
|
+
handle=HandleBinding(name=parameter.name),
|
|
176
|
+
classical_type=injected_classical_type,
|
|
177
|
+
)
|
|
178
|
+
if isinstance(arg_val, QmodAnnotatedExpression)
|
|
179
|
+
else arg_val
|
|
180
|
+
),
|
|
181
|
+
defining_function=closure,
|
|
182
|
+
)
|
|
183
|
+
return ClassicalParameterDeclaration(
|
|
184
|
+
name=parameter.name, classical_type=injected_classical_type
|
|
185
|
+
)
|
|
165
186
|
|
|
166
|
-
if not isinstance(parameter, PortDeclaration):
|
|
167
|
-
return parameter
|
|
168
187
|
|
|
188
|
+
def _evaluate_quantum_type_from_arg(
|
|
189
|
+
parameter: PortDeclaration,
|
|
190
|
+
argument: Evaluated,
|
|
191
|
+
closure: FunctionClosure,
|
|
192
|
+
) -> PortDeclaration:
|
|
193
|
+
unified_scope = closure.scope | closure.signature_scope
|
|
169
194
|
updated_quantum_type: QuantumType = evaluate_type_in_quantum_symbol(
|
|
170
|
-
parameter.quantum_type.model_copy(),
|
|
195
|
+
parameter.quantum_type.model_copy(), unified_scope, parameter.name
|
|
171
196
|
)
|
|
172
197
|
if parameter.direction != PortDeclarationDirection.Output:
|
|
173
198
|
arg_type = argument.as_type(QuantumVariable).quantum_type
|
|
174
199
|
updated_output_quantum_type = inject_quantum_type_attributes(
|
|
175
|
-
arg_type, updated_quantum_type
|
|
200
|
+
arg_type.without_symbolic_attributes(), updated_quantum_type
|
|
176
201
|
)
|
|
177
202
|
if updated_output_quantum_type is None:
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
f"{arg_type.qmod_type_name} is incompatible with parameter "
|
|
181
|
-
f"{parameter.name!r} of type {updated_quantum_type.qmod_type_name}"
|
|
203
|
+
_raise_argument_type_error(
|
|
204
|
+
argument.value, arg_type, parameter.name, updated_quantum_type
|
|
182
205
|
)
|
|
183
206
|
updated_quantum_type = updated_output_quantum_type
|
|
207
|
+
validate_quantum_type_attributes(updated_quantum_type)
|
|
208
|
+
closure.scope[parameter.name] = Evaluated(
|
|
209
|
+
value=QuantumSymbol(
|
|
210
|
+
handle=HandleBinding(name=parameter.name), quantum_type=updated_quantum_type
|
|
211
|
+
),
|
|
212
|
+
defining_function=closure,
|
|
213
|
+
)
|
|
184
214
|
return parameter.model_copy(update={"quantum_type": updated_quantum_type})
|
|
185
215
|
|
|
186
216
|
|
|
217
|
+
def _evaluate_op_list_type_from_arg(
|
|
218
|
+
parameter: QuantumOperandDeclaration, argument: Evaluated, closure: FunctionClosure
|
|
219
|
+
) -> QuantumOperandDeclaration:
|
|
220
|
+
arg_val = argument.value
|
|
221
|
+
if not isinstance(arg_val, list) or any(
|
|
222
|
+
not isinstance(op, FunctionClosure) for op in arg_val
|
|
223
|
+
):
|
|
224
|
+
if isinstance(arg_val, FunctionClosure):
|
|
225
|
+
_raise_argument_type_error(
|
|
226
|
+
"<lambda>",
|
|
227
|
+
arg_val.as_operand_declaration(is_list=False),
|
|
228
|
+
parameter.name,
|
|
229
|
+
parameter,
|
|
230
|
+
)
|
|
231
|
+
raise ClassiqInternalExpansionError("Non-lambda argument to lambda parameter")
|
|
232
|
+
for idx, operand in enumerate(arg_val):
|
|
233
|
+
check_signature_match(
|
|
234
|
+
parameter.positional_arg_declarations,
|
|
235
|
+
operand.positional_arg_declarations,
|
|
236
|
+
f"operand #{idx + 1} in parameter {parameter.name!r} "
|
|
237
|
+
f"in function {closure.name!r}",
|
|
238
|
+
)
|
|
239
|
+
return parameter
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def _evaluate_op_type_from_arg(
|
|
243
|
+
parameter: QuantumOperandDeclaration, argument: Evaluated, closure: FunctionClosure
|
|
244
|
+
) -> QuantumOperandDeclaration:
|
|
245
|
+
arg_val = argument.value
|
|
246
|
+
if not isinstance(arg_val, FunctionClosure):
|
|
247
|
+
if isinstance(arg_val, list):
|
|
248
|
+
if len(arg_val) == 0:
|
|
249
|
+
_raise_argument_type_error(
|
|
250
|
+
arg_val,
|
|
251
|
+
AnonQuantumOperandDeclaration(is_list=True),
|
|
252
|
+
parameter.name,
|
|
253
|
+
parameter,
|
|
254
|
+
)
|
|
255
|
+
first_lambda = arg_val[0]
|
|
256
|
+
if isinstance(first_lambda, FunctionClosure):
|
|
257
|
+
_raise_argument_type_error(
|
|
258
|
+
arg_val,
|
|
259
|
+
first_lambda.as_operand_declaration(is_list=True),
|
|
260
|
+
parameter.name,
|
|
261
|
+
parameter,
|
|
262
|
+
)
|
|
263
|
+
raise ClassiqInternalExpansionError("Non-lambda argument to lambda parameter")
|
|
264
|
+
check_signature_match(
|
|
265
|
+
parameter.positional_arg_declarations,
|
|
266
|
+
arg_val.positional_arg_declarations,
|
|
267
|
+
f"operand {parameter.name!r} in function {closure.name!r}",
|
|
268
|
+
)
|
|
269
|
+
return parameter
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def _raise_argument_type_error(
|
|
273
|
+
arg_val: Any,
|
|
274
|
+
arg_type: Union[QmodType, AnonQuantumOperandDeclaration],
|
|
275
|
+
param_name: str,
|
|
276
|
+
param_type: Union[QmodType, AnonQuantumOperandDeclaration],
|
|
277
|
+
) -> NoReturn:
|
|
278
|
+
raise ClassiqExpansionError(
|
|
279
|
+
f"Argument {str(arg_val)!r} of type "
|
|
280
|
+
f"{arg_type.qmod_type_name} is incompatible with parameter "
|
|
281
|
+
f"{param_name!r} of type {param_type.qmod_type_name}"
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
|
|
187
285
|
def evaluate_type_in_quantum_symbol(
|
|
188
286
|
type_to_update: QuantumType, scope: Scope, param_name: str
|
|
189
287
|
) -> ConcreteQuantumType:
|
|
@@ -206,16 +304,15 @@ def _evaluate_qarray_in_quantum_symbol(
|
|
|
206
304
|
)
|
|
207
305
|
type_to_update.element_type = new_element_type
|
|
208
306
|
if type_to_update.length is not None:
|
|
209
|
-
|
|
307
|
+
type_to_update.length = _eval_expr(
|
|
210
308
|
type_to_update.length,
|
|
211
309
|
scope,
|
|
212
310
|
int,
|
|
311
|
+
Integer,
|
|
213
312
|
type_to_update.type_name,
|
|
214
313
|
"length",
|
|
215
314
|
param_name,
|
|
216
315
|
)
|
|
217
|
-
if new_length is not None:
|
|
218
|
-
type_to_update.length = Expression(expr=str(new_length))
|
|
219
316
|
return type_to_update
|
|
220
317
|
|
|
221
318
|
|
|
@@ -224,73 +321,74 @@ def _evaluate_qnum_in_quantum_symbol(
|
|
|
224
321
|
) -> QuantumNumeric:
|
|
225
322
|
if type_to_update.size is None:
|
|
226
323
|
return type_to_update
|
|
227
|
-
|
|
324
|
+
type_to_update.size = _eval_expr(
|
|
228
325
|
type_to_update.size,
|
|
229
326
|
scope,
|
|
230
327
|
int,
|
|
328
|
+
Integer,
|
|
231
329
|
type_to_update.type_name,
|
|
232
330
|
"size",
|
|
233
331
|
param_name,
|
|
234
332
|
)
|
|
235
|
-
if new_size is not None:
|
|
236
|
-
type_to_update.size = Expression(expr=str(new_size))
|
|
237
333
|
|
|
238
334
|
if type_to_update.is_signed is not None:
|
|
239
|
-
|
|
335
|
+
type_to_update.is_signed = _eval_expr(
|
|
240
336
|
type_to_update.is_signed,
|
|
241
337
|
scope,
|
|
242
338
|
bool,
|
|
339
|
+
Bool,
|
|
243
340
|
type_to_update.type_name,
|
|
244
341
|
"sign",
|
|
245
342
|
param_name,
|
|
246
343
|
)
|
|
247
|
-
if new_is_sign is not None:
|
|
248
|
-
type_to_update.is_signed = Expression(expr=str(new_is_sign))
|
|
249
344
|
else:
|
|
250
345
|
type_to_update.is_signed = Expression(expr="False")
|
|
251
346
|
|
|
252
347
|
if type_to_update.fraction_digits is not None:
|
|
253
|
-
|
|
348
|
+
type_to_update.fraction_digits = _eval_expr(
|
|
254
349
|
type_to_update.fraction_digits,
|
|
255
350
|
scope,
|
|
256
351
|
int,
|
|
352
|
+
Integer,
|
|
257
353
|
type_to_update.type_name,
|
|
258
354
|
"fraction digits",
|
|
259
355
|
param_name,
|
|
260
356
|
)
|
|
261
|
-
if new_fraction_digits is not None:
|
|
262
|
-
type_to_update.fraction_digits = Expression(expr=str(new_fraction_digits))
|
|
263
357
|
else:
|
|
264
358
|
type_to_update.fraction_digits = Expression(expr="0")
|
|
265
359
|
|
|
266
360
|
return type_to_update
|
|
267
361
|
|
|
268
362
|
|
|
269
|
-
_EXPR_TYPE = TypeVar("_EXPR_TYPE")
|
|
363
|
+
_EXPR_TYPE = TypeVar("_EXPR_TYPE", bound=RuntimeConstant)
|
|
270
364
|
|
|
271
365
|
|
|
272
366
|
def _eval_expr(
|
|
273
367
|
expression: Expression,
|
|
274
368
|
scope: Scope,
|
|
275
369
|
expected_type: type[_EXPR_TYPE],
|
|
370
|
+
expected_qmod_type: type,
|
|
276
371
|
type_name: str,
|
|
277
372
|
attr_name: str,
|
|
278
373
|
param_name: str,
|
|
279
|
-
) ->
|
|
280
|
-
val = evaluate_classical_expression(expression, scope).value
|
|
281
|
-
if
|
|
374
|
+
) -> Expression:
|
|
375
|
+
val = evaluate_classical_expression(Expression(expr=expression.expr), scope).value
|
|
376
|
+
if isinstance(val, sympy.Basic):
|
|
377
|
+
val = get_sympy_val(val)
|
|
378
|
+
if expected_type is int and isinstance(val, float) and int(val) == val:
|
|
282
379
|
val = int(val)
|
|
283
|
-
if isinstance(val, expected_type)
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
isinstance(val, sympy.Basic) and len(val.free_symbols) > 0
|
|
380
|
+
if not isinstance(val, expected_type) and (
|
|
381
|
+
not isinstance(val, QmodAnnotatedExpression)
|
|
382
|
+
or not isinstance(val.get_type(val.root), expected_qmod_type)
|
|
287
383
|
):
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
)
|
|
384
|
+
raise ClassiqExpansionError(
|
|
385
|
+
f"When inferring the type of parameter {param_name!r}: "
|
|
386
|
+
f"{type_name} {attr_name} must be {expected_qmod_type.__name__}, got "
|
|
387
|
+
f"{str(val)!r}"
|
|
388
|
+
)
|
|
389
|
+
expr = Expression(expr=str(val))
|
|
390
|
+
expr._evaluated_expr = EvaluatedExpression(value=val)
|
|
391
|
+
return expr
|
|
294
392
|
|
|
295
393
|
|
|
296
394
|
def _evaluate_qstruct_in_quantum_symbol(
|
|
@@ -325,11 +423,9 @@ def evaluate_type_in_classical_symbol(
|
|
|
325
423
|
if isinstance(type_to_update, ClassicalArray):
|
|
326
424
|
length = type_to_update.length
|
|
327
425
|
if length is not None:
|
|
328
|
-
|
|
329
|
-
length, scope, int, "classical array", "length", param_name
|
|
426
|
+
length = _eval_expr(
|
|
427
|
+
length, scope, int, Integer, "classical array", "length", param_name
|
|
330
428
|
)
|
|
331
|
-
if new_length is not None:
|
|
332
|
-
length = Expression(expr=str(new_length))
|
|
333
429
|
updated_type = ClassicalArray(
|
|
334
430
|
element_type=evaluate_type_in_classical_symbol(
|
|
335
431
|
type_to_update.element_type, scope, param_name
|
|
@@ -4,6 +4,8 @@ from dataclasses import dataclass
|
|
|
4
4
|
from enum import Enum
|
|
5
5
|
from typing import Any, Union, cast
|
|
6
6
|
|
|
7
|
+
import sympy
|
|
8
|
+
|
|
7
9
|
from classiq.interface.exceptions import ClassiqInternalExpansionError
|
|
8
10
|
from classiq.interface.generator.expressions.proxies.classical.qmod_struct_instance import (
|
|
9
11
|
QmodStructInstance,
|
|
@@ -50,7 +52,7 @@ def qmod_val_to_str(val: Any) -> str:
|
|
|
50
52
|
return f"[{', '.join(qmod_val_to_str(item) for item in val)}]"
|
|
51
53
|
if isinstance(val, Enum):
|
|
52
54
|
return Enum.__str__(val)
|
|
53
|
-
if isinstance(val, (int, float, bool, complex)):
|
|
55
|
+
if isinstance(val, (int, float, bool, complex, sympy.Basic)):
|
|
54
56
|
return str(val)
|
|
55
57
|
raise ClassiqInternalExpansionError(
|
|
56
58
|
f"Unrecognized value {str(val)!r} of type {type(val)}"
|