classiq 0.77.0__py3-none-any.whl → 0.79.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/applications/iqae/__init__.py +0 -0
- classiq/applications/iqae/iqae.py +207 -0
- classiq/execution/__init__.py +1 -1
- classiq/interface/_version.py +1 -1
- classiq/interface/applications/iqae/__init__.py +0 -0
- classiq/interface/applications/iqae/generic_iqae.py +222 -0
- classiq/interface/applications/iqae/iqae_result.py +45 -0
- classiq/interface/debug_info/debug_info.py +3 -0
- classiq/interface/executor/execution_result.py +1 -1
- classiq/interface/executor/user_budget.py +1 -1
- classiq/interface/generator/arith/arithmetic.py +10 -6
- classiq/interface/generator/arith/binary_ops.py +8 -11
- classiq/interface/generator/expressions/proxies/classical/any_classical_value.py +9 -3
- classiq/interface/generator/expressions/proxies/classical/classical_array_proxy.py +90 -23
- classiq/interface/generator/expressions/proxies/classical/utils.py +4 -0
- classiq/interface/generator/functions/classical_type.py +74 -0
- classiq/interface/generator/functions/concrete_types.py +3 -0
- classiq/interface/generator/functions/type_name.py +32 -3
- classiq/interface/generator/generated_circuit_data.py +21 -8
- classiq/interface/helpers/model_normalizer.py +47 -0
- classiq/interface/ide/visual_model.py +2 -0
- classiq/interface/interface_version.py +1 -1
- classiq/interface/model/bounds.py +12 -0
- classiq/interface/model/model.py +9 -5
- classiq/interface/model/quantum_type.py +25 -3
- classiq/interface/model/statement_block.py +2 -0
- classiq/model_expansions/atomic_expression_functions_defs.py +20 -6
- classiq/model_expansions/closure.py +1 -58
- classiq/model_expansions/evaluators/argument_types.py +21 -2
- classiq/model_expansions/evaluators/classical_type_inference.py +42 -13
- classiq/model_expansions/evaluators/quantum_type_utils.py +31 -3
- classiq/model_expansions/function_builder.py +0 -45
- classiq/model_expansions/generative_functions.py +1 -1
- classiq/model_expansions/interpreters/base_interpreter.py +12 -14
- classiq/model_expansions/interpreters/frontend_generative_interpreter.py +0 -17
- classiq/model_expansions/interpreters/generative_interpreter.py +9 -0
- classiq/model_expansions/quantum_operations/__init__.py +3 -0
- classiq/model_expansions/quantum_operations/allocate.py +3 -1
- classiq/model_expansions/quantum_operations/assignment_result_processor.py +7 -3
- classiq/model_expansions/quantum_operations/bind.py +8 -1
- classiq/model_expansions/quantum_operations/bounds.py +30 -0
- classiq/model_expansions/quantum_operations/call_emitter.py +116 -37
- classiq/model_expansions/quantum_operations/emitter.py +6 -1
- classiq/model_expansions/quantum_operations/function_calls_cache.py +91 -0
- classiq/model_expansions/quantum_operations/quantum_function_call.py +5 -6
- classiq/model_expansions/scope.py +33 -13
- classiq/model_expansions/scope_initialization.py +1 -14
- classiq/model_expansions/transformers/type_qualifier_inference.py +183 -0
- classiq/model_expansions/utils/text_utils.py +4 -2
- classiq/model_expansions/visitors/symbolic_param_inference.py +33 -28
- classiq/model_expansions/visitors/variable_references.py +39 -43
- classiq/open_library/functions/linear_pauli_rotation.py +6 -6
- classiq/open_library/functions/state_preparation.py +1 -1
- classiq/open_library/functions/utility_functions.py +2 -2
- classiq/qmod/builtins/classical_execution_primitives.py +1 -1
- classiq/qmod/declaration_inferrer.py +5 -3
- classiq/qmod/model_state_container.py +21 -1
- classiq/qmod/native/pretty_printer.py +8 -0
- classiq/qmod/pretty_print/expression_to_python.py +8 -1
- classiq/qmod/pretty_print/pretty_printer.py +7 -0
- classiq/qmod/python_classical_type.py +1 -1
- classiq/qmod/qmod_parameter.py +43 -7
- classiq/qmod/qmod_variable.py +7 -4
- classiq/qmod/semantics/annotation/qstruct_annotator.py +15 -4
- classiq/qmod/utilities.py +5 -1
- {classiq-0.77.0.dist-info → classiq-0.79.0.dist-info}/METADATA +1 -1
- {classiq-0.77.0.dist-info → classiq-0.79.0.dist-info}/RECORD +68 -59
- classiq/interface/executor/iqae_result.py +0 -17
- {classiq-0.77.0.dist-info → classiq-0.79.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,183 @@
|
|
1
|
+
import ast
|
2
|
+
import functools
|
3
|
+
import itertools
|
4
|
+
from collections.abc import Collection, Iterator, Sequence
|
5
|
+
from contextlib import contextmanager
|
6
|
+
|
7
|
+
from classiq.interface.exceptions import ClassiqInternalExpansionError
|
8
|
+
from classiq.interface.generator.functions.type_qualifier import TypeQualifier
|
9
|
+
from classiq.interface.model.allocate import Allocate
|
10
|
+
from classiq.interface.model.bind_operation import BindOperation
|
11
|
+
from classiq.interface.model.control import Control
|
12
|
+
from classiq.interface.model.invert import Invert
|
13
|
+
from classiq.interface.model.model_visitor import ModelVisitor
|
14
|
+
from classiq.interface.model.phase_operation import PhaseOperation
|
15
|
+
from classiq.interface.model.port_declaration import PortDeclaration
|
16
|
+
from classiq.interface.model.power import Power
|
17
|
+
from classiq.interface.model.quantum_expressions.amplitude_loading_operation import (
|
18
|
+
AmplitudeLoadingOperation,
|
19
|
+
)
|
20
|
+
from classiq.interface.model.quantum_expressions.arithmetic_operation import (
|
21
|
+
ArithmeticOperation,
|
22
|
+
)
|
23
|
+
from classiq.interface.model.quantum_expressions.quantum_expression import (
|
24
|
+
QuantumExpressionOperation,
|
25
|
+
)
|
26
|
+
from classiq.interface.model.quantum_function_call import QuantumFunctionCall
|
27
|
+
from classiq.interface.model.quantum_statement import QuantumStatement
|
28
|
+
from classiq.interface.model.within_apply_operation import WithinApply
|
29
|
+
|
30
|
+
from classiq.model_expansions.visitors.variable_references import VarRefCollector
|
31
|
+
|
32
|
+
|
33
|
+
class TypeQualifierInference(ModelVisitor):
|
34
|
+
"""
|
35
|
+
This class assumes that function calls are topologically sorted, so it traverses
|
36
|
+
the list of function calls and infers the type qualifiers for each function call
|
37
|
+
without going recursively into the function calls.
|
38
|
+
The function definition ports are modified inplace.
|
39
|
+
"""
|
40
|
+
|
41
|
+
def __init__(self, support_unused_ports: bool = True) -> None:
|
42
|
+
self._signature_ports: dict[str, PortDeclaration] = dict()
|
43
|
+
self._inferred_ports: dict[str, PortDeclaration] = dict()
|
44
|
+
self._support_unused_ports = (
|
45
|
+
support_unused_ports # could be turned off for debugging
|
46
|
+
)
|
47
|
+
|
48
|
+
@contextmanager
|
49
|
+
def infer_ports(self, ports: Collection[PortDeclaration]) -> Iterator[bool]:
|
50
|
+
for port in ports:
|
51
|
+
if port.type_qualifier is TypeQualifier.Inferred:
|
52
|
+
self._inferred_ports[port.name] = port
|
53
|
+
else:
|
54
|
+
self._signature_ports[port.name] = port
|
55
|
+
|
56
|
+
yield len(self._inferred_ports) > 0
|
57
|
+
|
58
|
+
self._set_unused_as_const()
|
59
|
+
self._signature_ports.clear()
|
60
|
+
self._inferred_ports.clear()
|
61
|
+
|
62
|
+
def _set_unused_as_const(self) -> None:
|
63
|
+
unresolved_ports = [
|
64
|
+
port
|
65
|
+
for port in self._inferred_ports.values()
|
66
|
+
if port.type_qualifier is TypeQualifier.Inferred
|
67
|
+
]
|
68
|
+
if not self._support_unused_ports and len(unresolved_ports) > 0:
|
69
|
+
raise ClassiqInternalExpansionError(
|
70
|
+
f"Unresolved inferred ports detected: {', '.join(port.name for port in unresolved_ports)}. "
|
71
|
+
"All ports must have their type qualifiers resolved."
|
72
|
+
)
|
73
|
+
for port in unresolved_ports:
|
74
|
+
port.type_qualifier = TypeQualifier.Const
|
75
|
+
|
76
|
+
def _reduce_qualifier(self, candidate: str, qualifier: TypeQualifier) -> None:
|
77
|
+
if candidate in self._inferred_ports:
|
78
|
+
self._inferred_ports[candidate].type_qualifier = TypeQualifier.and_(
|
79
|
+
self._inferred_ports[candidate].type_qualifier, qualifier
|
80
|
+
)
|
81
|
+
|
82
|
+
def run(
|
83
|
+
self, ports: Collection[PortDeclaration], body: Sequence[QuantumStatement]
|
84
|
+
) -> None:
|
85
|
+
with self.infer_ports(ports) as should_infer:
|
86
|
+
if should_infer:
|
87
|
+
self.visit(body)
|
88
|
+
|
89
|
+
def visit_QuantumFunctionCall(self, call: QuantumFunctionCall) -> None:
|
90
|
+
for handle, port in call.handles_with_params:
|
91
|
+
self._reduce_qualifier(handle.name, port.type_qualifier)
|
92
|
+
|
93
|
+
def visit_Allocate(self, alloc: Allocate) -> None:
|
94
|
+
self._reduce_qualifier(alloc.target.name, TypeQualifier.QFree)
|
95
|
+
|
96
|
+
def visit_BindOperation(self, bind_op: BindOperation) -> None:
|
97
|
+
reduced_qualifier = self._get_reduced_qualifier(bind_op)
|
98
|
+
for handle in itertools.chain(bind_op.in_handles, bind_op.out_handles):
|
99
|
+
self._reduce_qualifier(handle.name, reduced_qualifier)
|
100
|
+
|
101
|
+
def _get_reduced_qualifier(self, bind_op: BindOperation) -> TypeQualifier:
|
102
|
+
handles = itertools.chain(bind_op.in_handles, bind_op.out_handles)
|
103
|
+
op_vars = {handle.name for handle in handles}
|
104
|
+
|
105
|
+
signature_ports = {
|
106
|
+
name: self._signature_ports[name]
|
107
|
+
for name in op_vars.intersection(self._signature_ports)
|
108
|
+
}
|
109
|
+
known_inferred_ports = {
|
110
|
+
name: self._inferred_ports[name]
|
111
|
+
for name in op_vars.intersection(self._inferred_ports)
|
112
|
+
if self._inferred_ports[name].type_qualifier is not TypeQualifier.Inferred
|
113
|
+
}
|
114
|
+
min_qualifier = self._get_min_qualifier(known_inferred_ports, signature_ports)
|
115
|
+
if not all(
|
116
|
+
port.type_qualifier is min_qualifier for port in signature_ports.values()
|
117
|
+
):
|
118
|
+
raise ClassiqInternalExpansionError(
|
119
|
+
f"Bind operation {bind_op} has inconsistent type qualifiers: "
|
120
|
+
f"{signature_ports}, {known_inferred_ports}"
|
121
|
+
)
|
122
|
+
|
123
|
+
return min_qualifier
|
124
|
+
|
125
|
+
@staticmethod
|
126
|
+
def _get_min_qualifier(
|
127
|
+
known_inferred_ports: dict[str, PortDeclaration],
|
128
|
+
signature_ports: dict[str, PortDeclaration],
|
129
|
+
) -> TypeQualifier:
|
130
|
+
known_ports = tuple(
|
131
|
+
itertools.chain(signature_ports.values(), known_inferred_ports.values())
|
132
|
+
)
|
133
|
+
if len(known_ports) == 0:
|
134
|
+
return TypeQualifier.Quantum
|
135
|
+
elif len(known_ports) == 1:
|
136
|
+
return known_ports[0].type_qualifier
|
137
|
+
else:
|
138
|
+
return functools.reduce(
|
139
|
+
TypeQualifier.and_, (port.type_qualifier for port in known_ports)
|
140
|
+
)
|
141
|
+
|
142
|
+
@staticmethod
|
143
|
+
def _extract_expr_vars(expr_op: QuantumExpressionOperation) -> list[str]:
|
144
|
+
vrc = VarRefCollector(
|
145
|
+
ignore_duplicated_handles=True, ignore_sympy_symbols=True, unevaluated=True
|
146
|
+
)
|
147
|
+
vrc.visit(ast.parse(expr_op.expression.expr))
|
148
|
+
return [handle.name for handle in vrc.var_handles]
|
149
|
+
|
150
|
+
def visit_ArithmeticOperation(self, arith: ArithmeticOperation) -> None:
|
151
|
+
result_var = arith.result_var.name
|
152
|
+
self._reduce_qualifier(result_var, TypeQualifier.QFree)
|
153
|
+
for expr_var in self._extract_expr_vars(arith):
|
154
|
+
self._reduce_qualifier(expr_var, TypeQualifier.Const)
|
155
|
+
|
156
|
+
def visit_AmplitudeLoadingOperation(
|
157
|
+
self, amp_load: AmplitudeLoadingOperation
|
158
|
+
) -> None:
|
159
|
+
result_var = amp_load.result_var.name
|
160
|
+
self._reduce_qualifier(result_var, TypeQualifier.Quantum)
|
161
|
+
for expr_var in self._extract_expr_vars(amp_load):
|
162
|
+
self._reduce_qualifier(expr_var, TypeQualifier.Const)
|
163
|
+
|
164
|
+
def visit_PhaseOperation(self, phase_op: PhaseOperation) -> None:
|
165
|
+
for expr_var in self._extract_expr_vars(phase_op):
|
166
|
+
self._reduce_qualifier(expr_var, TypeQualifier.Const)
|
167
|
+
|
168
|
+
def visit_Control(self, control: Control) -> None:
|
169
|
+
for control_var in self._extract_expr_vars(control):
|
170
|
+
self._reduce_qualifier(control_var, TypeQualifier.Const)
|
171
|
+
self.visit(control.body)
|
172
|
+
if control.else_block is not None:
|
173
|
+
self.visit(control.else_block)
|
174
|
+
|
175
|
+
def visit_Invert(self, invert: Invert) -> None:
|
176
|
+
self.visit(invert.body)
|
177
|
+
|
178
|
+
def visit_Power(self, power: Power) -> None:
|
179
|
+
self.visit(power.body)
|
180
|
+
|
181
|
+
def visit_WithinApply(self, within_apply: WithinApply) -> None:
|
182
|
+
self.visit(within_apply.compute)
|
183
|
+
self.visit(within_apply.action)
|
@@ -10,7 +10,9 @@ def they(items: list) -> str:
|
|
10
10
|
return "it" if len(items) == 1 else "they"
|
11
11
|
|
12
12
|
|
13
|
-
def readable_list(items: list) -> str:
|
13
|
+
def readable_list(items: list, quote: bool = False) -> str:
|
14
|
+
if quote:
|
15
|
+
items = [repr(str(item)) for item in items]
|
14
16
|
if len(items) == 1:
|
15
17
|
return str(items[0])
|
16
|
-
return f"{', '.join(items[:-1])} and {items[-1]}"
|
18
|
+
return f"{', '.join(items[:-1])}{',' if len(items) > 2 else ''} and {items[-1]}"
|
@@ -8,7 +8,12 @@ from classiq.interface.generator.expressions.atomic_expression_functions import
|
|
8
8
|
CLASSICAL_ATTRIBUTES,
|
9
9
|
)
|
10
10
|
from classiq.interface.generator.expressions.expression import Expression
|
11
|
-
from classiq.interface.generator.functions.classical_type import
|
11
|
+
from classiq.interface.generator.functions.classical_type import (
|
12
|
+
ClassicalArray,
|
13
|
+
ClassicalList,
|
14
|
+
ClassicalTuple,
|
15
|
+
ClassicalType,
|
16
|
+
)
|
12
17
|
from classiq.interface.generator.functions.type_name import TypeName
|
13
18
|
from classiq.interface.helpers.pydantic_model_helpers import nameables_to_dict
|
14
19
|
from classiq.interface.model.classical_parameter_declaration import (
|
@@ -20,7 +25,6 @@ from classiq.interface.model.native_function_definition import NativeFunctionDef
|
|
20
25
|
from classiq.interface.model.quantum_function_call import ArgValue, QuantumFunctionCall
|
21
26
|
from classiq.interface.model.quantum_function_declaration import (
|
22
27
|
AnonPositionalArg,
|
23
|
-
AnonQuantumFunctionDeclaration,
|
24
28
|
AnonQuantumOperandDeclaration,
|
25
29
|
NamedParamsQuantumFunctionDeclaration,
|
26
30
|
QuantumOperandDeclaration,
|
@@ -33,6 +37,22 @@ from classiq.interface.model.quantum_lambda_function import (
|
|
33
37
|
from classiq.model_expansions.visitors.variable_references import VarRefCollector
|
34
38
|
|
35
39
|
|
40
|
+
def set_generative_recursively(classical_type: ClassicalType) -> None:
|
41
|
+
if (
|
42
|
+
isinstance(classical_type, TypeName)
|
43
|
+
and classical_type.has_classical_struct_decl
|
44
|
+
):
|
45
|
+
for field_type in classical_type.classical_struct_decl.variables.values():
|
46
|
+
set_generative_recursively(field_type)
|
47
|
+
return
|
48
|
+
classical_type.set_generative()
|
49
|
+
if isinstance(classical_type, (ClassicalArray, ClassicalList)):
|
50
|
+
set_generative_recursively(classical_type.element_type)
|
51
|
+
if isinstance(classical_type, ClassicalTuple):
|
52
|
+
for element_type in classical_type.element_types:
|
53
|
+
set_generative_recursively(element_type)
|
54
|
+
|
55
|
+
|
36
56
|
def _get_expressions(arg: ArgValue) -> list[Expression]:
|
37
57
|
if isinstance(arg, Expression):
|
38
58
|
return [arg]
|
@@ -73,7 +93,6 @@ class SymbolicParamInference(ModelVisitor):
|
|
73
93
|
else nameables_to_dict(additional_signatures)
|
74
94
|
)
|
75
95
|
self._inferred_funcs: set[str] = set()
|
76
|
-
self._call_stack: list[str] = []
|
77
96
|
self._scope: Mapping[str, ClassicalType] = {}
|
78
97
|
self._scope_operands: dict[str, QuantumOperandDeclaration] = {}
|
79
98
|
|
@@ -81,9 +100,6 @@ class SymbolicParamInference(ModelVisitor):
|
|
81
100
|
for func in self._functions.values():
|
82
101
|
self._infer_func_params(func)
|
83
102
|
|
84
|
-
def _is_recursive_call(self, func: str) -> bool:
|
85
|
-
return func in self._call_stack
|
86
|
-
|
87
103
|
@contextmanager
|
88
104
|
def function_context(
|
89
105
|
self,
|
@@ -91,8 +107,6 @@ class SymbolicParamInference(ModelVisitor):
|
|
91
107
|
scope: Mapping[str, ClassicalType],
|
92
108
|
scope_operands: dict[str, QuantumOperandDeclaration],
|
93
109
|
) -> Iterator[None]:
|
94
|
-
if func_name is not None:
|
95
|
-
self._call_stack.append(func_name)
|
96
110
|
prev_scope = self._scope
|
97
111
|
self._scope = scope
|
98
112
|
prev_scope_ops = self._scope_operands
|
@@ -100,31 +114,18 @@ class SymbolicParamInference(ModelVisitor):
|
|
100
114
|
yield
|
101
115
|
self._scope = prev_scope
|
102
116
|
self._scope_operands = prev_scope_ops
|
103
|
-
if func_name is not None:
|
104
|
-
self._call_stack.pop()
|
105
117
|
|
106
118
|
def _infer_func_params(self, func: NativeFunctionDefinition) -> None:
|
107
119
|
if func.name in self._inferred_funcs:
|
108
120
|
return
|
121
|
+
self._inferred_funcs.add(func.name)
|
109
122
|
scope = {param.name: param.classical_type for param in func.param_decls}
|
110
123
|
scope_operands = func.operand_declarations_dict
|
111
124
|
with self.function_context(func.name, scope, scope_operands):
|
112
125
|
for param in func.positional_arg_declarations:
|
113
126
|
for expr in _get_param_expressions(param):
|
114
127
|
self._process_compile_time_expression(expr.expr)
|
115
|
-
self._set_enums_generative(func)
|
116
128
|
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
|
|
129
130
|
def visit_QuantumLambdaFunction(self, func: QuantumLambdaFunction) -> None:
|
130
131
|
func.set_op_decl(func.func_decl.model_copy(deep=True))
|
@@ -139,26 +140,28 @@ class SymbolicParamInference(ModelVisitor):
|
|
139
140
|
)
|
140
141
|
)
|
141
142
|
with self.function_context(None, scope, scope_operands):
|
142
|
-
self._set_enums_generative(func.named_func_decl)
|
143
143
|
self.visit(func.body)
|
144
144
|
|
145
145
|
def visit_QuantumFunctionCall(self, call: QuantumFunctionCall) -> None:
|
146
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
147
|
params = self._get_params(call)
|
151
148
|
for param, arg in zip_longest(params, call.positional_args):
|
152
149
|
if (
|
153
150
|
not isinstance(param, AnonClassicalParameterDeclaration)
|
154
|
-
or param.classical_type.
|
151
|
+
or param.classical_type.is_purely_generative
|
155
152
|
):
|
156
153
|
self._process_compile_time_expressions(arg)
|
157
154
|
else:
|
155
|
+
if isinstance(arg, Expression):
|
156
|
+
for expr_part in self.get_generative_expression_parts(arg.expr):
|
157
|
+
self._process_compile_time_expression(expr_part)
|
158
158
|
for expr in _get_expressions(arg):
|
159
159
|
self._process_nested_compile_time_expression(expr.expr)
|
160
160
|
self.generic_visit(call)
|
161
161
|
|
162
|
+
def get_generative_expression_parts(self, expr: str) -> list[str]:
|
163
|
+
return []
|
164
|
+
|
162
165
|
def _get_params(self, call: QuantumFunctionCall) -> Sequence[AnonPositionalArg]:
|
163
166
|
name = call.func_name
|
164
167
|
if name in self._scope_operands:
|
@@ -185,7 +188,7 @@ class SymbolicParamInference(ModelVisitor):
|
|
185
188
|
not isinstance(handle, FieldHandleBinding)
|
186
189
|
or handle.field not in CLASSICAL_ATTRIBUTES
|
187
190
|
):
|
188
|
-
self._scope[handle.name]
|
191
|
+
set_generative_recursively(self._scope[handle.name])
|
189
192
|
|
190
193
|
def _process_nested_compile_time_expression(self, expr: str) -> None:
|
191
194
|
vrc = VarRefCollector(
|
@@ -195,3 +198,5 @@ class SymbolicParamInference(ModelVisitor):
|
|
195
198
|
for handle in vrc.var_handles:
|
196
199
|
for nested_expr in handle.expressions():
|
197
200
|
self._process_compile_time_expression(nested_expr.expr)
|
201
|
+
for handle in vrc.subscript_handles:
|
202
|
+
self._process_compile_time_expression(str(handle))
|
@@ -34,11 +34,18 @@ class VarRefCollector(ast.NodeVisitor):
|
|
34
34
|
self._ignore_sympy_symbols = ignore_sympy_symbols
|
35
35
|
self._unevaluated = unevaluated
|
36
36
|
self._is_nested = False
|
37
|
+
self._in_subscript = False
|
37
38
|
|
38
39
|
@property
|
39
40
|
def var_handles(self) -> list[HandleBinding]:
|
40
41
|
return list(self._var_handles)
|
41
42
|
|
43
|
+
@property
|
44
|
+
def subscript_handles(self) -> list[HandleBinding]:
|
45
|
+
return [
|
46
|
+
handle for handle, in_subscript in self._var_handles.items() if in_subscript
|
47
|
+
]
|
48
|
+
|
42
49
|
def visit(self, node: ast.AST) -> Union[
|
43
50
|
SubscriptHandleBinding,
|
44
51
|
SlicedHandleBinding,
|
@@ -55,42 +62,8 @@ class VarRefCollector(ast.NodeVisitor):
|
|
55
62
|
)
|
56
63
|
return res
|
57
64
|
|
58
|
-
def visit_Subscript(
|
59
|
-
self, node
|
60
|
-
) -> Union[SubscriptHandleBinding, SlicedHandleBinding, None]:
|
61
|
-
self.visit(node.slice)
|
62
|
-
with self.set_nested():
|
63
|
-
base_handle = self.visit(node.value)
|
64
|
-
if base_handle is None:
|
65
|
-
return None
|
66
|
-
|
67
|
-
handle: Union[SubscriptHandleBinding, SlicedHandleBinding]
|
68
|
-
if isinstance(node.slice, ast.Slice):
|
69
|
-
if not self._unevaluated and (
|
70
|
-
not isinstance(node.slice.lower, ast.Num)
|
71
|
-
or not isinstance(node.slice.upper, ast.Num)
|
72
|
-
):
|
73
|
-
raise ClassiqInternalExpansionError("Unevaluated slice bounds.")
|
74
|
-
if node.slice.lower is None or node.slice.upper is None:
|
75
|
-
raise ClassiqExpansionError(
|
76
|
-
f"{str(base_handle)!r} slice must specify both lower and upper bounds"
|
77
|
-
)
|
78
|
-
handle = SlicedHandleBinding(
|
79
|
-
base_handle=base_handle,
|
80
|
-
start=Expression(expr=ast.unparse(node.slice.lower)),
|
81
|
-
end=Expression(expr=ast.unparse(node.slice.upper)),
|
82
|
-
)
|
83
|
-
elif not self._unevaluated and not isinstance(node.slice, ast.Num):
|
84
|
-
raise ClassiqInternalExpansionError("Unevaluated slice.")
|
85
|
-
else:
|
86
|
-
handle = SubscriptHandleBinding(
|
87
|
-
base_handle=base_handle,
|
88
|
-
index=Expression(expr=ast.unparse(node.slice)),
|
89
|
-
)
|
90
|
-
|
91
|
-
if not self._is_nested:
|
92
|
-
self._var_handles[handle] = True
|
93
|
-
return handle
|
65
|
+
def visit_Subscript(self, node: ast.Subscript) -> Union[HandleBinding, None]:
|
66
|
+
return self._get_subscript_handle(node.value, node.slice)
|
94
67
|
|
95
68
|
def visit_Attribute(self, node: ast.Attribute) -> Optional[FieldHandleBinding]:
|
96
69
|
return self._get_field_handle(node.value, node.attr)
|
@@ -111,7 +84,6 @@ class VarRefCollector(ast.NodeVisitor):
|
|
111
84
|
raise ClassiqInternalExpansionError(
|
112
85
|
"Unexpected 'do_subscript' arguments"
|
113
86
|
)
|
114
|
-
self.visit(node.args[1])
|
115
87
|
return self._get_subscript_handle(node.args[0], node.args[1])
|
116
88
|
return self.generic_visit(node)
|
117
89
|
|
@@ -127,20 +99,27 @@ class VarRefCollector(ast.NodeVisitor):
|
|
127
99
|
field=field,
|
128
100
|
)
|
129
101
|
if not self._is_nested:
|
130
|
-
self.
|
102
|
+
self._add_handle(handle)
|
131
103
|
return handle
|
132
104
|
|
133
105
|
def _get_subscript_handle(
|
134
106
|
self, subject: ast.expr, subscript: ast.expr
|
135
107
|
) -> Optional[HandleBinding]:
|
108
|
+
with self.set_in_subscript():
|
109
|
+
self.visit(subscript)
|
136
110
|
with self.set_nested():
|
137
111
|
base_handle = self.visit(subject)
|
138
112
|
if base_handle is None:
|
139
113
|
return None
|
140
114
|
handle: HandleBinding
|
141
115
|
if isinstance(subscript, ast.Slice):
|
116
|
+
if not self._unevaluated and (
|
117
|
+
not isinstance(subscript.lower, ast.Num)
|
118
|
+
or not isinstance(subscript.upper, ast.Num)
|
119
|
+
):
|
120
|
+
raise ClassiqInternalExpansionError("Unevaluated slice bounds")
|
142
121
|
if subscript.lower is None or subscript.upper is None:
|
143
|
-
raise
|
122
|
+
raise ClassiqInternalExpansionError(
|
144
123
|
f"{str(base_handle)!r} slice must specify both lower and upper bounds"
|
145
124
|
)
|
146
125
|
handle = SlicedHandleBinding(
|
@@ -148,13 +127,15 @@ class VarRefCollector(ast.NodeVisitor):
|
|
148
127
|
start=Expression(expr=ast.unparse(subscript.lower)),
|
149
128
|
end=Expression(expr=ast.unparse(subscript.upper)),
|
150
129
|
)
|
130
|
+
elif not self._unevaluated and not isinstance(subscript, ast.Num):
|
131
|
+
raise ClassiqInternalExpansionError("Unevaluated subscript")
|
151
132
|
else:
|
152
133
|
handle = SubscriptHandleBinding(
|
153
134
|
base_handle=base_handle,
|
154
135
|
index=Expression(expr=ast.unparse(subscript)),
|
155
136
|
)
|
156
137
|
if not self._is_nested:
|
157
|
-
self.
|
138
|
+
self._add_handle(handle)
|
158
139
|
return handle
|
159
140
|
|
160
141
|
def visit_Name(self, node: ast.Name) -> Optional[HandleBinding]:
|
@@ -164,16 +145,31 @@ class VarRefCollector(ast.NodeVisitor):
|
|
164
145
|
return None
|
165
146
|
handle = HandleBinding(name=node.id)
|
166
147
|
if not self._is_nested:
|
167
|
-
self.
|
148
|
+
self._add_handle(handle)
|
168
149
|
return handle
|
169
150
|
|
170
151
|
@contextmanager
|
171
|
-
def set_nested(self) -> Iterator[None]:
|
152
|
+
def set_nested(self, val: bool = True) -> Iterator[None]:
|
172
153
|
previous_is_nested = self._is_nested
|
173
|
-
self._is_nested =
|
154
|
+
self._is_nested = val
|
174
155
|
yield
|
175
156
|
self._is_nested = previous_is_nested
|
176
157
|
|
158
|
+
@contextmanager
|
159
|
+
def set_in_subscript(self) -> Iterator[None]:
|
160
|
+
previous_in_subscript = self._in_subscript
|
161
|
+
self._in_subscript = True
|
162
|
+
with self.set_nested(False):
|
163
|
+
yield
|
164
|
+
self._in_subscript = previous_in_subscript
|
165
|
+
|
166
|
+
def _add_handle(self, handle: HandleBinding) -> None:
|
167
|
+
if handle not in self._var_handles:
|
168
|
+
self._var_handles[handle] = self._in_subscript
|
169
|
+
return
|
170
|
+
if self._in_subscript and not self._var_handles[handle]:
|
171
|
+
self._var_handles[handle] = True
|
172
|
+
|
177
173
|
|
178
174
|
class VarRefTransformer(ast.NodeTransformer):
|
179
175
|
def __init__(self, var_mapping: dict[str, str]) -> None:
|
@@ -42,14 +42,14 @@ def linear_pauli_rotations(
|
|
42
42
|
Corresponds to the braket notation:
|
43
43
|
|
44
44
|
$$
|
45
|
-
\\left|x
|
46
|
-
_{m}
|
47
|
-
_{n}\\prod_{k=1}^{m}\\left(\\cos\\left(
|
48
|
-
i\\sin\\left(
|
45
|
+
\\left|x\\right\\rangle _{n}\\left|q\\right\\rangle
|
46
|
+
_{m}\\rightarrow\\left|x\\right\\rangle
|
47
|
+
_{n}\\prod_{k=1}^{m}\\left(\\cos\\left(\\frac{a_{k}}{2}x+\\frac{b_{k}}{2}\\right)-
|
48
|
+
i\\sin\\left(\\frac{a_{k}}{2}x+\\frac{b_{k}}{2}\\right)P_{k}\\right)\\left|q_{k}\\right\\rangle
|
49
49
|
$$
|
50
50
|
|
51
|
-
where $\\left|x
|
52
|
-
$\\left|q
|
51
|
+
where $\\left|x\\right\\rangle$ is the control register,
|
52
|
+
$\\left|q\\right\\rangle$ is the target register, each $P_{k}$ is one of
|
53
53
|
the three Pauli matrices $X$, $Y$, or $Z$, and $a_{k}$, $b_{k}$ are
|
54
54
|
the user given slopes and offsets, respectively.
|
55
55
|
|
@@ -353,7 +353,7 @@ def _load_phases(
|
|
353
353
|
@qfunc
|
354
354
|
def inplace_prepare_complex_amplitudes(
|
355
355
|
magnitudes: CArray[CReal],
|
356
|
-
phases:
|
356
|
+
phases: list[float],
|
357
357
|
target: QArray[QBit, Literal["log(get_field(magnitudes, 'len'), 2)"]],
|
358
358
|
) -> None:
|
359
359
|
"""
|
@@ -32,7 +32,7 @@ def apply_to_all(
|
|
32
32
|
|
33
33
|
@qfunc
|
34
34
|
def hadamard_transform(target: QArray[QBit]) -> None:
|
35
|
-
"""
|
35
|
+
r"""
|
36
36
|
[Qmod Classiq-library function]
|
37
37
|
|
38
38
|
Applies Hadamard transform to the target qubits.
|
@@ -40,7 +40,7 @@ def hadamard_transform(target: QArray[QBit]) -> None:
|
|
40
40
|
Corresponds to the braket notation:
|
41
41
|
|
42
42
|
$$
|
43
|
-
H^{
|
43
|
+
H^{\otimes n} |x\rangle = \frac{1}{\sqrt{2^n}} \sum_{y=0}^{2^n - 1} (-1)^{x \\cdot y} |y\rangle
|
44
44
|
$$
|
45
45
|
|
46
46
|
Args:
|
@@ -1,8 +1,8 @@
|
|
1
1
|
from typing import Final, Optional, Union
|
2
2
|
|
3
|
+
from classiq.interface.applications.iqae.iqae_result import IQAEResult
|
3
4
|
from classiq.interface.exceptions import ClassiqError
|
4
5
|
from classiq.interface.executor.execution_preferences import QaeWithQpeEstimationMethod
|
5
|
-
from classiq.interface.executor.iqae_result import IQAEResult
|
6
6
|
from classiq.interface.executor.result import (
|
7
7
|
EstimationResult,
|
8
8
|
EstimationResults,
|
@@ -65,8 +65,8 @@ class _PythonClassicalType(PythonClassicalType):
|
|
65
65
|
return
|
66
66
|
|
67
67
|
enum_decl = declaration_from_enum(py_type)
|
68
|
+
check_duplicate_types([enum_decl, *self.qmodule.user_types()])
|
68
69
|
self.qmodule.enum_decls[py_type.__name__] = enum_decl
|
69
|
-
check_duplicate_types([enum_decl])
|
70
70
|
|
71
71
|
def register_struct(self, py_type: type) -> TypeName:
|
72
72
|
classical_type = super().register_struct(py_type)
|
@@ -74,7 +74,9 @@ class _PythonClassicalType(PythonClassicalType):
|
|
74
74
|
return classical_type
|
75
75
|
all_decls = BUILTIN_STRUCT_DECLARATIONS | self.qmodule.type_decls
|
76
76
|
if py_type.__name__ in all_decls:
|
77
|
-
classical_type.set_classical_struct_decl(
|
77
|
+
classical_type.set_classical_struct_decl(
|
78
|
+
all_decls[py_type.__name__].model_copy()
|
79
|
+
)
|
78
80
|
return classical_type
|
79
81
|
|
80
82
|
struct_decl = StructDeclaration(
|
@@ -83,8 +85,8 @@ class _PythonClassicalType(PythonClassicalType):
|
|
83
85
|
f.name: self.convert(f.type) for f in dataclasses.fields(py_type)
|
84
86
|
},
|
85
87
|
)
|
88
|
+
check_duplicate_types([struct_decl, *self.qmodule.user_types()])
|
86
89
|
self.qmodule.type_decls[py_type.__name__] = struct_decl
|
87
|
-
check_duplicate_types([struct_decl])
|
88
90
|
validate_cstruct(struct_decl)
|
89
91
|
|
90
92
|
classical_type.set_classical_struct_decl(struct_decl)
|
@@ -1,5 +1,6 @@
|
|
1
1
|
from collections import defaultdict
|
2
|
-
from
|
2
|
+
from collections.abc import Sequence
|
3
|
+
from typing import TYPE_CHECKING, Union
|
3
4
|
|
4
5
|
from classiq.interface.generator.constant import Constant
|
5
6
|
from classiq.interface.generator.types.compilation_metadata import CompilationMetadata
|
@@ -10,6 +11,9 @@ from classiq.interface.model.native_function_definition import (
|
|
10
11
|
NativeFunctionDefinition,
|
11
12
|
)
|
12
13
|
|
14
|
+
from classiq.qmod.builtins.enums import BUILTIN_ENUM_DECLARATIONS
|
15
|
+
from classiq.qmod.builtins.structs import BUILTIN_STRUCT_DECLARATIONS
|
16
|
+
|
13
17
|
if TYPE_CHECKING:
|
14
18
|
from classiq.qmod.quantum_function import GenerativeQFunc
|
15
19
|
|
@@ -34,6 +38,22 @@ class ModelStateContainer:
|
|
34
38
|
self.generative_functions = {}
|
35
39
|
self.function_dependencies = defaultdict(list)
|
36
40
|
|
41
|
+
def user_types(
|
42
|
+
self,
|
43
|
+
) -> Sequence[Union[EnumDeclaration, StructDeclaration, QStructDeclaration]]:
|
44
|
+
type_decls = [
|
45
|
+
t
|
46
|
+
for t in self.type_decls.values()
|
47
|
+
if t.name not in BUILTIN_STRUCT_DECLARATIONS
|
48
|
+
]
|
49
|
+
enum_decls = [
|
50
|
+
t
|
51
|
+
for t in self.enum_decls.values()
|
52
|
+
if t.name not in BUILTIN_ENUM_DECLARATIONS
|
53
|
+
]
|
54
|
+
qstruct_decls = list(self.qstruct_decls.values())
|
55
|
+
return [*type_decls, *enum_decls, *qstruct_decls]
|
56
|
+
|
37
57
|
|
38
58
|
QMODULE = ModelStateContainer()
|
39
59
|
QMODULE.reset()
|
@@ -1,12 +1,14 @@
|
|
1
1
|
from collections.abc import Mapping
|
2
2
|
from typing import Optional, Union
|
3
3
|
|
4
|
+
from classiq.interface.exceptions import ClassiqInternalError
|
4
5
|
from classiq.interface.generator.constant import Constant
|
5
6
|
from classiq.interface.generator.expressions.expression import Expression
|
6
7
|
from classiq.interface.generator.functions.classical_type import (
|
7
8
|
Bool,
|
8
9
|
ClassicalArray,
|
9
10
|
ClassicalList,
|
11
|
+
ClassicalTuple,
|
10
12
|
Integer,
|
11
13
|
Real,
|
12
14
|
)
|
@@ -250,6 +252,12 @@ class DSLPrettyPrinter(ModelVisitor):
|
|
250
252
|
def visit_ClassicalArray(self, ctarray: ClassicalArray) -> str:
|
251
253
|
return f"{self.visit(ctarray.element_type)}[{ctarray.size}]"
|
252
254
|
|
255
|
+
def visit_ClassicalTuple(self, classical_tuple: ClassicalTuple) -> str:
|
256
|
+
raw_type = classical_tuple.get_raw_type()
|
257
|
+
if isinstance(raw_type, ClassicalTuple):
|
258
|
+
raise ClassiqInternalError("Empty tuple pretty-print not supported")
|
259
|
+
return self.visit(raw_type)
|
260
|
+
|
253
261
|
def visit_TypeName(self, type_: TypeName) -> str:
|
254
262
|
return type_.name
|
255
263
|
|