classiq 0.75.0__py3-none-any.whl → 0.77.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 +15 -7
- classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +11 -1
- classiq/applications/combinatorial_optimization/combinatorial_problem.py +8 -7
- classiq/applications/qnn/gradients/quantum_gradient.py +3 -5
- classiq/applications/qnn/gradients/simple_quantum_gradient.py +2 -2
- classiq/applications/qnn/qlayer.py +14 -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 +16 -2
- 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 +45 -21
- classiq/interface/generator/expressions/proxies/classical/qmod_struct_instance.py +7 -0
- classiq/interface/generator/expressions/proxies/classical/utils.py +12 -11
- classiq/interface/generator/expressions/proxies/quantum/qmod_qarray_proxy.py +6 -15
- classiq/interface/generator/expressions/proxies/quantum/qmod_qscalar_proxy.py +22 -6
- classiq/interface/generator/expressions/proxies/quantum/qmod_sized_proxy.py +9 -4
- classiq/interface/generator/expressions/sympy_supported_expressions.py +1 -0
- classiq/interface/generator/functions/classical_type.py +6 -1
- classiq/interface/generator/functions/type_name.py +7 -2
- classiq/interface/generator/functions/type_qualifier.py +15 -0
- classiq/interface/generator/model/preferences/preferences.py +7 -0
- classiq/interface/generator/quantum_program.py +5 -19
- classiq/interface/helpers/backward_compatibility.py +9 -0
- classiq/interface/helpers/datastructures.py +6 -0
- classiq/interface/model/handle_binding.py +8 -0
- classiq/interface/model/model.py +3 -6
- classiq/interface/model/port_declaration.py +1 -2
- classiq/interface/model/quantum_function_call.py +31 -1
- classiq/interface/model/quantum_lambda_function.py +2 -1
- classiq/interface/model/quantum_statement.py +14 -1
- classiq/interface/server/routes.py +6 -0
- classiq/interface/source_reference.py +7 -2
- classiq/model_expansions/atomic_expression_functions_defs.py +62 -19
- classiq/model_expansions/capturing/captured_vars.py +18 -6
- classiq/model_expansions/closure.py +5 -0
- classiq/model_expansions/evaluators/arg_type_match.py +2 -2
- classiq/model_expansions/evaluators/argument_types.py +3 -3
- classiq/model_expansions/evaluators/classical_expression.py +9 -9
- classiq/model_expansions/evaluators/classical_type_inference.py +17 -6
- classiq/model_expansions/evaluators/parameter_types.py +45 -24
- classiq/model_expansions/expression_evaluator.py +21 -12
- classiq/model_expansions/function_builder.py +45 -0
- classiq/model_expansions/generative_functions.py +62 -35
- classiq/model_expansions/interpreters/base_interpreter.py +32 -7
- classiq/model_expansions/interpreters/frontend_generative_interpreter.py +9 -3
- classiq/model_expansions/interpreters/generative_interpreter.py +17 -5
- classiq/model_expansions/quantum_operations/allocate.py +8 -3
- classiq/model_expansions/quantum_operations/assignment_result_processor.py +221 -20
- 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 +35 -18
- 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 -9
- classiq/model_expansions/quantum_operations/quantum_function_call.py +4 -3
- classiq/model_expansions/scope.py +63 -10
- 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 +45 -7
- classiq/model_expansions/utils/handles_collector.py +1 -1
- classiq/model_expansions/visitors/symbolic_param_inference.py +3 -3
- classiq/model_expansions/visitors/variable_references.py +45 -9
- classiq/open_library/functions/lookup_table.py +1 -1
- classiq/open_library/functions/state_preparation.py +1 -1
- 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/create_model_function.py +21 -3
- classiq/qmod/declaration_inferrer.py +19 -7
- classiq/qmod/generative.py +9 -1
- classiq/qmod/global_declarative_switch.py +19 -0
- classiq/qmod/native/expression_to_qmod.py +4 -0
- classiq/qmod/native/pretty_printer.py +12 -3
- classiq/qmod/pretty_print/pretty_printer.py +5 -1
- classiq/qmod/python_classical_type.py +4 -5
- classiq/qmod/qfunc.py +31 -23
- classiq/qmod/qmod_constant.py +15 -7
- classiq/qmod/qmod_variable.py +7 -1
- classiq/qmod/quantum_expandable.py +29 -1
- classiq/qmod/quantum_function.py +45 -25
- 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 +26 -0
- classiq/qmod/write_qmod.py +36 -10
- classiq/synthesis.py +24 -37
- classiq/visualization.py +35 -0
- {classiq-0.75.0.dist-info → classiq-0.77.0.dist-info}/METADATA +1 -1
- {classiq-0.75.0.dist-info → classiq-0.77.0.dist-info}/RECORD +101 -96
- {classiq-0.75.0.dist-info → classiq-0.77.0.dist-info}/WHEEL +0 -0
@@ -1,5 +1,5 @@
|
|
1
1
|
from itertools import chain
|
2
|
-
from typing import TYPE_CHECKING, Generic
|
2
|
+
from typing import TYPE_CHECKING, Generic, Optional
|
3
3
|
|
4
4
|
from classiq.interface.generator.functions.port_declaration import (
|
5
5
|
PortDeclarationDirection,
|
@@ -34,13 +34,24 @@ class DeclarativeCallEmitter(
|
|
34
34
|
|
35
35
|
if self._is_function_purely_declarative(
|
36
36
|
function
|
37
|
-
) and self._are_args_purely_declarative(args):
|
37
|
+
) and self._are_args_purely_declarative(args, function.name):
|
38
38
|
self._interpreter.add_purely_declarative_function(function)
|
39
39
|
return False
|
40
40
|
|
41
41
|
return True
|
42
42
|
|
43
|
-
def _is_function_purely_declarative(
|
43
|
+
def _is_function_purely_declarative(
|
44
|
+
self, function: FunctionClosure, seen_funcs: Optional[set[str]] = None
|
45
|
+
) -> bool:
|
46
|
+
if seen_funcs is None:
|
47
|
+
seen_funcs = set()
|
48
|
+
if function.name in seen_funcs:
|
49
|
+
return True
|
50
|
+
seen_funcs.add(function.name)
|
51
|
+
|
52
|
+
if function.is_atomic:
|
53
|
+
return True
|
54
|
+
|
44
55
|
if function.name not in QMODULE.native_defs:
|
45
56
|
return False
|
46
57
|
|
@@ -56,9 +67,9 @@ class DeclarativeCallEmitter(
|
|
56
67
|
return False
|
57
68
|
|
58
69
|
dependencies = QMODULE.function_dependencies[function.name]
|
59
|
-
return self._are_identifiers_purely_declarative(dependencies)
|
70
|
+
return self._are_identifiers_purely_declarative(dependencies, seen_funcs)
|
60
71
|
|
61
|
-
def _are_args_purely_declarative(self, args: list[Evaluated]) -> bool:
|
72
|
+
def _are_args_purely_declarative(self, args: list[Evaluated], caller: str) -> bool:
|
62
73
|
values = [arg.value for arg in args]
|
63
74
|
function_inputs: list[FunctionClosure] = list(
|
64
75
|
chain.from_iterable(
|
@@ -78,10 +89,13 @@ class DeclarativeCallEmitter(
|
|
78
89
|
if any(func.is_lambda for func in function_inputs):
|
79
90
|
return False
|
80
91
|
dependencies = [func.name for func in function_inputs if not func.is_lambda]
|
81
|
-
return self._are_identifiers_purely_declarative(dependencies)
|
92
|
+
return self._are_identifiers_purely_declarative(dependencies, {caller})
|
82
93
|
|
83
|
-
def _are_identifiers_purely_declarative(
|
84
|
-
|
85
|
-
|
94
|
+
def _are_identifiers_purely_declarative(
|
95
|
+
self, dependencies: list[str], seen_funcs: set[str]
|
96
|
+
) -> bool:
|
97
|
+
return all(
|
98
|
+
self._is_function_purely_declarative(self._current_scope[dep].value)
|
86
99
|
for dep in dependencies
|
100
|
+
if seen_funcs is None or dep not in seen_funcs
|
87
101
|
)
|
@@ -15,6 +15,11 @@ from classiq.interface.debug_info.debug_info import (
|
|
15
15
|
DebugInfoCollection,
|
16
16
|
)
|
17
17
|
from classiq.interface.exceptions import ClassiqInternalExpansionError
|
18
|
+
from classiq.interface.generator.expressions.atomic_expression_functions import (
|
19
|
+
CLASSICAL_ATTRIBUTES,
|
20
|
+
SUPPORTED_CLASSIQ_BUILTIN_FUNCTIONS,
|
21
|
+
SUPPORTED_PYTHON_BUILTIN_FUNCTIONS,
|
22
|
+
)
|
18
23
|
from classiq.interface.generator.expressions.evaluated_expression import (
|
19
24
|
EvaluatedExpression,
|
20
25
|
)
|
@@ -30,7 +35,11 @@ from classiq.interface.generator.functions.port_declaration import (
|
|
30
35
|
PortDeclarationDirection,
|
31
36
|
)
|
32
37
|
from classiq.interface.helpers.pydantic_model_helpers import nameables_to_dict
|
33
|
-
from classiq.interface.model.handle_binding import
|
38
|
+
from classiq.interface.model.handle_binding import (
|
39
|
+
FieldHandleBinding,
|
40
|
+
HandleBinding,
|
41
|
+
NestedHandleBinding,
|
42
|
+
)
|
34
43
|
from classiq.interface.model.native_function_definition import NativeFunctionDefinition
|
35
44
|
from classiq.interface.model.quantum_function_declaration import (
|
36
45
|
NamedParamsQuantumFunctionDeclaration,
|
@@ -172,12 +181,7 @@ class Emitter(Generic[QuantumStatementT], ABC):
|
|
172
181
|
self._capture_classical_var(var_name, var_type)
|
173
182
|
|
174
183
|
def _update_captured_vars(self, op: QuantumOperation) -> None:
|
175
|
-
|
176
|
-
[(handle, PortDeclarationDirection.Input) for handle in op.inputs]
|
177
|
-
+ [(handle, PortDeclarationDirection.Output) for handle in op.outputs]
|
178
|
-
+ [(handle, PortDeclarationDirection.Inout) for handle in op.inouts]
|
179
|
-
)
|
180
|
-
for handle, direction in handles:
|
184
|
+
for handle, direction in op.handles_with_directions:
|
181
185
|
self._capture_handle(handle, direction)
|
182
186
|
|
183
187
|
def _capture_handle(
|
@@ -220,9 +224,17 @@ class Emitter(Generic[QuantumStatementT], ABC):
|
|
220
224
|
handles = dict.fromkeys(
|
221
225
|
handle
|
222
226
|
for handle in vrc.var_handles
|
223
|
-
if
|
227
|
+
if handle.name
|
228
|
+
not in SUPPORTED_PYTHON_BUILTIN_FUNCTIONS
|
229
|
+
| SUPPORTED_CLASSIQ_BUILTIN_FUNCTIONS
|
230
|
+
and isinstance(self._current_scope[handle.name].value, QuantumSymbol)
|
224
231
|
)
|
225
|
-
return [
|
232
|
+
return [
|
233
|
+
self._interpreter.evaluate(handle).value
|
234
|
+
for handle in handles
|
235
|
+
if not isinstance(handle, FieldHandleBinding)
|
236
|
+
or handle.field not in CLASSICAL_ATTRIBUTES
|
237
|
+
]
|
226
238
|
|
227
239
|
def _get_classical_vars_in_expression(
|
228
240
|
self, expr: Expression
|
@@ -49,13 +49,14 @@ class QuantumFunctionCallEmitter(CallEmitter[QuantumFunctionCall]):
|
|
49
49
|
raise ClassiqInternalExpansionError(
|
50
50
|
f"Unexpected lambda list type {type(funcs).__name__!r}"
|
51
51
|
)
|
52
|
-
self.
|
52
|
+
for stmt in self._create_recursive_if(call, index, len(funcs)):
|
53
|
+
self._interpreter.emit(stmt)
|
53
54
|
return True
|
54
55
|
|
55
56
|
@staticmethod
|
56
57
|
def _create_recursive_if(
|
57
58
|
call: QuantumFunctionCall, index: ClassicalScalarProxy, num_funcs: int
|
58
|
-
) -> QuantumStatement:
|
59
|
+
) -> list[QuantumStatement]:
|
59
60
|
if TYPE_CHECKING:
|
60
61
|
assert isinstance(call.function, OperandIdentifier)
|
61
62
|
stmt: list[QuantumStatement] = []
|
@@ -74,7 +75,7 @@ class QuantumFunctionCallEmitter(CallEmitter[QuantumFunctionCall]):
|
|
74
75
|
else_=stmt,
|
75
76
|
)
|
76
77
|
]
|
77
|
-
return stmt
|
78
|
+
return stmt
|
78
79
|
|
79
80
|
|
80
81
|
class DeclarativeQuantumFunctionCallEmitter(
|
@@ -22,7 +22,9 @@ from classiq.interface.generator.expressions.proxies.classical.qmod_struct_insta
|
|
22
22
|
from classiq.interface.generator.functions.type_name import TypeName
|
23
23
|
from classiq.interface.model.handle_binding import (
|
24
24
|
FieldHandleBinding,
|
25
|
+
GeneralHandle,
|
25
26
|
HandleBinding,
|
27
|
+
HandlesList,
|
26
28
|
SlicedHandleBinding,
|
27
29
|
SubscriptHandleBinding,
|
28
30
|
)
|
@@ -39,10 +41,17 @@ T = TypeVar("T")
|
|
39
41
|
|
40
42
|
|
41
43
|
@dataclass(frozen=True)
|
42
|
-
class
|
43
|
-
handle: HandleBinding
|
44
|
+
class QuantumVariable:
|
44
45
|
quantum_type: QuantumType
|
45
46
|
|
47
|
+
def emit(self) -> GeneralHandle:
|
48
|
+
raise NotImplementedError
|
49
|
+
|
50
|
+
|
51
|
+
@dataclass(frozen=True)
|
52
|
+
class QuantumSymbol(QuantumVariable):
|
53
|
+
handle: HandleBinding
|
54
|
+
|
46
55
|
@property
|
47
56
|
def is_subscript(self) -> bool:
|
48
57
|
return isinstance(self.handle, (SubscriptHandleBinding, SlicedHandleBinding))
|
@@ -66,8 +75,6 @@ class QuantumSymbol:
|
|
66
75
|
raise ClassiqExpansionError(
|
67
76
|
f"{self.quantum_type.type_name} is not subscriptable"
|
68
77
|
)
|
69
|
-
if TYPE_CHECKING:
|
70
|
-
assert self.quantum_type.length is not None
|
71
78
|
if isinstance(start, int) and isinstance(end, int) and start >= end:
|
72
79
|
raise ClassiqExpansionError(
|
73
80
|
f"{self.quantum_type.type_name} slice '{self.handle}[{start}:{end}]' "
|
@@ -75,6 +82,7 @@ class QuantumSymbol:
|
|
75
82
|
)
|
76
83
|
if (isinstance(start, int) and start < 0) or (
|
77
84
|
isinstance(end, int)
|
85
|
+
and self.quantum_type.length is not None
|
78
86
|
and self.quantum_type.length.is_constant()
|
79
87
|
and end > self.quantum_type.length_value
|
80
88
|
):
|
@@ -100,19 +108,23 @@ class QuantumSymbol:
|
|
100
108
|
raise ClassiqExpansionError(
|
101
109
|
f"{self.quantum_type.type_name} is not subscriptable"
|
102
110
|
)
|
103
|
-
if TYPE_CHECKING:
|
104
|
-
assert self.quantum_type.length is not None
|
105
111
|
if isinstance(index, int) and (
|
106
112
|
index < 0
|
107
113
|
or (
|
108
|
-
self.quantum_type.length
|
114
|
+
self.quantum_type.length is not None
|
115
|
+
and self.quantum_type.length.is_constant()
|
109
116
|
and index >= self.quantum_type.length_value
|
110
117
|
)
|
111
118
|
):
|
119
|
+
length_suffix = (
|
120
|
+
f" (of length {self.quantum_type.length})"
|
121
|
+
if self.quantum_type.length is not None
|
122
|
+
else ""
|
123
|
+
)
|
112
124
|
raise ClassiqExpansionError(
|
113
125
|
f"Index {index} is out of bounds for "
|
114
|
-
f"{self.quantum_type.type_name.lower()} {str(self.handle)!r}
|
115
|
-
f"
|
126
|
+
f"{self.quantum_type.type_name.lower()} {str(self.handle)!r}"
|
127
|
+
f"{length_suffix}"
|
116
128
|
)
|
117
129
|
return QuantumSymbol(
|
118
130
|
handle=SubscriptHandleBinding(
|
@@ -137,6 +149,47 @@ class QuantumSymbol:
|
|
137
149
|
for field_name, field_type in quantum_type.fields.items()
|
138
150
|
}
|
139
151
|
|
152
|
+
def __str__(self) -> str:
|
153
|
+
return str(self.handle)
|
154
|
+
|
155
|
+
|
156
|
+
@dataclass(frozen=True)
|
157
|
+
class QuantumSymbolList(QuantumVariable):
|
158
|
+
handles: list[HandleBinding]
|
159
|
+
|
160
|
+
@staticmethod
|
161
|
+
def from_symbols(
|
162
|
+
symbols: list[Union[QuantumSymbol, "QuantumSymbolList"]],
|
163
|
+
) -> "QuantumSymbolList":
|
164
|
+
handles = list(
|
165
|
+
itertools.chain.from_iterable(
|
166
|
+
(
|
167
|
+
symbol.handles
|
168
|
+
if isinstance(symbol, QuantumSymbolList)
|
169
|
+
else [symbol.handle]
|
170
|
+
)
|
171
|
+
for symbol in symbols
|
172
|
+
)
|
173
|
+
)
|
174
|
+
if len(handles) == 0:
|
175
|
+
raise ClassiqExpansionError("Empty concatenation expression")
|
176
|
+
length: Optional[Expression]
|
177
|
+
if any(not symbol.quantum_type.has_size_in_bits for symbol in symbols):
|
178
|
+
length = None
|
179
|
+
else:
|
180
|
+
length = Expression(
|
181
|
+
expr=str(sum(symbol.quantum_type.size_in_bits for symbol in symbols))
|
182
|
+
)
|
183
|
+
return QuantumSymbolList(
|
184
|
+
handles=handles, quantum_type=QuantumBitvector(length=length)
|
185
|
+
)
|
186
|
+
|
187
|
+
def emit(self) -> HandlesList:
|
188
|
+
return HandlesList(handles=self.handles)
|
189
|
+
|
190
|
+
def __str__(self) -> str:
|
191
|
+
return str(self.handles)
|
192
|
+
|
140
193
|
|
141
194
|
@singledispatch
|
142
195
|
def evaluated_to_str(value: Any) -> str:
|
@@ -180,7 +233,7 @@ class Evaluated: # FIXME: Merge with EvaluatedExpression if possible
|
|
180
233
|
def emit(self) -> ArgValue:
|
181
234
|
from classiq.model_expansions.closure import FunctionClosure
|
182
235
|
|
183
|
-
if isinstance(self.value, (
|
236
|
+
if isinstance(self.value, (QuantumVariable, FunctionClosure)):
|
184
237
|
return self.value.emit()
|
185
238
|
if isinstance(self.value, list) and all(
|
186
239
|
isinstance(item, FunctionClosure) for item in self.value
|
@@ -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
|
@@ -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
|
|
@@ -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 = (
|
@@ -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
|
@@ -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)"]],
|
@@ -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
|