classiq 0.74.0__py3-none-any.whl → 0.76.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/_internals/api_wrapper.py +36 -0
- classiq/analyzer/show_interactive_hack.py +58 -2
- classiq/applications/chemistry/chemistry_model_constructor.py +8 -1
- classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +2 -0
- classiq/applications/combinatorial_optimization/combinatorial_problem.py +4 -4
- classiq/applications/qnn/gradients/quantum_gradient.py +3 -5
- classiq/applications/qnn/gradients/simple_quantum_gradient.py +2 -2
- classiq/applications/qnn/qlayer.py +23 -19
- classiq/applications/qnn/types.py +1 -4
- classiq/execution/__init__.py +3 -0
- classiq/execution/execution_session.py +3 -16
- classiq/execution/qnn.py +2 -2
- classiq/execution/user_budgets.py +38 -0
- classiq/executor.py +7 -19
- classiq/interface/_version.py +1 -1
- classiq/interface/debug_info/debug_info.py +18 -13
- classiq/interface/executor/user_budget.py +56 -0
- classiq/interface/generator/application_apis/finance_declarations.py +3 -0
- classiq/interface/generator/expressions/atomic_expression_functions.py +3 -0
- classiq/interface/generator/expressions/proxies/classical/any_classical_value.py +30 -124
- classiq/interface/generator/expressions/proxies/classical/classical_array_proxy.py +46 -22
- classiq/interface/generator/expressions/proxies/classical/qmod_struct_instance.py +7 -0
- classiq/interface/generator/expressions/proxies/classical/utils.py +14 -13
- classiq/interface/generator/expressions/proxies/quantum/qmod_qscalar_proxy.py +9 -2
- classiq/interface/generator/expressions/proxies/quantum/qmod_sized_proxy.py +4 -1
- classiq/interface/generator/expressions/sympy_supported_expressions.py +1 -0
- classiq/interface/generator/functions/classical_type.py +36 -1
- classiq/interface/generator/functions/type_name.py +32 -5
- classiq/interface/generator/functions/type_qualifier.py +15 -0
- classiq/interface/generator/generated_circuit_data.py +11 -25
- classiq/interface/generator/model/preferences/preferences.py +7 -0
- classiq/interface/generator/quantum_program.py +5 -19
- classiq/interface/generator/synthesis_metadata/synthesis_execution_data.py +10 -13
- classiq/interface/helpers/backward_compatibility.py +9 -0
- classiq/interface/helpers/datastructures.py +6 -0
- classiq/interface/helpers/versioned_model.py +12 -0
- classiq/interface/interface_version.py +1 -1
- classiq/interface/model/handle_binding.py +12 -0
- classiq/interface/model/port_declaration.py +1 -2
- classiq/interface/model/quantum_lambda_function.py +2 -1
- classiq/interface/model/statement_block.py +9 -1
- classiq/interface/model/within_apply_operation.py +12 -0
- classiq/interface/server/routes.py +6 -0
- classiq/model_expansions/atomic_expression_functions_defs.py +82 -23
- classiq/model_expansions/capturing/captured_vars.py +2 -0
- classiq/model_expansions/closure.py +18 -0
- classiq/model_expansions/evaluators/argument_types.py +6 -5
- classiq/model_expansions/evaluators/classical_type_inference.py +17 -6
- classiq/model_expansions/evaluators/parameter_types.py +26 -13
- classiq/model_expansions/evaluators/type_type_match.py +2 -2
- classiq/model_expansions/expression_evaluator.py +1 -1
- classiq/model_expansions/generative_functions.py +66 -33
- classiq/model_expansions/interpreters/base_interpreter.py +27 -19
- classiq/model_expansions/interpreters/frontend_generative_interpreter.py +26 -0
- classiq/model_expansions/interpreters/generative_interpreter.py +25 -1
- classiq/model_expansions/quantum_operations/allocate.py +27 -11
- classiq/model_expansions/quantum_operations/assignment_result_processor.py +220 -19
- classiq/model_expansions/quantum_operations/bind.py +54 -30
- classiq/model_expansions/quantum_operations/block_evaluator.py +42 -0
- classiq/model_expansions/quantum_operations/call_emitter.py +14 -12
- classiq/model_expansions/quantum_operations/composite_emitter.py +1 -1
- classiq/model_expansions/quantum_operations/declarative_call_emitter.py +23 -9
- classiq/model_expansions/quantum_operations/emitter.py +21 -8
- classiq/model_expansions/quantum_operations/expression_evaluator.py +1 -0
- classiq/model_expansions/quantum_operations/handle_evaluator.py +1 -0
- classiq/model_expansions/quantum_operations/quantum_function_call.py +4 -3
- classiq/model_expansions/scope.py +10 -7
- classiq/model_expansions/sympy_conversion/arithmetics.py +18 -0
- classiq/model_expansions/sympy_conversion/expression_to_sympy.py +2 -0
- classiq/model_expansions/sympy_conversion/sympy_to_python.py +10 -1
- classiq/model_expansions/transformers/model_renamer.py +48 -8
- classiq/model_expansions/utils/handles_collector.py +1 -1
- classiq/model_expansions/visitors/symbolic_param_inference.py +197 -0
- classiq/model_expansions/visitors/variable_references.py +45 -9
- classiq/qmod/builtins/functions/allocation.py +2 -2
- classiq/qmod/builtins/functions/arithmetic.py +14 -12
- classiq/qmod/builtins/functions/standard_gates.py +23 -23
- classiq/qmod/declaration_inferrer.py +19 -7
- classiq/qmod/generative.py +9 -1
- classiq/qmod/native/expression_to_qmod.py +4 -0
- classiq/qmod/native/pretty_printer.py +8 -3
- classiq/qmod/pretty_print/pretty_printer.py +1 -1
- classiq/qmod/python_classical_type.py +4 -5
- classiq/qmod/qmod_constant.py +15 -7
- classiq/qmod/qmod_variable.py +30 -2
- classiq/qmod/quantum_function.py +19 -6
- classiq/qmod/semantics/lambdas.py +6 -2
- classiq/qmod/semantics/validation/main_validation.py +17 -4
- classiq/qmod/symbolic.py +8 -19
- classiq/qmod/symbolic_expr.py +34 -2
- classiq/qmod/write_qmod.py +5 -1
- classiq/synthesis.py +17 -31
- classiq/visualization.py +35 -0
- {classiq-0.74.0.dist-info → classiq-0.76.0.dist-info}/METADATA +1 -1
- {classiq-0.74.0.dist-info → classiq-0.76.0.dist-info}/RECORD +96 -91
- {classiq-0.74.0.dist-info → classiq-0.76.0.dist-info}/WHEEL +1 -1
@@ -66,8 +66,6 @@ class QuantumSymbol:
|
|
66
66
|
raise ClassiqExpansionError(
|
67
67
|
f"{self.quantum_type.type_name} is not subscriptable"
|
68
68
|
)
|
69
|
-
if TYPE_CHECKING:
|
70
|
-
assert self.quantum_type.length is not None
|
71
69
|
if isinstance(start, int) and isinstance(end, int) and start >= end:
|
72
70
|
raise ClassiqExpansionError(
|
73
71
|
f"{self.quantum_type.type_name} slice '{self.handle}[{start}:{end}]' "
|
@@ -75,6 +73,7 @@ class QuantumSymbol:
|
|
75
73
|
)
|
76
74
|
if (isinstance(start, int) and start < 0) or (
|
77
75
|
isinstance(end, int)
|
76
|
+
and self.quantum_type.length is not None
|
78
77
|
and self.quantum_type.length.is_constant()
|
79
78
|
and end > self.quantum_type.length_value
|
80
79
|
):
|
@@ -100,19 +99,23 @@ class QuantumSymbol:
|
|
100
99
|
raise ClassiqExpansionError(
|
101
100
|
f"{self.quantum_type.type_name} is not subscriptable"
|
102
101
|
)
|
103
|
-
if TYPE_CHECKING:
|
104
|
-
assert self.quantum_type.length is not None
|
105
102
|
if isinstance(index, int) and (
|
106
103
|
index < 0
|
107
104
|
or (
|
108
|
-
self.quantum_type.length
|
105
|
+
self.quantum_type.length is not None
|
106
|
+
and self.quantum_type.length.is_constant()
|
109
107
|
and index >= self.quantum_type.length_value
|
110
108
|
)
|
111
109
|
):
|
110
|
+
length_suffix = (
|
111
|
+
f" (of length {self.quantum_type.length})"
|
112
|
+
if self.quantum_type.length is not None
|
113
|
+
else ""
|
114
|
+
)
|
112
115
|
raise ClassiqExpansionError(
|
113
116
|
f"Index {index} is out of bounds for "
|
114
|
-
f"{self.quantum_type.type_name.lower()} {str(self.handle)!r}
|
115
|
-
f"
|
117
|
+
f"{self.quantum_type.type_name.lower()} {str(self.handle)!r}"
|
118
|
+
f"{length_suffix}"
|
116
119
|
)
|
117
120
|
return QuantumSymbol(
|
118
121
|
handle=SubscriptHandleBinding(
|
@@ -47,3 +47,21 @@ class BitwiseNot(Function):
|
|
47
47
|
return ~a
|
48
48
|
|
49
49
|
return None
|
50
|
+
|
51
|
+
|
52
|
+
class RShift(Function):
|
53
|
+
@classmethod
|
54
|
+
def eval(cls, a: Any, b: Any) -> Optional[int]:
|
55
|
+
if isinstance(a, Integer) and isinstance(b, Integer):
|
56
|
+
return a >> b
|
57
|
+
|
58
|
+
return None
|
59
|
+
|
60
|
+
|
61
|
+
class LShift(Function):
|
62
|
+
@classmethod
|
63
|
+
def eval(cls, a: Any, b: Any) -> Optional[int]:
|
64
|
+
if isinstance(a, Integer) and isinstance(b, Integer):
|
65
|
+
return a << b
|
66
|
+
|
67
|
+
return None
|
@@ -17,6 +17,9 @@ from sympy.printing.pycode import PythonCodePrinter
|
|
17
17
|
|
18
18
|
from classiq.interface.exceptions import ClassiqInternalExpansionError
|
19
19
|
from classiq.interface.generator.expressions.expression_types import ExpressionValue
|
20
|
+
from classiq.interface.generator.expressions.proxies.classical.any_classical_value import (
|
21
|
+
AnyClassicalValue,
|
22
|
+
)
|
20
23
|
|
21
24
|
from classiq.model_expansions.sympy_conversion.arithmetics import LogicalXor
|
22
25
|
|
@@ -24,7 +27,9 @@ from classiq.model_expansions.sympy_conversion.arithmetics import LogicalXor
|
|
24
27
|
def sympy_to_python(
|
25
28
|
value: Any, locals: Optional[dict[str, ExpressionValue]] = None
|
26
29
|
) -> ExpressionValue:
|
27
|
-
if isinstance(value,
|
30
|
+
if isinstance(value, AnyClassicalValue):
|
31
|
+
pass
|
32
|
+
elif isinstance(value, Integer):
|
28
33
|
value = int(value)
|
29
34
|
elif isinstance(value, Float):
|
30
35
|
value = float(value)
|
@@ -66,6 +71,8 @@ class SympyToQuantumExpressionTranslator(PythonCodePrinter):
|
|
66
71
|
"BitwiseOr": "|",
|
67
72
|
"BitwiseXor": "^",
|
68
73
|
"LogicalXor": "^",
|
74
|
+
"RShift": ">>",
|
75
|
+
"LShift": "<<",
|
69
76
|
}
|
70
77
|
UNARY_BITWISE_OPERATORS_MAPPING = {"BitwiseNot": "~"}
|
71
78
|
|
@@ -117,6 +124,8 @@ class SympyToBoolExpressionTranslator(SympyToQuantumExpressionTranslator):
|
|
117
124
|
|
118
125
|
|
119
126
|
def translate_sympy_quantum_expression(expr: Basic, preserve_bool_ops: bool) -> str:
|
127
|
+
if isinstance(expr, AnyClassicalValue):
|
128
|
+
return str(expr)
|
120
129
|
if preserve_bool_ops:
|
121
130
|
return SympyToBoolExpressionTranslator().doprint(expr)
|
122
131
|
else:
|
@@ -2,8 +2,10 @@ import ast
|
|
2
2
|
import re
|
3
3
|
from collections.abc import Mapping, Sequence
|
4
4
|
from dataclasses import dataclass
|
5
|
+
from functools import cmp_to_key
|
5
6
|
from typing import TypeVar, cast
|
6
7
|
|
8
|
+
from classiq.interface.exceptions import ClassiqInternalExpansionError
|
7
9
|
from classiq.interface.generator.expressions.expression import Expression
|
8
10
|
from classiq.interface.generator.visitor import NodeType
|
9
11
|
from classiq.interface.model.handle_binding import HandleBinding
|
@@ -23,6 +25,39 @@ def _replace_full_word(pattern: str, substitution: str, target: str) -> str:
|
|
23
25
|
)
|
24
26
|
|
25
27
|
|
28
|
+
def _handle_contains_handle(handle: HandleBinding, other_handle: HandleBinding) -> int:
|
29
|
+
if str(other_handle) in str(handle) or other_handle.qmod_expr in handle.qmod_expr:
|
30
|
+
return 1
|
31
|
+
if str(handle) in str(other_handle) or handle.qmod_expr in other_handle.qmod_expr:
|
32
|
+
return -1
|
33
|
+
return 0
|
34
|
+
|
35
|
+
|
36
|
+
class _ExprNormalizer(ast.NodeTransformer):
|
37
|
+
def visit_Call(self, node: ast.Call) -> ast.AST:
|
38
|
+
if not isinstance(node.func, ast.Name):
|
39
|
+
return self.generic_visit(node)
|
40
|
+
if node.func.id == "get_field":
|
41
|
+
if (
|
42
|
+
len(node.args) != 2
|
43
|
+
or not isinstance(node.args[1], ast.Constant)
|
44
|
+
or not isinstance(node.args[1].value, str)
|
45
|
+
):
|
46
|
+
raise ClassiqInternalExpansionError("Unexpected 'get_field' arguments")
|
47
|
+
return ast.Attribute(
|
48
|
+
value=self.visit(node.args[0]), attr=node.args[1].value
|
49
|
+
)
|
50
|
+
if node.func.id == "do_subscript":
|
51
|
+
if len(node.args) != 2:
|
52
|
+
raise ClassiqInternalExpansionError(
|
53
|
+
"Unexpected 'do_subscript' arguments"
|
54
|
+
)
|
55
|
+
return ast.Subscript(
|
56
|
+
value=self.visit(node.args[0]), slice=self.visit(node.args[1])
|
57
|
+
)
|
58
|
+
return self.generic_visit(node)
|
59
|
+
|
60
|
+
|
26
61
|
@dataclass(frozen=True)
|
27
62
|
class HandleRenaming:
|
28
63
|
source_handle: HandleBinding
|
@@ -39,26 +74,29 @@ SymbolRenaming = Mapping[HandleBinding, Sequence[HandleRenaming]]
|
|
39
74
|
def _rewrite_expression(
|
40
75
|
symbol_mapping: SymbolRenaming, expression: Expression
|
41
76
|
) -> Expression:
|
77
|
+
normalized_expr = _ExprNormalizer().visit(ast.parse(expression.expr))
|
42
78
|
vrc = VarRefCollector(
|
43
79
|
ignore_duplicated_handles=True, ignore_sympy_symbols=True, unevaluated=True
|
44
80
|
)
|
45
|
-
vrc.visit(
|
81
|
+
vrc.visit(normalized_expr)
|
46
82
|
|
47
83
|
handle_names = {
|
48
84
|
part.source_handle: part.target_var_handle
|
49
85
|
for parts in symbol_mapping.values()
|
50
86
|
for part in parts
|
51
87
|
}
|
52
|
-
new_expr_str =
|
53
|
-
|
88
|
+
new_expr_str = ast.unparse(normalized_expr)
|
89
|
+
sorted_handles = sorted(
|
90
|
+
vrc.var_handles,
|
91
|
+
key=cmp_to_key( # type:ignore[misc]
|
92
|
+
lambda handle, other_handle: _handle_contains_handle(other_handle, handle)
|
93
|
+
),
|
94
|
+
)
|
95
|
+
for handle in sorted_handles:
|
54
96
|
new_handle = handle.collapse()
|
55
97
|
for handle_to_replace, replacement in handle_names.items():
|
56
98
|
new_handle = new_handle.replace_prefix(handle_to_replace, replacement)
|
57
99
|
new_expr_str = _replace_full_word(str(handle), str(new_handle), new_expr_str)
|
58
|
-
if handle.qmod_expr != str(handle):
|
59
|
-
new_expr_str = _replace_full_word(
|
60
|
-
str(handle.qmod_expr), str(new_handle.qmod_expr), new_expr_str
|
61
|
-
)
|
62
100
|
|
63
101
|
new_expr = Expression(expr=new_expr_str)
|
64
102
|
new_expr._evaluated_expr = expression._evaluated_expr
|
@@ -92,7 +130,9 @@ class _ReplaceSplitVarsExpressions(ModelTransformer):
|
|
92
130
|
) -> QuantumExpressionOperation:
|
93
131
|
op = cast(QuantumExpressionOperation, self.generic_visit(op))
|
94
132
|
previous_var_handles = list(op._var_handles)
|
95
|
-
op._var_handles = self.visit(
|
133
|
+
op._var_handles = _ReplaceSplitVarsHandles(self._symbol_mapping).visit(
|
134
|
+
op._var_handles
|
135
|
+
)
|
96
136
|
op._var_types = {
|
97
137
|
new_handle.name: op._var_types.get(
|
98
138
|
new_handle.name, op._var_types[previous_handle.name]
|
@@ -18,7 +18,7 @@ class _HandlesCollector(Visitor):
|
|
18
18
|
self.handles.append(handle)
|
19
19
|
|
20
20
|
def visit_Expression(self, expression: Expression) -> None:
|
21
|
-
vrc = VarRefCollector(ignore_duplicated_handles=True)
|
21
|
+
vrc = VarRefCollector(ignore_duplicated_handles=True, unevaluated=True)
|
22
22
|
vrc.visit(ast.parse(expression.expr))
|
23
23
|
self.handles.extend(vrc.var_handles)
|
24
24
|
|
@@ -0,0 +1,197 @@
|
|
1
|
+
import ast
|
2
|
+
from collections.abc import Iterator, Mapping, Sequence
|
3
|
+
from contextlib import contextmanager
|
4
|
+
from itertools import chain, zip_longest
|
5
|
+
from typing import Optional, cast
|
6
|
+
|
7
|
+
from classiq.interface.generator.expressions.atomic_expression_functions import (
|
8
|
+
CLASSICAL_ATTRIBUTES,
|
9
|
+
)
|
10
|
+
from classiq.interface.generator.expressions.expression import Expression
|
11
|
+
from classiq.interface.generator.functions.classical_type import ClassicalType
|
12
|
+
from classiq.interface.generator.functions.type_name import TypeName
|
13
|
+
from classiq.interface.helpers.pydantic_model_helpers import nameables_to_dict
|
14
|
+
from classiq.interface.model.classical_parameter_declaration import (
|
15
|
+
AnonClassicalParameterDeclaration,
|
16
|
+
)
|
17
|
+
from classiq.interface.model.handle_binding import FieldHandleBinding, HandleBinding
|
18
|
+
from classiq.interface.model.model_visitor import ModelVisitor
|
19
|
+
from classiq.interface.model.native_function_definition import NativeFunctionDefinition
|
20
|
+
from classiq.interface.model.quantum_function_call import ArgValue, QuantumFunctionCall
|
21
|
+
from classiq.interface.model.quantum_function_declaration import (
|
22
|
+
AnonPositionalArg,
|
23
|
+
AnonQuantumFunctionDeclaration,
|
24
|
+
AnonQuantumOperandDeclaration,
|
25
|
+
NamedParamsQuantumFunctionDeclaration,
|
26
|
+
QuantumOperandDeclaration,
|
27
|
+
)
|
28
|
+
from classiq.interface.model.quantum_lambda_function import (
|
29
|
+
OperandIdentifier,
|
30
|
+
QuantumLambdaFunction,
|
31
|
+
)
|
32
|
+
|
33
|
+
from classiq.model_expansions.visitors.variable_references import VarRefCollector
|
34
|
+
|
35
|
+
|
36
|
+
def _get_expressions(arg: ArgValue) -> list[Expression]:
|
37
|
+
if isinstance(arg, Expression):
|
38
|
+
return [arg]
|
39
|
+
if isinstance(arg, HandleBinding):
|
40
|
+
return arg.expressions()
|
41
|
+
if isinstance(arg, OperandIdentifier):
|
42
|
+
return [arg.index]
|
43
|
+
if isinstance(arg, list):
|
44
|
+
return list(chain.from_iterable(_get_expressions(item) for item in arg))
|
45
|
+
return []
|
46
|
+
|
47
|
+
|
48
|
+
def _get_param_expressions(param: AnonPositionalArg) -> list[Expression]:
|
49
|
+
if isinstance(param, AnonClassicalParameterDeclaration):
|
50
|
+
return param.classical_type.expressions
|
51
|
+
if isinstance(param, AnonQuantumOperandDeclaration):
|
52
|
+
return list(
|
53
|
+
chain.from_iterable(
|
54
|
+
_get_param_expressions(nested_param)
|
55
|
+
for nested_param in param.positional_arg_declarations
|
56
|
+
)
|
57
|
+
)
|
58
|
+
return param.quantum_type.expressions
|
59
|
+
|
60
|
+
|
61
|
+
class SymbolicParamInference(ModelVisitor):
|
62
|
+
def __init__(
|
63
|
+
self,
|
64
|
+
functions: list[NativeFunctionDefinition],
|
65
|
+
additional_signatures: (
|
66
|
+
list[NamedParamsQuantumFunctionDeclaration] | None
|
67
|
+
) = None,
|
68
|
+
) -> None:
|
69
|
+
self._functions = nameables_to_dict(functions)
|
70
|
+
self._additional_signatures = (
|
71
|
+
{}
|
72
|
+
if additional_signatures is None
|
73
|
+
else nameables_to_dict(additional_signatures)
|
74
|
+
)
|
75
|
+
self._inferred_funcs: set[str] = set()
|
76
|
+
self._call_stack: list[str] = []
|
77
|
+
self._scope: Mapping[str, ClassicalType] = {}
|
78
|
+
self._scope_operands: dict[str, QuantumOperandDeclaration] = {}
|
79
|
+
|
80
|
+
def infer(self) -> None:
|
81
|
+
for func in self._functions.values():
|
82
|
+
self._infer_func_params(func)
|
83
|
+
|
84
|
+
def _is_recursive_call(self, func: str) -> bool:
|
85
|
+
return func in self._call_stack
|
86
|
+
|
87
|
+
@contextmanager
|
88
|
+
def function_context(
|
89
|
+
self,
|
90
|
+
func_name: Optional[str],
|
91
|
+
scope: Mapping[str, ClassicalType],
|
92
|
+
scope_operands: dict[str, QuantumOperandDeclaration],
|
93
|
+
) -> Iterator[None]:
|
94
|
+
if func_name is not None:
|
95
|
+
self._call_stack.append(func_name)
|
96
|
+
prev_scope = self._scope
|
97
|
+
self._scope = scope
|
98
|
+
prev_scope_ops = self._scope_operands
|
99
|
+
self._scope_operands = scope_operands
|
100
|
+
yield
|
101
|
+
self._scope = prev_scope
|
102
|
+
self._scope_operands = prev_scope_ops
|
103
|
+
if func_name is not None:
|
104
|
+
self._call_stack.pop()
|
105
|
+
|
106
|
+
def _infer_func_params(self, func: NativeFunctionDefinition) -> None:
|
107
|
+
if func.name in self._inferred_funcs:
|
108
|
+
return
|
109
|
+
scope = {param.name: param.classical_type for param in func.param_decls}
|
110
|
+
scope_operands = func.operand_declarations_dict
|
111
|
+
with self.function_context(func.name, scope, scope_operands):
|
112
|
+
for param in func.positional_arg_declarations:
|
113
|
+
for expr in _get_param_expressions(param):
|
114
|
+
self._process_compile_time_expression(expr.expr)
|
115
|
+
self._set_enums_generative(func)
|
116
|
+
self.visit(func.body)
|
117
|
+
self._inferred_funcs.add(func.name)
|
118
|
+
|
119
|
+
def _set_enums_generative(self, decl: AnonQuantumFunctionDeclaration) -> None:
|
120
|
+
for param in decl.positional_arg_declarations:
|
121
|
+
if (
|
122
|
+
isinstance(param, AnonClassicalParameterDeclaration)
|
123
|
+
and param.name is not None
|
124
|
+
and isinstance(param.classical_type, TypeName)
|
125
|
+
and param.classical_type.is_enum
|
126
|
+
):
|
127
|
+
self._scope[param.name].set_generative()
|
128
|
+
|
129
|
+
def visit_QuantumLambdaFunction(self, func: QuantumLambdaFunction) -> None:
|
130
|
+
func.set_op_decl(func.func_decl.model_copy(deep=True))
|
131
|
+
scope = dict(self._scope) | {
|
132
|
+
cast(str, param.name): param.classical_type
|
133
|
+
for param in func.named_func_decl.param_decls
|
134
|
+
}
|
135
|
+
scope_operands = self._scope_operands | nameables_to_dict(
|
136
|
+
cast(
|
137
|
+
Sequence[QuantumOperandDeclaration],
|
138
|
+
func.named_func_decl.operand_declarations,
|
139
|
+
)
|
140
|
+
)
|
141
|
+
with self.function_context(None, scope, scope_operands):
|
142
|
+
self._set_enums_generative(func.named_func_decl)
|
143
|
+
self.visit(func.body)
|
144
|
+
|
145
|
+
def visit_QuantumFunctionCall(self, call: QuantumFunctionCall) -> None:
|
146
|
+
self._process_compile_time_expressions(call.function)
|
147
|
+
name = call.func_name
|
148
|
+
if self._is_recursive_call(name):
|
149
|
+
return # Recursion is not fully supported
|
150
|
+
params = self._get_params(call)
|
151
|
+
for param, arg in zip_longest(params, call.positional_args):
|
152
|
+
if (
|
153
|
+
not isinstance(param, AnonClassicalParameterDeclaration)
|
154
|
+
or param.classical_type.is_generative
|
155
|
+
):
|
156
|
+
self._process_compile_time_expressions(arg)
|
157
|
+
else:
|
158
|
+
for expr in _get_expressions(arg):
|
159
|
+
self._process_nested_compile_time_expression(expr.expr)
|
160
|
+
self.generic_visit(call)
|
161
|
+
|
162
|
+
def _get_params(self, call: QuantumFunctionCall) -> Sequence[AnonPositionalArg]:
|
163
|
+
name = call.func_name
|
164
|
+
if name in self._scope_operands:
|
165
|
+
return self._scope_operands[name].positional_arg_declarations
|
166
|
+
elif name in self._functions:
|
167
|
+
func = self._functions[name]
|
168
|
+
self._infer_func_params(func)
|
169
|
+
return func.positional_arg_declarations
|
170
|
+
elif name in self._additional_signatures:
|
171
|
+
return self._additional_signatures[name].positional_arg_declarations
|
172
|
+
return call.func_decl.positional_arg_declarations
|
173
|
+
|
174
|
+
def _process_compile_time_expressions(self, arg: ArgValue) -> None:
|
175
|
+
for expr in _get_expressions(arg):
|
176
|
+
self._process_compile_time_expression(expr.expr)
|
177
|
+
|
178
|
+
def _process_compile_time_expression(self, expr: str) -> None:
|
179
|
+
vrc = VarRefCollector(
|
180
|
+
ignore_duplicated_handles=True, ignore_sympy_symbols=True, unevaluated=True
|
181
|
+
)
|
182
|
+
vrc.visit(ast.parse(expr))
|
183
|
+
for handle in vrc.var_handles:
|
184
|
+
if handle.name in self._scope and (
|
185
|
+
not isinstance(handle, FieldHandleBinding)
|
186
|
+
or handle.field not in CLASSICAL_ATTRIBUTES
|
187
|
+
):
|
188
|
+
self._scope[handle.name].set_generative()
|
189
|
+
|
190
|
+
def _process_nested_compile_time_expression(self, expr: str) -> None:
|
191
|
+
vrc = VarRefCollector(
|
192
|
+
ignore_duplicated_handles=True, ignore_sympy_symbols=True, unevaluated=True
|
193
|
+
)
|
194
|
+
vrc.visit(ast.parse(expr))
|
195
|
+
for handle in vrc.var_handles:
|
196
|
+
for nested_expr in handle.expressions():
|
197
|
+
self._process_compile_time_expression(nested_expr.expr)
|
@@ -95,16 +95,25 @@ class VarRefCollector(ast.NodeVisitor):
|
|
95
95
|
def visit_Attribute(self, node: ast.Attribute) -> Optional[FieldHandleBinding]:
|
96
96
|
return self._get_field_handle(node.value, node.attr)
|
97
97
|
|
98
|
-
def visit_Call(self, node: ast.Call) -> Optional[
|
99
|
-
if (
|
100
|
-
not isinstance(node.func, ast.Name)
|
101
|
-
or node.func.id != "get_field"
|
102
|
-
or len(node.args) != 2
|
103
|
-
or not isinstance(node.args[1], ast.Constant)
|
104
|
-
or not isinstance(node.args[1].value, str)
|
105
|
-
):
|
98
|
+
def visit_Call(self, node: ast.Call) -> Optional[HandleBinding]:
|
99
|
+
if not isinstance(node.func, ast.Name):
|
106
100
|
return self.generic_visit(node)
|
107
|
-
|
101
|
+
if node.func.id == "get_field":
|
102
|
+
if (
|
103
|
+
len(node.args) != 2
|
104
|
+
or not isinstance(node.args[1], ast.Constant)
|
105
|
+
or not isinstance(node.args[1].value, str)
|
106
|
+
):
|
107
|
+
raise ClassiqInternalExpansionError("Unexpected 'get_field' arguments")
|
108
|
+
return self._get_field_handle(node.args[0], node.args[1].value)
|
109
|
+
if node.func.id == "do_subscript":
|
110
|
+
if len(node.args) != 2:
|
111
|
+
raise ClassiqInternalExpansionError(
|
112
|
+
"Unexpected 'do_subscript' arguments"
|
113
|
+
)
|
114
|
+
self.visit(node.args[1])
|
115
|
+
return self._get_subscript_handle(node.args[0], node.args[1])
|
116
|
+
return self.generic_visit(node)
|
108
117
|
|
109
118
|
def _get_field_handle(
|
110
119
|
self, subject: ast.expr, field: str
|
@@ -121,6 +130,33 @@ class VarRefCollector(ast.NodeVisitor):
|
|
121
130
|
self._var_handles[handle] = True
|
122
131
|
return handle
|
123
132
|
|
133
|
+
def _get_subscript_handle(
|
134
|
+
self, subject: ast.expr, subscript: ast.expr
|
135
|
+
) -> Optional[HandleBinding]:
|
136
|
+
with self.set_nested():
|
137
|
+
base_handle = self.visit(subject)
|
138
|
+
if base_handle is None:
|
139
|
+
return None
|
140
|
+
handle: HandleBinding
|
141
|
+
if isinstance(subscript, ast.Slice):
|
142
|
+
if subscript.lower is None or subscript.upper is None:
|
143
|
+
raise ClassiqExpansionError(
|
144
|
+
f"{str(base_handle)!r} slice must specify both lower and upper bounds"
|
145
|
+
)
|
146
|
+
handle = SlicedHandleBinding(
|
147
|
+
base_handle=base_handle,
|
148
|
+
start=Expression(expr=ast.unparse(subscript.lower)),
|
149
|
+
end=Expression(expr=ast.unparse(subscript.upper)),
|
150
|
+
)
|
151
|
+
else:
|
152
|
+
handle = SubscriptHandleBinding(
|
153
|
+
base_handle=base_handle,
|
154
|
+
index=Expression(expr=ast.unparse(subscript)),
|
155
|
+
)
|
156
|
+
if not self._is_nested:
|
157
|
+
self._var_handles[handle] = True
|
158
|
+
return handle
|
159
|
+
|
124
160
|
def visit_Name(self, node: ast.Name) -> Optional[HandleBinding]:
|
125
161
|
if not self._ignore_sympy_symbols and node.id in set(
|
126
162
|
SYMPY_SUPPORTED_EXPRESSIONS
|
@@ -2,11 +2,11 @@ from typing import Literal
|
|
2
2
|
|
3
3
|
from classiq.qmod.qfunc import qfunc
|
4
4
|
from classiq.qmod.qmod_parameter import CArray, CReal
|
5
|
-
from classiq.qmod.qmod_variable import Input, Output, QArray, QBit
|
5
|
+
from classiq.qmod.qmod_variable import Input, Output, QArray, QBit, QFree
|
6
6
|
|
7
7
|
|
8
8
|
@qfunc(external=True)
|
9
|
-
def free(in_: Input[QArray[QBit]]) -> None:
|
9
|
+
def free(in_: QFree[Input[QArray[QBit]]]) -> None:
|
10
10
|
"""
|
11
11
|
[Qmod core-library function]
|
12
12
|
|
@@ -2,7 +2,7 @@ from typing import Literal
|
|
2
2
|
|
3
3
|
from classiq.qmod.qfunc import qfunc
|
4
4
|
from classiq.qmod.qmod_parameter import CArray, CBool, CReal
|
5
|
-
from classiq.qmod.qmod_variable import Output, QArray, QBit, QNum
|
5
|
+
from classiq.qmod.qmod_variable import Const, Output, QArray, QBit, QFree, QNum
|
6
6
|
|
7
7
|
|
8
8
|
@qfunc(external=True)
|
@@ -24,13 +24,15 @@ def unitary(
|
|
24
24
|
|
25
25
|
@qfunc(external=True)
|
26
26
|
def add(
|
27
|
-
left: QNum,
|
28
|
-
right: QNum,
|
29
|
-
result:
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
27
|
+
left: Const[QNum],
|
28
|
+
right: Const[QNum],
|
29
|
+
result: QFree[
|
30
|
+
Output[
|
31
|
+
QNum[
|
32
|
+
Literal["result_size"],
|
33
|
+
Literal["result_is_signed"],
|
34
|
+
Literal["result_fraction_places"],
|
35
|
+
]
|
34
36
|
]
|
35
37
|
],
|
36
38
|
result_size: CReal,
|
@@ -41,20 +43,20 @@ def add(
|
|
41
43
|
|
42
44
|
|
43
45
|
@qfunc(external=True)
|
44
|
-
def modular_add(left: QArray[QBit], right: QArray[QBit]) -> None:
|
46
|
+
def modular_add(left: Const[QArray[QBit]], right: QFree[QArray[QBit]]) -> None:
|
45
47
|
pass
|
46
48
|
|
47
49
|
|
48
50
|
@qfunc(external=True)
|
49
|
-
def modular_add_constant(left: CReal, right: QNum) -> None:
|
51
|
+
def modular_add_constant(left: CReal, right: QFree[QNum]) -> None:
|
50
52
|
pass
|
51
53
|
|
52
54
|
|
53
55
|
@qfunc(external=True)
|
54
|
-
def integer_xor(left: QArray[QBit], right: QArray[QBit]) -> None:
|
56
|
+
def integer_xor(left: Const[QArray[QBit]], right: QFree[QArray[QBit]]) -> None:
|
55
57
|
pass
|
56
58
|
|
57
59
|
|
58
60
|
@qfunc(external=True)
|
59
|
-
def real_xor_constant(left: CReal, right: QNum) -> None:
|
61
|
+
def real_xor_constant(left: CReal, right: QFree[QNum]) -> None:
|
60
62
|
pass
|