classiq 0.76.0__py3-none-any.whl → 0.78.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/chemistry/chemistry_model_constructor.py +7 -6
- classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +9 -1
- classiq/applications/combinatorial_optimization/combinatorial_problem.py +4 -3
- 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/expressions/proxies/classical/any_classical_value.py +9 -3
- classiq/interface/generator/expressions/proxies/quantum/qmod_qarray_proxy.py +6 -15
- classiq/interface/generator/expressions/proxies/quantum/qmod_qscalar_proxy.py +14 -5
- classiq/interface/generator/expressions/proxies/quantum/qmod_sized_proxy.py +5 -3
- classiq/interface/generator/generated_circuit_data.py +18 -7
- classiq/interface/ide/visual_model.py +2 -0
- classiq/interface/model/handle_binding.py +8 -0
- classiq/interface/model/model.py +3 -6
- classiq/interface/model/quantum_function_call.py +31 -1
- classiq/interface/model/quantum_statement.py +14 -1
- classiq/interface/source_reference.py +7 -2
- classiq/model_expansions/capturing/captured_vars.py +16 -6
- classiq/model_expansions/closure.py +1 -58
- classiq/model_expansions/evaluators/arg_type_match.py +2 -2
- classiq/model_expansions/evaluators/argument_types.py +4 -5
- classiq/model_expansions/evaluators/classical_expression.py +9 -9
- classiq/model_expansions/evaluators/parameter_types.py +19 -11
- classiq/model_expansions/expression_evaluator.py +20 -11
- classiq/model_expansions/generative_functions.py +1 -1
- classiq/model_expansions/interpreters/base_interpreter.py +27 -15
- classiq/model_expansions/interpreters/frontend_generative_interpreter.py +0 -16
- classiq/model_expansions/interpreters/generative_interpreter.py +4 -4
- classiq/model_expansions/quantum_operations/allocate.py +2 -2
- classiq/model_expansions/quantum_operations/assignment_result_processor.py +3 -1
- classiq/model_expansions/quantum_operations/call_emitter.py +91 -42
- classiq/model_expansions/quantum_operations/emitter.py +7 -7
- classiq/model_expansions/quantum_operations/function_calls_cache.py +84 -0
- classiq/model_expansions/scope.py +73 -13
- classiq/model_expansions/transformers/model_renamer.py +2 -2
- 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 +4 -15
- classiq/open_library/functions/lookup_table.py +1 -1
- classiq/open_library/functions/state_preparation.py +1 -1
- classiq/qmod/builtins/classical_execution_primitives.py +1 -1
- classiq/qmod/create_model_function.py +21 -3
- classiq/qmod/global_declarative_switch.py +19 -0
- classiq/qmod/native/pretty_printer.py +4 -0
- classiq/qmod/pretty_print/pretty_printer.py +4 -0
- classiq/qmod/qfunc.py +31 -23
- classiq/qmod/qmod_variable.py +7 -4
- classiq/qmod/quantum_expandable.py +29 -1
- classiq/qmod/quantum_function.py +26 -19
- classiq/qmod/utilities.py +4 -0
- classiq/qmod/write_qmod.py +36 -10
- classiq/synthesis.py +7 -6
- {classiq-0.76.0.dist-info → classiq-0.78.0.dist-info}/METADATA +1 -1
- {classiq-0.76.0.dist-info → classiq-0.78.0.dist-info}/RECORD +62 -55
- classiq/interface/executor/iqae_result.py +0 -17
- {classiq-0.76.0.dist-info → classiq-0.78.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,84 @@
|
|
1
|
+
import json
|
2
|
+
from typing import Any
|
3
|
+
|
4
|
+
from classiq.interface.exceptions import ClassiqInternalExpansionError
|
5
|
+
from classiq.interface.generator.expressions.proxies.classical.classical_proxy import (
|
6
|
+
ClassicalProxy,
|
7
|
+
)
|
8
|
+
from classiq.interface.generator.expressions.proxies.classical.classical_struct_proxy import (
|
9
|
+
ClassicalStructProxy,
|
10
|
+
)
|
11
|
+
from classiq.interface.generator.expressions.proxies.classical.utils import (
|
12
|
+
get_proxy_type,
|
13
|
+
)
|
14
|
+
from classiq.interface.generator.functions.port_declaration import (
|
15
|
+
PortDeclarationDirection,
|
16
|
+
)
|
17
|
+
from classiq.interface.model.port_declaration import PortDeclaration
|
18
|
+
from classiq.interface.model.quantum_function_declaration import (
|
19
|
+
NamedParamsQuantumFunctionDeclaration,
|
20
|
+
)
|
21
|
+
|
22
|
+
from classiq.model_expansions.closure import FunctionClosure
|
23
|
+
from classiq.model_expansions.scope import (
|
24
|
+
Evaluated,
|
25
|
+
QuantumSymbol,
|
26
|
+
evaluated_to_str as evaluated_classical_param_to_str,
|
27
|
+
)
|
28
|
+
|
29
|
+
|
30
|
+
def get_func_call_cache_key(
|
31
|
+
decl: NamedParamsQuantumFunctionDeclaration,
|
32
|
+
args: list[Evaluated],
|
33
|
+
) -> str:
|
34
|
+
if len(decl.positional_arg_declarations) != len(args):
|
35
|
+
raise ClassiqInternalExpansionError(
|
36
|
+
"Mismatch between number of args to number of arg declarations"
|
37
|
+
)
|
38
|
+
|
39
|
+
# output arguments cannot affect the morphization of the function, as their
|
40
|
+
# attributes are defined locally by the function, according to other arguments
|
41
|
+
non_outputs_args = [
|
42
|
+
arg
|
43
|
+
for arg_decl, arg in zip(decl.positional_arg_declarations, args)
|
44
|
+
if not (
|
45
|
+
isinstance(arg_decl, PortDeclaration)
|
46
|
+
and arg_decl.direction is PortDeclarationDirection.Output
|
47
|
+
)
|
48
|
+
]
|
49
|
+
return f"{decl.name}__{_evaluated_args_to_str(non_outputs_args)}"
|
50
|
+
|
51
|
+
|
52
|
+
def _evaluated_args_to_str(evaluated_args: list[Evaluated]) -> str:
|
53
|
+
args_signature = [
|
54
|
+
_evaluated_arg_to_str(eval_arg.value) for eval_arg in evaluated_args
|
55
|
+
]
|
56
|
+
return json.dumps(args_signature)
|
57
|
+
|
58
|
+
|
59
|
+
def _evaluated_arg_to_str(arg: Any) -> str:
|
60
|
+
if isinstance(arg, str):
|
61
|
+
return arg
|
62
|
+
if isinstance(arg, QuantumSymbol):
|
63
|
+
return _evaluated_quantum_symbol_to_str(arg)
|
64
|
+
if isinstance(arg, FunctionClosure):
|
65
|
+
return _evaluated_one_operand_to_str(arg)
|
66
|
+
if isinstance(arg, list) and arg and isinstance(arg[0], FunctionClosure):
|
67
|
+
return _evaluated_operands_list_to_str(arg)
|
68
|
+
if isinstance(arg, ClassicalProxy):
|
69
|
+
if isinstance(arg, ClassicalStructProxy):
|
70
|
+
return repr(arg.struct_declaration)
|
71
|
+
return repr(get_proxy_type(arg))
|
72
|
+
return evaluated_classical_param_to_str(arg)
|
73
|
+
|
74
|
+
|
75
|
+
def _evaluated_quantum_symbol_to_str(port: QuantumSymbol) -> str:
|
76
|
+
return port.quantum_type.model_dump_json(exclude_none=True, exclude={"name"})
|
77
|
+
|
78
|
+
|
79
|
+
def _evaluated_one_operand_to_str(operand: FunctionClosure) -> str:
|
80
|
+
return operand.name
|
81
|
+
|
82
|
+
|
83
|
+
def _evaluated_operands_list_to_str(arg: list[FunctionClosure]) -> str:
|
84
|
+
return json.dumps([_evaluated_one_operand_to_str(ope) for ope in arg])
|
@@ -3,7 +3,9 @@ from collections import UserDict
|
|
3
3
|
from collections.abc import Iterator
|
4
4
|
from dataclasses import dataclass
|
5
5
|
from functools import singledispatch
|
6
|
-
from typing import TYPE_CHECKING, Any, Optional, TypeVar, Union
|
6
|
+
from typing import TYPE_CHECKING, Any, NoReturn, Optional, TypeVar, Union
|
7
|
+
|
8
|
+
import sympy
|
7
9
|
|
8
10
|
from classiq.interface.exceptions import (
|
9
11
|
ClassiqExpansionError,
|
@@ -22,7 +24,9 @@ from classiq.interface.generator.expressions.proxies.classical.qmod_struct_insta
|
|
22
24
|
from classiq.interface.generator.functions.type_name import TypeName
|
23
25
|
from classiq.interface.model.handle_binding import (
|
24
26
|
FieldHandleBinding,
|
27
|
+
GeneralHandle,
|
25
28
|
HandleBinding,
|
29
|
+
HandlesList,
|
26
30
|
SlicedHandleBinding,
|
27
31
|
SubscriptHandleBinding,
|
28
32
|
)
|
@@ -32,6 +36,8 @@ from classiq.interface.model.quantum_type import (
|
|
32
36
|
QuantumType,
|
33
37
|
)
|
34
38
|
|
39
|
+
from classiq.model_expansions.utils.text_utils import readable_list, s
|
40
|
+
|
35
41
|
if TYPE_CHECKING:
|
36
42
|
from classiq.model_expansions.closure import FunctionClosure
|
37
43
|
|
@@ -39,10 +45,17 @@ T = TypeVar("T")
|
|
39
45
|
|
40
46
|
|
41
47
|
@dataclass(frozen=True)
|
42
|
-
class
|
43
|
-
handle: HandleBinding
|
48
|
+
class QuantumVariable:
|
44
49
|
quantum_type: QuantumType
|
45
50
|
|
51
|
+
def emit(self) -> GeneralHandle:
|
52
|
+
raise NotImplementedError
|
53
|
+
|
54
|
+
|
55
|
+
@dataclass(frozen=True)
|
56
|
+
class QuantumSymbol(QuantumVariable):
|
57
|
+
handle: HandleBinding
|
58
|
+
|
46
59
|
@property
|
47
60
|
def is_subscript(self) -> bool:
|
48
61
|
return isinstance(self.handle, (SubscriptHandleBinding, SlicedHandleBinding))
|
@@ -140,6 +153,47 @@ class QuantumSymbol:
|
|
140
153
|
for field_name, field_type in quantum_type.fields.items()
|
141
154
|
}
|
142
155
|
|
156
|
+
def __str__(self) -> str:
|
157
|
+
return str(self.handle)
|
158
|
+
|
159
|
+
|
160
|
+
@dataclass(frozen=True)
|
161
|
+
class QuantumSymbolList(QuantumVariable):
|
162
|
+
handles: list[HandleBinding]
|
163
|
+
|
164
|
+
@staticmethod
|
165
|
+
def from_symbols(
|
166
|
+
symbols: list[Union[QuantumSymbol, "QuantumSymbolList"]],
|
167
|
+
) -> "QuantumSymbolList":
|
168
|
+
handles = list(
|
169
|
+
itertools.chain.from_iterable(
|
170
|
+
(
|
171
|
+
symbol.handles
|
172
|
+
if isinstance(symbol, QuantumSymbolList)
|
173
|
+
else [symbol.handle]
|
174
|
+
)
|
175
|
+
for symbol in symbols
|
176
|
+
)
|
177
|
+
)
|
178
|
+
if len(handles) == 0:
|
179
|
+
raise ClassiqExpansionError("Empty concatenation expression")
|
180
|
+
length: Optional[Expression]
|
181
|
+
if any(not symbol.quantum_type.has_size_in_bits for symbol in symbols):
|
182
|
+
length = None
|
183
|
+
else:
|
184
|
+
length = Expression(
|
185
|
+
expr=str(sum(symbol.quantum_type.size_in_bits for symbol in symbols))
|
186
|
+
)
|
187
|
+
return QuantumSymbolList(
|
188
|
+
handles=handles, quantum_type=QuantumBitvector(length=length)
|
189
|
+
)
|
190
|
+
|
191
|
+
def emit(self) -> HandlesList:
|
192
|
+
return HandlesList(handles=self.handles)
|
193
|
+
|
194
|
+
def __str__(self) -> str:
|
195
|
+
return str(self.handles)
|
196
|
+
|
143
197
|
|
144
198
|
@singledispatch
|
145
199
|
def evaluated_to_str(value: Any) -> str:
|
@@ -156,34 +210,40 @@ def _evaluated_to_str_struct_literal(value: QmodStructInstance) -> str:
|
|
156
210
|
return f"struct_literal({value.struct_declaration.name}, {', '.join(f'{k}={evaluated_to_str(v)}' for k, v in value.fields.items())})"
|
157
211
|
|
158
212
|
|
213
|
+
def _raise_type_error(val: Any, t: type, location_hint: Optional[str]) -> NoReturn:
|
214
|
+
if isinstance(val, sympy.Basic) and len(val.free_symbols) > 0:
|
215
|
+
symbolic_vars = list(val.free_symbols)
|
216
|
+
suffix = f" {location_hint}" if location_hint is not None else ""
|
217
|
+
raise ClassiqExpansionError(
|
218
|
+
f"Cannot use execution parameter{s(symbolic_vars)} {readable_list(symbolic_vars, quote=True)} in a compile-time context{suffix}"
|
219
|
+
)
|
220
|
+
raise ClassiqExpansionError(f"Invalid access to expression {val!r} as {t}")
|
221
|
+
|
222
|
+
|
159
223
|
@dataclass(frozen=True)
|
160
224
|
class Evaluated: # FIXME: Merge with EvaluatedExpression if possible
|
161
225
|
value: Any
|
162
226
|
defining_function: Optional["FunctionClosure"] = None
|
163
227
|
|
164
|
-
def as_type(self, t: type[T]) -> T:
|
228
|
+
def as_type(self, t: type[T], location_hint: Optional[str] = None) -> T:
|
165
229
|
if t is int:
|
166
|
-
return self._as_int() # type: ignore[return-value]
|
230
|
+
return self._as_int(location_hint) # type: ignore[return-value]
|
167
231
|
|
168
232
|
if not isinstance(self.value, t):
|
169
|
-
|
170
|
-
f"Invalid access to expression {self.value!r} as {t}"
|
171
|
-
)
|
233
|
+
_raise_type_error(self.value, t, location_hint)
|
172
234
|
|
173
235
|
return self.value
|
174
236
|
|
175
|
-
def _as_int(self) -> int:
|
237
|
+
def _as_int(self, location_hint: Optional[str]) -> int:
|
176
238
|
if not isinstance(self.value, (int, float)):
|
177
|
-
|
178
|
-
f"Invalid access to expression {self.value!r} as {int}"
|
179
|
-
)
|
239
|
+
_raise_type_error(self.value, int, location_hint)
|
180
240
|
|
181
241
|
return int(self.value)
|
182
242
|
|
183
243
|
def emit(self) -> ArgValue:
|
184
244
|
from classiq.model_expansions.closure import FunctionClosure
|
185
245
|
|
186
|
-
if isinstance(self.value, (
|
246
|
+
if isinstance(self.value, (QuantumVariable, FunctionClosure)):
|
187
247
|
return self.value.emit()
|
188
248
|
if isinstance(self.value, list) and all(
|
189
249
|
isinstance(item, FunctionClosure) for item in self.value
|
@@ -33,7 +33,7 @@ def _handle_contains_handle(handle: HandleBinding, other_handle: HandleBinding)
|
|
33
33
|
return 0
|
34
34
|
|
35
35
|
|
36
|
-
class
|
36
|
+
class ExprNormalizer(ast.NodeTransformer):
|
37
37
|
def visit_Call(self, node: ast.Call) -> ast.AST:
|
38
38
|
if not isinstance(node.func, ast.Name):
|
39
39
|
return self.generic_visit(node)
|
@@ -74,7 +74,7 @@ SymbolRenaming = Mapping[HandleBinding, Sequence[HandleRenaming]]
|
|
74
74
|
def _rewrite_expression(
|
75
75
|
symbol_mapping: SymbolRenaming, expression: Expression
|
76
76
|
) -> Expression:
|
77
|
-
normalized_expr =
|
77
|
+
normalized_expr = ExprNormalizer().visit(ast.parse(expression.expr))
|
78
78
|
vrc = VarRefCollector(
|
79
79
|
ignore_duplicated_handles=True, ignore_sympy_symbols=True, unevaluated=True
|
80
80
|
)
|
@@ -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]}"
|
@@ -62,9 +62,9 @@ class SymbolicParamInference(ModelVisitor):
|
|
62
62
|
def __init__(
|
63
63
|
self,
|
64
64
|
functions: list[NativeFunctionDefinition],
|
65
|
-
additional_signatures:
|
66
|
-
list[NamedParamsQuantumFunctionDeclaration]
|
67
|
-
|
65
|
+
additional_signatures: Optional[
|
66
|
+
list[NamedParamsQuantumFunctionDeclaration]
|
67
|
+
] = None,
|
68
68
|
) -> None:
|
69
69
|
self._functions = nameables_to_dict(functions)
|
70
70
|
self._additional_signatures = (
|
@@ -73,7 +73,6 @@ class SymbolicParamInference(ModelVisitor):
|
|
73
73
|
else nameables_to_dict(additional_signatures)
|
74
74
|
)
|
75
75
|
self._inferred_funcs: set[str] = set()
|
76
|
-
self._call_stack: list[str] = []
|
77
76
|
self._scope: Mapping[str, ClassicalType] = {}
|
78
77
|
self._scope_operands: dict[str, QuantumOperandDeclaration] = {}
|
79
78
|
|
@@ -81,9 +80,6 @@ class SymbolicParamInference(ModelVisitor):
|
|
81
80
|
for func in self._functions.values():
|
82
81
|
self._infer_func_params(func)
|
83
82
|
|
84
|
-
def _is_recursive_call(self, func: str) -> bool:
|
85
|
-
return func in self._call_stack
|
86
|
-
|
87
83
|
@contextmanager
|
88
84
|
def function_context(
|
89
85
|
self,
|
@@ -91,8 +87,6 @@ class SymbolicParamInference(ModelVisitor):
|
|
91
87
|
scope: Mapping[str, ClassicalType],
|
92
88
|
scope_operands: dict[str, QuantumOperandDeclaration],
|
93
89
|
) -> Iterator[None]:
|
94
|
-
if func_name is not None:
|
95
|
-
self._call_stack.append(func_name)
|
96
90
|
prev_scope = self._scope
|
97
91
|
self._scope = scope
|
98
92
|
prev_scope_ops = self._scope_operands
|
@@ -100,12 +94,11 @@ class SymbolicParamInference(ModelVisitor):
|
|
100
94
|
yield
|
101
95
|
self._scope = prev_scope
|
102
96
|
self._scope_operands = prev_scope_ops
|
103
|
-
if func_name is not None:
|
104
|
-
self._call_stack.pop()
|
105
97
|
|
106
98
|
def _infer_func_params(self, func: NativeFunctionDefinition) -> None:
|
107
99
|
if func.name in self._inferred_funcs:
|
108
100
|
return
|
101
|
+
self._inferred_funcs.add(func.name)
|
109
102
|
scope = {param.name: param.classical_type for param in func.param_decls}
|
110
103
|
scope_operands = func.operand_declarations_dict
|
111
104
|
with self.function_context(func.name, scope, scope_operands):
|
@@ -114,7 +107,6 @@ class SymbolicParamInference(ModelVisitor):
|
|
114
107
|
self._process_compile_time_expression(expr.expr)
|
115
108
|
self._set_enums_generative(func)
|
116
109
|
self.visit(func.body)
|
117
|
-
self._inferred_funcs.add(func.name)
|
118
110
|
|
119
111
|
def _set_enums_generative(self, decl: AnonQuantumFunctionDeclaration) -> None:
|
120
112
|
for param in decl.positional_arg_declarations:
|
@@ -144,9 +136,6 @@ class SymbolicParamInference(ModelVisitor):
|
|
144
136
|
|
145
137
|
def visit_QuantumFunctionCall(self, call: QuantumFunctionCall) -> None:
|
146
138
|
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
139
|
params = self._get_params(call)
|
151
140
|
for param, arg in zip_longest(params, call.positional_args):
|
152
141
|
if (
|
@@ -39,7 +39,7 @@ def span_lookup_table(func: RealFunction, *targets: QNum) -> QNum:
|
|
39
39
|
The quantum result of applying func to targets
|
40
40
|
|
41
41
|
Notes:
|
42
|
-
Must be called inside a generative function (`@qfunc
|
42
|
+
Must be called inside a generative function (`@qfunc`)
|
43
43
|
"""
|
44
44
|
if len(targets) == 0:
|
45
45
|
raise ClassiqValueError("No targets specified")
|
@@ -334,7 +334,7 @@ def _classical_hadamard_transform(arr: list[float]) -> np.ndarray:
|
|
334
334
|
return 1 / np.sqrt(len(arr)) * np.array(sympy.fwht(np.array(arr)))
|
335
335
|
|
336
336
|
|
337
|
-
@qfunc
|
337
|
+
@qfunc
|
338
338
|
def _load_phases(
|
339
339
|
phases: list[float],
|
340
340
|
target: QArray[QBit, Literal["log(get_field(phases, 'len'), 2)"]],
|
@@ -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,
|
@@ -1,4 +1,4 @@
|
|
1
|
-
from typing import Optional, Union
|
1
|
+
from typing import Optional, Union, cast
|
2
2
|
|
3
3
|
from classiq.interface.exceptions import ClassiqError
|
4
4
|
from classiq.interface.executor.execution_preferences import ExecutionPreferences
|
@@ -7,10 +7,26 @@ from classiq.interface.generator.model.preferences.preferences import Preference
|
|
7
7
|
from classiq.interface.model.model import MAIN_FUNCTION_NAME, SerializedModel
|
8
8
|
|
9
9
|
from classiq.qmod.classical_function import CFunc
|
10
|
-
from classiq.qmod.quantum_function import GenerativeQFunc, QFunc
|
10
|
+
from classiq.qmod.quantum_function import BaseQFunc, GenerativeQFunc, QFunc
|
11
11
|
from classiq.qmod.write_qmod import write_qmod
|
12
12
|
|
13
13
|
|
14
|
+
class _EntryPointWrapper(str):
|
15
|
+
entry_point: BaseQFunc
|
16
|
+
|
17
|
+
|
18
|
+
def add_entry_point(
|
19
|
+
new_model: SerializedModel, old_model: SerializedModel
|
20
|
+
) -> SerializedModel:
|
21
|
+
if not hasattr(old_model, "entry_point"):
|
22
|
+
return new_model
|
23
|
+
new_model_with_entry_point = _EntryPointWrapper(new_model)
|
24
|
+
new_model_with_entry_point.entry_point = cast(
|
25
|
+
_EntryPointWrapper, old_model
|
26
|
+
).entry_point
|
27
|
+
return cast(SerializedModel, new_model_with_entry_point)
|
28
|
+
|
29
|
+
|
14
30
|
def create_model(
|
15
31
|
entry_point: Union[QFunc, GenerativeQFunc],
|
16
32
|
constraints: Optional[Constraints] = None,
|
@@ -48,7 +64,9 @@ def create_model(
|
|
48
64
|
preferences,
|
49
65
|
classical_execution_function,
|
50
66
|
)
|
51
|
-
|
67
|
+
serialized_model = _EntryPointWrapper(model.get_model())
|
68
|
+
serialized_model.entry_point = entry_point
|
69
|
+
result = cast(SerializedModel, serialized_model)
|
52
70
|
|
53
71
|
if out_file is not None:
|
54
72
|
write_qmod(result, out_file)
|
@@ -0,0 +1,19 @@
|
|
1
|
+
from collections.abc import Iterator
|
2
|
+
from contextlib import contextmanager
|
3
|
+
|
4
|
+
_DECLARATIVE_SWITCH = False
|
5
|
+
|
6
|
+
|
7
|
+
def get_global_declarative_switch() -> bool:
|
8
|
+
return _DECLARATIVE_SWITCH
|
9
|
+
|
10
|
+
|
11
|
+
@contextmanager
|
12
|
+
def set_global_declarative_switch() -> Iterator[None]:
|
13
|
+
global _DECLARATIVE_SWITCH
|
14
|
+
previous = _DECLARATIVE_SWITCH
|
15
|
+
_DECLARATIVE_SWITCH = True
|
16
|
+
try:
|
17
|
+
yield
|
18
|
+
finally:
|
19
|
+
_DECLARATIVE_SWITCH = previous
|
@@ -35,6 +35,7 @@ from classiq.interface.model.control import Control
|
|
35
35
|
from classiq.interface.model.handle_binding import (
|
36
36
|
FieldHandleBinding,
|
37
37
|
HandleBinding,
|
38
|
+
HandlesList,
|
38
39
|
SlicedHandleBinding,
|
39
40
|
SubscriptHandleBinding,
|
40
41
|
)
|
@@ -395,6 +396,9 @@ class DSLPrettyPrinter(ModelVisitor):
|
|
395
396
|
def visit_FieldHandleBinding(self, var_ref: FieldHandleBinding) -> str:
|
396
397
|
return f"{self.visit(var_ref.base_handle)}.{self.visit(var_ref.field)}"
|
397
398
|
|
399
|
+
def visit_HandlesList(self, handles: HandlesList) -> str:
|
400
|
+
return f"{{{', '.join(map(self.visit, handles.handles))}}}"
|
401
|
+
|
398
402
|
def visit_ArithmeticOperation(self, arith_op: ArithmeticOperation) -> str:
|
399
403
|
if arith_op.operation_kind == ArithmeticOperationKind.Assignment:
|
400
404
|
op = "="
|
@@ -37,6 +37,7 @@ from classiq.interface.model.control import Control
|
|
37
37
|
from classiq.interface.model.handle_binding import (
|
38
38
|
FieldHandleBinding,
|
39
39
|
HandleBinding,
|
40
|
+
HandlesList,
|
40
41
|
SlicedHandleBinding,
|
41
42
|
SubscriptHandleBinding,
|
42
43
|
)
|
@@ -518,6 +519,9 @@ class PythonPrettyPrinter(ModelVisitor):
|
|
518
519
|
def visit_FieldHandleBinding(self, var_ref: FieldHandleBinding) -> str:
|
519
520
|
return f"{self.visit(var_ref.base_handle)}.{self.visit(var_ref.field)}"
|
520
521
|
|
522
|
+
def visit_HandlesList(self, handles: HandlesList) -> str:
|
523
|
+
return self.visit(handles.handles)
|
524
|
+
|
521
525
|
def visit_ArithmeticOperation(
|
522
526
|
self, arith_op: ArithmeticOperation, in_lambda: bool = False
|
523
527
|
) -> str:
|