classiq 0.56.1__py3-none-any.whl → 0.57.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/analyzer/show_interactive_hack.py +16 -4
- classiq/executor.py +1 -1
- classiq/interface/_version.py +1 -1
- classiq/interface/executor/execution_request.py +5 -5
- classiq/interface/generator/arith/arithmetic_expression_validator.py +28 -9
- classiq/interface/generator/types/builtin_enum_declarations.py +1 -0
- classiq/model_expansions/closure.py +24 -6
- classiq/model_expansions/quantum_operations/call_emitter.py +207 -0
- classiq/model_expansions/quantum_operations/classicalif.py +2 -2
- classiq/model_expansions/quantum_operations/control.py +7 -5
- classiq/model_expansions/quantum_operations/emitter.py +1 -186
- classiq/model_expansions/quantum_operations/expression_operation.py +26 -189
- classiq/model_expansions/quantum_operations/inplace_binary_operation.py +2 -2
- classiq/model_expansions/quantum_operations/invert.py +2 -2
- classiq/model_expansions/quantum_operations/phase.py +3 -1
- classiq/model_expansions/quantum_operations/power.py +2 -2
- classiq/model_expansions/quantum_operations/quantum_assignment_operation.py +7 -9
- classiq/model_expansions/quantum_operations/quantum_function_call.py +2 -2
- classiq/model_expansions/quantum_operations/repeat.py +2 -2
- classiq/model_expansions/transformers/__init__.py +0 -0
- classiq/model_expansions/transformers/var_splitter.py +237 -0
- classiq/qmod/builtins/classical_functions.py +1 -0
- classiq/qmod/builtins/functions/state_preparation.py +1 -1
- classiq/qmod/create_model_function.py +25 -20
- classiq/qmod/pretty_print/pretty_printer.py +53 -28
- classiq/qmod/qfunc.py +18 -16
- classiq/qmod/quantum_function.py +30 -24
- classiq/synthesis.py +3 -1
- {classiq-0.56.1.dist-info → classiq-0.57.0.dist-info}/METADATA +1 -1
- {classiq-0.56.1.dist-info → classiq-0.57.0.dist-info}/RECORD +31 -28
- {classiq-0.56.1.dist-info → classiq-0.57.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,237 @@
|
|
1
|
+
import ast
|
2
|
+
from dataclasses import dataclass
|
3
|
+
from itertools import chain
|
4
|
+
from typing import TYPE_CHECKING, Callable, TypeVar
|
5
|
+
|
6
|
+
from classiq.interface.exceptions import (
|
7
|
+
ClassiqExpansionError,
|
8
|
+
ClassiqInternalExpansionError,
|
9
|
+
)
|
10
|
+
from classiq.interface.generator.expressions.expression import Expression
|
11
|
+
from classiq.interface.generator.visitor import NodeType, Transformer
|
12
|
+
from classiq.interface.model.bind_operation import BindOperation
|
13
|
+
from classiq.interface.model.handle_binding import (
|
14
|
+
HandleBinding,
|
15
|
+
NestedHandleBinding,
|
16
|
+
SlicedHandleBinding,
|
17
|
+
)
|
18
|
+
from classiq.interface.model.quantum_type import (
|
19
|
+
QuantumBitvector,
|
20
|
+
QuantumScalar,
|
21
|
+
QuantumType,
|
22
|
+
)
|
23
|
+
from classiq.interface.model.variable_declaration_statement import (
|
24
|
+
VariableDeclarationStatement,
|
25
|
+
)
|
26
|
+
|
27
|
+
from classiq.model_expansions.scope import QuantumSymbol, Scope
|
28
|
+
from classiq.model_expansions.visitors.variable_references import VarRefCollector
|
29
|
+
|
30
|
+
AST_NODE = TypeVar("AST_NODE", bound=NodeType)
|
31
|
+
|
32
|
+
|
33
|
+
@dataclass(frozen=True)
|
34
|
+
class SymbolPart:
|
35
|
+
source_handle: HandleBinding
|
36
|
+
target_var_name: str
|
37
|
+
target_var_type: QuantumType
|
38
|
+
|
39
|
+
@property
|
40
|
+
def target_var_handle(self) -> HandleBinding:
|
41
|
+
return HandleBinding(name=self.target_var_name)
|
42
|
+
|
43
|
+
|
44
|
+
SymbolParts = dict[QuantumSymbol, list[SymbolPart]]
|
45
|
+
PartNamer = Callable[[str], str]
|
46
|
+
|
47
|
+
|
48
|
+
class VarSplitter:
|
49
|
+
def __init__(self, scope: Scope):
|
50
|
+
self._scope = scope
|
51
|
+
|
52
|
+
def split_symbols(self, expression: Expression, namer: PartNamer) -> SymbolParts:
|
53
|
+
vrc = VarRefCollector(ignore_duplicated_handles=True)
|
54
|
+
vrc.visit(ast.parse(expression.expr))
|
55
|
+
symbol_names_to_split = dict.fromkeys(
|
56
|
+
handle.name
|
57
|
+
for handle in self._get_handles(vrc)
|
58
|
+
if isinstance(handle, NestedHandleBinding)
|
59
|
+
)
|
60
|
+
|
61
|
+
symbol_handles = {
|
62
|
+
symbol: list(
|
63
|
+
dict.fromkeys(
|
64
|
+
handle.collapse()
|
65
|
+
for handle in vrc.var_handles
|
66
|
+
if handle.name == symbol.handle.name
|
67
|
+
)
|
68
|
+
)
|
69
|
+
for symbol_name in symbol_names_to_split
|
70
|
+
if isinstance(
|
71
|
+
symbol := self._scope[symbol_name].value,
|
72
|
+
QuantumSymbol,
|
73
|
+
)
|
74
|
+
}
|
75
|
+
|
76
|
+
return {
|
77
|
+
symbol: [
|
78
|
+
SymbolPart(
|
79
|
+
source_handle=part.handle,
|
80
|
+
target_var_name=namer(part.handle.identifier),
|
81
|
+
target_var_type=part.quantum_type,
|
82
|
+
)
|
83
|
+
for part in self._get_symbol_parts(symbol, handles)
|
84
|
+
]
|
85
|
+
for symbol, handles in symbol_handles.items()
|
86
|
+
}
|
87
|
+
|
88
|
+
def _get_handles(self, collector: VarRefCollector) -> list[HandleBinding]:
|
89
|
+
return [
|
90
|
+
handle
|
91
|
+
for handle in collector.var_handles
|
92
|
+
if isinstance(self._scope[handle.name].value, QuantumSymbol)
|
93
|
+
]
|
94
|
+
|
95
|
+
def _get_symbol_parts(
|
96
|
+
self, symbol: QuantumSymbol, target_parts: list[HandleBinding]
|
97
|
+
) -> list[QuantumSymbol]:
|
98
|
+
for i in range(len(target_parts)):
|
99
|
+
for j in range(i + 1, len(target_parts)):
|
100
|
+
if target_parts[i].overlaps(target_parts[j]):
|
101
|
+
raise ClassiqInternalExpansionError(
|
102
|
+
f"Handles {str(target_parts[i])!r} and "
|
103
|
+
f"{str(target_parts[j])!r} overlapping in expression"
|
104
|
+
)
|
105
|
+
return self._get_symbol_parts_unsafe(symbol, target_parts)
|
106
|
+
|
107
|
+
def _get_symbol_parts_unsafe(
|
108
|
+
self, symbol: QuantumSymbol, target_parts: list[HandleBinding]
|
109
|
+
) -> list[QuantumSymbol]:
|
110
|
+
if all(
|
111
|
+
symbol.handle == target_part or symbol.handle not in target_part.prefixes()
|
112
|
+
for target_part in target_parts
|
113
|
+
) or isinstance(symbol.quantum_type, QuantumScalar):
|
114
|
+
return [symbol]
|
115
|
+
|
116
|
+
if isinstance(symbol.quantum_type, QuantumBitvector):
|
117
|
+
return self._get_array_parts(symbol, target_parts)
|
118
|
+
|
119
|
+
return self._get_struct_parts(symbol, target_parts)
|
120
|
+
|
121
|
+
def _get_array_parts(
|
122
|
+
self, symbol: QuantumSymbol, target_parts: list[HandleBinding]
|
123
|
+
) -> list[QuantumSymbol]:
|
124
|
+
if TYPE_CHECKING:
|
125
|
+
assert isinstance(symbol.quantum_type, QuantumBitvector)
|
126
|
+
|
127
|
+
if not symbol.quantum_type.has_length:
|
128
|
+
raise ClassiqExpansionError(
|
129
|
+
f"Could not determine the length of quantum array " f"{symbol.handle}."
|
130
|
+
)
|
131
|
+
target_slices = {
|
132
|
+
target_part.start.to_int_value(): target_part.end.to_int_value()
|
133
|
+
for target_part in target_parts
|
134
|
+
if isinstance(target_part, SlicedHandleBinding)
|
135
|
+
and symbol.handle == target_part.base_handle
|
136
|
+
}
|
137
|
+
|
138
|
+
symbol_parts: list[QuantumSymbol] = []
|
139
|
+
idx = 0
|
140
|
+
while idx < symbol.quantum_type.length_value:
|
141
|
+
if idx in target_slices:
|
142
|
+
stop = target_slices[idx]
|
143
|
+
if stop <= idx:
|
144
|
+
raise ClassiqInternalExpansionError(
|
145
|
+
f"Illegal sliced handle {str(symbol[idx: stop].handle)!r}"
|
146
|
+
)
|
147
|
+
symbol_parts.append(symbol[idx:stop])
|
148
|
+
idx = stop
|
149
|
+
else:
|
150
|
+
symbol_parts.extend(
|
151
|
+
self._get_symbol_parts_unsafe(symbol[idx], target_parts)
|
152
|
+
)
|
153
|
+
idx += 1
|
154
|
+
|
155
|
+
return symbol_parts
|
156
|
+
|
157
|
+
def _get_struct_parts(
|
158
|
+
self, symbol: QuantumSymbol, target_parts: list[HandleBinding]
|
159
|
+
) -> list[QuantumSymbol]:
|
160
|
+
return list(
|
161
|
+
chain.from_iterable(
|
162
|
+
self._get_symbol_parts_unsafe(field_symbol, target_parts)
|
163
|
+
for field_symbol in symbol.fields.values()
|
164
|
+
)
|
165
|
+
)
|
166
|
+
|
167
|
+
@staticmethod
|
168
|
+
def get_bind_ops(symbol_parts: SymbolParts) -> list[BindOperation]:
|
169
|
+
return [
|
170
|
+
BindOperation(
|
171
|
+
in_handles=[symbol.handle],
|
172
|
+
out_handles=[part.target_var_handle for part in parts],
|
173
|
+
)
|
174
|
+
for symbol, parts in symbol_parts.items()
|
175
|
+
]
|
176
|
+
|
177
|
+
@staticmethod
|
178
|
+
def get_var_decls(symbol_parts: SymbolParts) -> list[VariableDeclarationStatement]:
|
179
|
+
return [
|
180
|
+
VariableDeclarationStatement(
|
181
|
+
name=part.target_var_name,
|
182
|
+
quantum_type=part.target_var_type,
|
183
|
+
)
|
184
|
+
for part in chain.from_iterable(symbol_parts.values())
|
185
|
+
]
|
186
|
+
|
187
|
+
def _check_all_handles_were_replaced(self, new_expr_str: str) -> None:
|
188
|
+
vrc = VarRefCollector(ignore_duplicated_handles=True)
|
189
|
+
vrc.visit(ast.parse(new_expr_str))
|
190
|
+
for handle in self._get_handles(vrc):
|
191
|
+
if isinstance(handle, NestedHandleBinding):
|
192
|
+
raise ClassiqInternalExpansionError(f"Did not replace handle {handle}")
|
193
|
+
|
194
|
+
def rewrite(self, subject: AST_NODE, symbol_mapping: SymbolParts) -> AST_NODE:
|
195
|
+
handle_replacements = {
|
196
|
+
part.source_handle: part.target_var_handle
|
197
|
+
for parts in symbol_mapping.values()
|
198
|
+
for part in parts
|
199
|
+
}
|
200
|
+
|
201
|
+
class ReplaceSplitVars(Transformer):
|
202
|
+
@staticmethod
|
203
|
+
def visit_HandleBinding(handle: HandleBinding) -> HandleBinding:
|
204
|
+
handle = handle.collapse()
|
205
|
+
for handle_to_replace, replacement in handle_replacements.items():
|
206
|
+
handle = handle.replace_prefix(handle_to_replace, replacement)
|
207
|
+
return handle
|
208
|
+
|
209
|
+
@staticmethod
|
210
|
+
def visit_Expression(expr: Expression) -> Expression:
|
211
|
+
return self._rewrite_expression(symbol_mapping, expr)
|
212
|
+
|
213
|
+
return ReplaceSplitVars().visit(subject)
|
214
|
+
|
215
|
+
def _rewrite_expression(
|
216
|
+
self,
|
217
|
+
symbol_mapping: SymbolParts,
|
218
|
+
expression: Expression,
|
219
|
+
) -> Expression:
|
220
|
+
vrc = VarRefCollector(ignore_duplicated_handles=True)
|
221
|
+
vrc.visit(ast.parse(expression.expr))
|
222
|
+
|
223
|
+
handle_names = {
|
224
|
+
part.source_handle: part.target_var_name
|
225
|
+
for parts in symbol_mapping.values()
|
226
|
+
for part in parts
|
227
|
+
}
|
228
|
+
new_expr_str = expression.expr
|
229
|
+
for handle in vrc.var_handles:
|
230
|
+
collapsed_handle = handle.collapse()
|
231
|
+
if collapsed_handle in handle_names:
|
232
|
+
new_expr_str = new_expr_str.replace(
|
233
|
+
str(handle), handle_names[collapsed_handle]
|
234
|
+
)
|
235
|
+
self._check_all_handles_were_replaced(new_expr_str)
|
236
|
+
|
237
|
+
return Expression(expr=new_expr_str)
|
@@ -415,7 +415,7 @@ def inplace_prepare_int(value: CInt, target: QArray[QBit]) -> None:
|
|
415
415
|
@qfunc(external=True)
|
416
416
|
def prepare_int(
|
417
417
|
value: CInt,
|
418
|
-
out: Output[QNum[Literal["floor(log(value, 2)) + 1"]
|
418
|
+
out: Output[QNum[Literal["floor(log(value, 2)) + 1"]]],
|
419
419
|
) -> None:
|
420
420
|
"""
|
421
421
|
[Qmod Classiq-library function]
|
@@ -71,7 +71,6 @@ def create_model(
|
|
71
71
|
constraints,
|
72
72
|
execution_preferences,
|
73
73
|
preferences,
|
74
|
-
classical_execution_function,
|
75
74
|
)
|
76
75
|
else:
|
77
76
|
if TYPE_CHECKING:
|
@@ -95,26 +94,21 @@ def _expand_generative_model(
|
|
95
94
|
constraints: Optional[Constraints] = None,
|
96
95
|
execution_preferences: Optional[ExecutionPreferences] = None,
|
97
96
|
preferences: Optional[Preferences] = None,
|
98
|
-
classical_execution_function: Optional[CFunc] = None,
|
99
97
|
) -> Model:
|
100
98
|
@QFunc
|
101
99
|
def _dummy() -> None:
|
102
100
|
pass
|
103
101
|
|
104
|
-
functions_compilation_metadata = {
|
105
|
-
dec_func._py_callable.__name__: dec_func.compilation_metadata
|
106
|
-
for dec_func in DEC_QFUNCS
|
107
|
-
if dec_func.compilation_metadata is not None
|
108
|
-
}
|
109
102
|
model = _dummy.create_model(
|
110
103
|
constraints,
|
111
104
|
execution_preferences,
|
112
105
|
preferences,
|
113
|
-
classical_execution_function,
|
114
|
-
functions_compilation_metadata,
|
115
106
|
)
|
116
|
-
|
117
|
-
model.functions =
|
107
|
+
gen_expand_model = _get_generative_functions(gen_main, preferences)
|
108
|
+
model.functions = gen_expand_model.functions
|
109
|
+
model.functions_compilation_metadata = (
|
110
|
+
gen_expand_model.functions_compilation_metadata
|
111
|
+
)
|
118
112
|
model.types = list(QMODULE.type_decls.values())
|
119
113
|
model.enums = list(QMODULE.enum_decls.values())
|
120
114
|
model.qstructs = list(QMODULE.qstruct_decls.values())
|
@@ -122,8 +116,9 @@ def _expand_generative_model(
|
|
122
116
|
|
123
117
|
|
124
118
|
def _get_generative_functions(
|
125
|
-
gen_main: QFunc,
|
126
|
-
|
119
|
+
gen_main: QFunc,
|
120
|
+
preferences: Optional[Preferences],
|
121
|
+
) -> Model:
|
127
122
|
# The Interpreter accepts a model and a list of generative functions.
|
128
123
|
# Since the main function is generative, it can only be expanded using the
|
129
124
|
# Interpreter.
|
@@ -132,14 +127,21 @@ def _get_generative_functions(
|
|
132
127
|
# passing them to the Interpreter.
|
133
128
|
gen_model = _get_wrapper_main(gen_main, preferences)
|
134
129
|
gen_functions = _get_all_model_functions_as_generative_functions()
|
135
|
-
|
136
|
-
return list(functions_dict.values())
|
130
|
+
return _interpret_generative_model(gen_model, gen_functions)
|
137
131
|
|
138
132
|
|
139
|
-
def _get_wrapper_main(
|
133
|
+
def _get_wrapper_main(
|
134
|
+
gen_main: QFunc,
|
135
|
+
preferences: Optional[Preferences],
|
136
|
+
) -> Model:
|
140
137
|
extra_args = {}
|
141
138
|
if preferences is not None:
|
142
139
|
extra_args["preferences"] = preferences
|
140
|
+
functions_compilation_metadata = {
|
141
|
+
qfunc._py_callable.__name__: qfunc.compilation_metadata
|
142
|
+
for qfunc in DEC_QFUNCS + GEN_QFUNCS
|
143
|
+
if qfunc.compilation_metadata is not None
|
144
|
+
}
|
143
145
|
return Model(
|
144
146
|
functions=[
|
145
147
|
NativeFunctionDefinition(
|
@@ -157,6 +159,7 @@ def _get_wrapper_main(gen_main: QFunc, preferences: Optional[Preferences]) -> Mo
|
|
157
159
|
],
|
158
160
|
),
|
159
161
|
],
|
162
|
+
functions_compilation_metadata=functions_compilation_metadata,
|
160
163
|
**extra_args,
|
161
164
|
)
|
162
165
|
|
@@ -164,7 +167,9 @@ def _get_wrapper_main(gen_main: QFunc, preferences: Optional[Preferences]) -> Mo
|
|
164
167
|
def _get_all_model_functions_as_generative_functions() -> list[GenerativeQFunc]:
|
165
168
|
|
166
169
|
gen_functions = list(GEN_QFUNCS) + [
|
167
|
-
GenerativeQFunc(
|
170
|
+
GenerativeQFunc(
|
171
|
+
dec_func._py_callable, dec_func.func_decl, dec_func.compilation_metadata
|
172
|
+
)
|
168
173
|
for dec_func in DEC_QFUNCS
|
169
174
|
]
|
170
175
|
return [
|
@@ -174,6 +179,7 @@ def _get_all_model_functions_as_generative_functions() -> list[GenerativeQFunc]:
|
|
174
179
|
else GenerativeQFunc(
|
175
180
|
gen_func._py_callable,
|
176
181
|
gen_func.func_decl.model_copy(update={"name": GEN_MAIN_NAME}),
|
182
|
+
gen_func.compilation_metadata,
|
177
183
|
)
|
178
184
|
)
|
179
185
|
for gen_func in gen_functions
|
@@ -183,7 +189,7 @@ def _get_all_model_functions_as_generative_functions() -> list[GenerativeQFunc]:
|
|
183
189
|
|
184
190
|
def _interpret_generative_model(
|
185
191
|
gen_model: Model, gen_functions: list[GenerativeQFunc]
|
186
|
-
) ->
|
192
|
+
) -> Model:
|
187
193
|
resolve_function_calls(
|
188
194
|
gen_model,
|
189
195
|
{gen_func.func_decl.name: gen_func.func_decl for gen_func in gen_functions},
|
@@ -201,5 +207,4 @@ def _interpret_generative_model(
|
|
201
207
|
expanded_gen_main_name
|
202
208
|
].model_copy(update={"name": MAIN_FUNCTION_NAME})
|
203
209
|
functions_dict.pop(expanded_gen_main_name)
|
204
|
-
|
205
|
-
return functions_dict
|
210
|
+
return expand_model.model_copy(update={"functions": list(functions_dict.values())})
|
@@ -3,6 +3,7 @@ from typing import Optional, Union, cast
|
|
3
3
|
|
4
4
|
import black
|
5
5
|
|
6
|
+
from classiq.interface.exceptions import ClassiqInternalError
|
6
7
|
from classiq.interface.generator.constant import Constant
|
7
8
|
from classiq.interface.generator.expressions.expression import Expression
|
8
9
|
from classiq.interface.generator.functions.classical_type import (
|
@@ -86,16 +87,15 @@ class VariableDeclarationAssignment(Visitor):
|
|
86
87
|
def __init__(self, pretty_printer: "PythonPrettyPrinter") -> None:
|
87
88
|
self.pretty_printer = pretty_printer
|
88
89
|
|
89
|
-
def visit(self, node: NodeType) -> tuple[str,
|
90
|
+
def visit(self, node: NodeType) -> tuple[str, list[str]]:
|
90
91
|
res = super().visit(node)
|
91
92
|
if not isinstance(res, tuple):
|
92
93
|
raise AssertionError(f"Pretty printing for {type(node)} is not supported ")
|
93
94
|
return res # type: ignore[return-value]
|
94
95
|
|
95
|
-
def visit_QuantumBit(self, qtype: QuantumBit) -> tuple[str,
|
96
|
+
def visit_QuantumBit(self, qtype: QuantumBit) -> tuple[str, list[str]]:
|
96
97
|
self.pretty_printer._imports["QBit"] = 1
|
97
|
-
|
98
|
-
return "QBit", None
|
98
|
+
return "QBit", []
|
99
99
|
|
100
100
|
def visit_QuantumBitvector(
|
101
101
|
self, qtype: QuantumBitvector
|
@@ -111,19 +111,10 @@ class VariableDeclarationAssignment(Visitor):
|
|
111
111
|
self, qtype: QuantumNumeric
|
112
112
|
) -> tuple[str, Optional[list[str]]]:
|
113
113
|
self.pretty_printer._imports["QNum"] = 1
|
114
|
+
return "QNum", self.pretty_printer._get_qnum_properties(qtype)
|
114
115
|
|
115
|
-
|
116
|
-
|
117
|
-
params = [
|
118
|
-
self.pretty_printer.visit(param)
|
119
|
-
for param in [qtype.size, qtype.is_signed, qtype.fraction_digits]
|
120
|
-
if param is not None
|
121
|
-
]
|
122
|
-
|
123
|
-
return "QNum", params
|
124
|
-
|
125
|
-
def visit_TypeName(self, qtype: TypeName) -> tuple[str, None]:
|
126
|
-
return qtype.name, None
|
116
|
+
def visit_TypeName(self, qtype: TypeName) -> tuple[str, list[str]]:
|
117
|
+
return qtype.name, []
|
127
118
|
|
128
119
|
|
129
120
|
class PythonPrettyPrinter(Visitor):
|
@@ -269,21 +260,46 @@ class PythonPrettyPrinter(Visitor):
|
|
269
260
|
return f"QArray[{element_type}]"
|
270
261
|
|
271
262
|
def visit_QuantumNumeric(self, qtype: QuantumNumeric) -> str:
|
272
|
-
params = ""
|
273
263
|
self._imports["QNum"] = 1
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
264
|
+
params = ""
|
265
|
+
qnum_properties = self._get_qnum_properties(qtype)
|
266
|
+
if len(qnum_properties) > 0:
|
278
267
|
params = "[{}]".format(
|
279
|
-
", ".join(
|
280
|
-
_add_quotes(self.visit(param))
|
281
|
-
for param in [qtype.size, qtype.is_signed, qtype.fraction_digits]
|
282
|
-
)
|
268
|
+
", ".join(_add_quotes(param) for param in qnum_properties)
|
283
269
|
)
|
284
|
-
|
285
270
|
return f"QNum{params}"
|
286
271
|
|
272
|
+
def _get_qnum_properties(self, qtype: QuantumNumeric) -> list[str]:
|
273
|
+
params: list[str] = []
|
274
|
+
if qtype.size is None:
|
275
|
+
return params
|
276
|
+
params.append(self.visit(qtype.size))
|
277
|
+
|
278
|
+
is_signed_expr = qtype.is_signed
|
279
|
+
fraction_digits_expr = qtype.fraction_digits
|
280
|
+
if is_signed_expr is None:
|
281
|
+
if fraction_digits_expr is not None:
|
282
|
+
raise ClassiqInternalError
|
283
|
+
return params
|
284
|
+
if fraction_digits_expr is None:
|
285
|
+
raise ClassiqInternalError
|
286
|
+
|
287
|
+
is_unsigned = (
|
288
|
+
is_signed_expr.is_evaluated()
|
289
|
+
and not is_signed_expr.to_bool_value()
|
290
|
+
or is_signed_expr.expr == "UNSIGNED"
|
291
|
+
)
|
292
|
+
is_integer = (
|
293
|
+
fraction_digits_expr.is_evaluated()
|
294
|
+
and fraction_digits_expr.to_int_value() == 0
|
295
|
+
)
|
296
|
+
if is_unsigned and is_integer:
|
297
|
+
return params
|
298
|
+
params.append(self.visit(is_signed_expr))
|
299
|
+
params.append(self.visit(fraction_digits_expr))
|
300
|
+
|
301
|
+
return params
|
302
|
+
|
287
303
|
def visit_AnonClassicalParameterDeclaration(
|
288
304
|
self, cparam: AnonClassicalParameterDeclaration
|
289
305
|
) -> str:
|
@@ -325,7 +341,7 @@ class PythonPrettyPrinter(Visitor):
|
|
325
341
|
type_name, params = VariableDeclarationAssignment(self).visit(
|
326
342
|
local_decl.quantum_type
|
327
343
|
)
|
328
|
-
params = [f'"{local_decl.name}"'] +
|
344
|
+
params = [f'"{local_decl.name}"'] + params
|
329
345
|
param_args = ", ".join(params)
|
330
346
|
return f"{self._indent}{self.visit_QuantumVariableDeclaration(local_decl)} = {type_name}({param_args})\n"
|
331
347
|
|
@@ -516,7 +532,12 @@ class PythonPrettyPrinter(Visitor):
|
|
516
532
|
|
517
533
|
|
518
534
|
def _add_quotes(exp: str) -> str:
|
519
|
-
if
|
535
|
+
if (
|
536
|
+
exp.lower() == "true"
|
537
|
+
or exp.lower() == "false"
|
538
|
+
or _convertible_to_number(exp)
|
539
|
+
or _is_constant(exp)
|
540
|
+
):
|
520
541
|
return exp
|
521
542
|
|
522
543
|
return f'"{exp}"'
|
@@ -531,3 +552,7 @@ def _convertible_to_number(exp: str) -> bool:
|
|
531
552
|
else:
|
532
553
|
return True
|
533
554
|
return False
|
555
|
+
|
556
|
+
|
557
|
+
def _is_constant(exp: str) -> bool:
|
558
|
+
return exp in ("SIGNED", "UNSIGNED")
|
classiq/qmod/qfunc.py
CHANGED
@@ -1,9 +1,14 @@
|
|
1
1
|
from typing import Callable, Literal, Optional, Union, overload
|
2
2
|
|
3
|
-
from classiq.interface.exceptions import
|
3
|
+
from classiq.interface.exceptions import ClassiqInternalError
|
4
4
|
|
5
5
|
from classiq.qmod.quantum_callable import QCallable
|
6
|
-
from classiq.qmod.quantum_function import
|
6
|
+
from classiq.qmod.quantum_function import (
|
7
|
+
BaseQFunc,
|
8
|
+
ExternalQFunc,
|
9
|
+
GenerativeQFunc,
|
10
|
+
QFunc,
|
11
|
+
)
|
7
12
|
|
8
13
|
GEN_QFUNCS: list[GenerativeQFunc] = []
|
9
14
|
DEC_QFUNCS: list[QFunc] = []
|
@@ -54,25 +59,22 @@ def qfunc(
|
|
54
59
|
synthesize_separately: bool = False,
|
55
60
|
) -> Union[Callable[[Callable], QCallable], QCallable]:
|
56
61
|
def wrapper(func: Callable) -> QCallable:
|
62
|
+
qfunc: BaseQFunc
|
57
63
|
if generative:
|
58
|
-
|
64
|
+
qfunc = GenerativeQFunc(func)
|
65
|
+
GEN_QFUNCS.append(qfunc)
|
66
|
+
elif external:
|
59
67
|
if synthesize_separately:
|
60
|
-
raise
|
61
|
-
"
|
62
|
-
)
|
63
|
-
GEN_QFUNCS.append(gen_qfunc)
|
64
|
-
return gen_qfunc
|
65
|
-
if external:
|
66
|
-
if synthesize_separately:
|
67
|
-
raise ClassiqError(
|
68
|
-
"External functions can not be synthesized separately"
|
68
|
+
raise ClassiqInternalError(
|
69
|
+
"External functions can't be marked as synthesized separately"
|
69
70
|
)
|
70
71
|
return ExternalQFunc(func)
|
71
|
-
|
72
|
+
else:
|
73
|
+
qfunc = QFunc(func)
|
74
|
+
DEC_QFUNCS.append(qfunc)
|
72
75
|
if synthesize_separately:
|
73
|
-
|
74
|
-
|
75
|
-
return dec_qfunc
|
76
|
+
qfunc.update_compilation_metadata(should_synthesize_separately=True)
|
77
|
+
return qfunc
|
76
78
|
|
77
79
|
if func is not None:
|
78
80
|
return wrapper(func)
|
classiq/qmod/quantum_function.py
CHANGED
@@ -28,12 +28,35 @@ from classiq.qmod.quantum_expandable import QExpandable, QTerminalCallable
|
|
28
28
|
from classiq.qmod.utilities import mangle_keyword
|
29
29
|
|
30
30
|
|
31
|
-
class
|
31
|
+
class BaseQFunc(QExpandable):
|
32
|
+
def __init__(
|
33
|
+
self,
|
34
|
+
py_callable: Callable,
|
35
|
+
compilation_metadata: Optional[CompilationMetadata] = None,
|
36
|
+
) -> None:
|
37
|
+
super().__init__(py_callable)
|
38
|
+
self.compilation_metadata = compilation_metadata
|
39
|
+
|
40
|
+
def update_compilation_metadata(self, **kwargs: Any) -> None:
|
41
|
+
self.compilation_metadata = self._compilation_metadata.model_copy(update=kwargs)
|
42
|
+
|
43
|
+
@property
|
44
|
+
def _compilation_metadata(self) -> CompilationMetadata:
|
45
|
+
if self.compilation_metadata is None:
|
46
|
+
return CompilationMetadata()
|
47
|
+
return self.compilation_metadata
|
48
|
+
|
49
|
+
|
50
|
+
class QFunc(BaseQFunc):
|
32
51
|
FRAME_DEPTH = 2
|
33
52
|
|
34
|
-
def __init__(
|
53
|
+
def __init__(
|
54
|
+
self,
|
55
|
+
py_callable: Callable,
|
56
|
+
compilation_metadata: Optional[CompilationMetadata] = None,
|
57
|
+
) -> None:
|
35
58
|
_validate_no_gen_params(py_callable.__annotations__)
|
36
|
-
super().__init__(py_callable)
|
59
|
+
super().__init__(py_callable, compilation_metadata)
|
37
60
|
functools.update_wrapper(self, py_callable)
|
38
61
|
self.compilation_metadata: Optional[CompilationMetadata] = None
|
39
62
|
|
@@ -44,21 +67,6 @@ class QFunc(QExpandable):
|
|
44
67
|
infer_func_decl(self._py_callable, qmodule=self._qmodule),
|
45
68
|
)
|
46
69
|
|
47
|
-
@property
|
48
|
-
def should_synthesize_separately(self) -> bool:
|
49
|
-
if self.compilation_metadata is None:
|
50
|
-
return False
|
51
|
-
return self.compilation_metadata.should_synthesize_separately
|
52
|
-
|
53
|
-
@should_synthesize_separately.setter
|
54
|
-
def should_synthesize_separately(self, value: bool) -> None:
|
55
|
-
if self.compilation_metadata is None:
|
56
|
-
self.compilation_metadata = CompilationMetadata(
|
57
|
-
should_synthesize_separately=value
|
58
|
-
)
|
59
|
-
else:
|
60
|
-
self.compilation_metadata.should_synthesize_separately = value
|
61
|
-
|
62
70
|
def __call__(self, *args: Any, **kwargs: Any) -> None:
|
63
71
|
super().__call__(*args, **kwargs)
|
64
72
|
self.expand()
|
@@ -69,16 +77,13 @@ class QFunc(QExpandable):
|
|
69
77
|
execution_preferences: Optional[ExecutionPreferences] = None,
|
70
78
|
preferences: Optional[Preferences] = None,
|
71
79
|
classical_execution_function: Optional[CFunc] = None,
|
72
|
-
functions_compilation_metadata: Optional[dict[str, CompilationMetadata]] = None,
|
73
80
|
) -> Model:
|
74
|
-
if functions_compilation_metadata is None:
|
75
|
-
functions_compilation_metadata = dict()
|
76
81
|
self._qmodule.enum_decls = dict()
|
77
82
|
self._qmodule.type_decls = dict()
|
78
83
|
self._qmodule.qstruct_decls = dict()
|
79
84
|
self._qmodule.native_defs = dict()
|
80
85
|
self._qmodule.constants = dict()
|
81
|
-
self._qmodule.functions_compilation_metadata =
|
86
|
+
self._qmodule.functions_compilation_metadata = dict()
|
82
87
|
QConstant.set_current_model(self._qmodule)
|
83
88
|
self.expand()
|
84
89
|
model_extra_settings: list[tuple[str, Any]] = [
|
@@ -167,13 +172,14 @@ class ExternalQFunc(QTerminalCallable):
|
|
167
172
|
][0]
|
168
173
|
|
169
174
|
|
170
|
-
class GenerativeQFunc(
|
175
|
+
class GenerativeQFunc(BaseQFunc):
|
171
176
|
def __init__(
|
172
177
|
self,
|
173
178
|
py_callable: Callable,
|
174
179
|
func_decl: Optional[NamedParamsQuantumFunctionDeclaration] = None,
|
180
|
+
compilation_metadata: Optional[CompilationMetadata] = None,
|
175
181
|
) -> None:
|
176
|
-
super().__init__(py_callable)
|
182
|
+
super().__init__(py_callable, compilation_metadata)
|
177
183
|
self._func_decl = func_decl
|
178
184
|
|
179
185
|
@property
|
classiq/synthesis.py
CHANGED
@@ -20,13 +20,15 @@ CANT_PARSE_QUANTUM_PROGRAM_MSG = (
|
|
20
20
|
)
|
21
21
|
|
22
22
|
|
23
|
-
def show(quantum_program: SerializedQuantumProgram) -> None:
|
23
|
+
def show(quantum_program: SerializedQuantumProgram, display_url: bool = True) -> None:
|
24
24
|
"""
|
25
25
|
Displays the interactive representation of the quantum program in the Classiq IDE.
|
26
26
|
|
27
27
|
Args:
|
28
28
|
quantum_program:
|
29
29
|
The serialized quantum program to be displayed.
|
30
|
+
display_url:
|
31
|
+
Whether to print the url
|
30
32
|
|
31
33
|
Links:
|
32
34
|
[Visualization tool](https://docs.classiq.io/latest/reference-manual/analyzer/quantum-program-visualization-tool/)
|