classiq 0.78.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/interface/_version.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/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 +3 -1
- classiq/interface/helpers/model_normalizer.py +47 -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/evaluators/argument_types.py +20 -0
- classiq/model_expansions/evaluators/classical_type_inference.py +42 -13
- classiq/model_expansions/evaluators/quantum_type_utils.py +31 -3
- classiq/model_expansions/generative_functions.py +1 -1
- 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 +47 -7
- classiq/model_expansions/quantum_operations/function_calls_cache.py +8 -1
- classiq/model_expansions/quantum_operations/quantum_function_call.py +5 -6
- classiq/model_expansions/scope.py +14 -4
- classiq/model_expansions/scope_initialization.py +1 -14
- classiq/model_expansions/visitors/symbolic_param_inference.py +32 -16
- 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/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/semantics/annotation/qstruct_annotator.py +15 -4
- classiq/qmod/utilities.py +1 -1
- {classiq-0.78.0.dist-info → classiq-0.79.0.dist-info}/METADATA +1 -1
- {classiq-0.78.0.dist-info → classiq-0.79.0.dist-info}/RECORD +48 -45
- {classiq-0.78.0.dist-info → classiq-0.79.0.dist-info}/WHEEL +0 -0
@@ -31,8 +31,13 @@ from classiq.interface.model.handle_binding import (
|
|
31
31
|
SubscriptHandleBinding,
|
32
32
|
)
|
33
33
|
from classiq.interface.model.quantum_function_call import ArgValue
|
34
|
+
from classiq.interface.model.quantum_function_declaration import (
|
35
|
+
AnonPositionalArg,
|
36
|
+
AnonQuantumOperandDeclaration,
|
37
|
+
)
|
34
38
|
from classiq.interface.model.quantum_type import (
|
35
39
|
QuantumBitvector,
|
40
|
+
QuantumNumeric,
|
36
41
|
QuantumType,
|
37
42
|
)
|
38
43
|
|
@@ -184,6 +189,9 @@ class QuantumSymbolList(QuantumVariable):
|
|
184
189
|
length = Expression(
|
185
190
|
expr=str(sum(symbol.quantum_type.size_in_bits for symbol in symbols))
|
186
191
|
)
|
192
|
+
for symbol in symbols:
|
193
|
+
if isinstance(symbol.quantum_type, QuantumNumeric):
|
194
|
+
symbol.quantum_type.reset_bounds()
|
187
195
|
return QuantumSymbolList(
|
188
196
|
handles=handles, quantum_type=QuantumBitvector(length=length)
|
189
197
|
)
|
@@ -212,7 +220,7 @@ def _evaluated_to_str_struct_literal(value: QmodStructInstance) -> str:
|
|
212
220
|
|
213
221
|
def _raise_type_error(val: Any, t: type, location_hint: Optional[str]) -> NoReturn:
|
214
222
|
if isinstance(val, sympy.Basic) and len(val.free_symbols) > 0:
|
215
|
-
symbolic_vars =
|
223
|
+
symbolic_vars = sorted(map(str, val.free_symbols))
|
216
224
|
suffix = f" {location_hint}" if location_hint is not None else ""
|
217
225
|
raise ClassiqExpansionError(
|
218
226
|
f"Cannot use execution parameter{s(symbolic_vars)} {readable_list(symbolic_vars, quote=True)} in a compile-time context{suffix}"
|
@@ -240,13 +248,15 @@ class Evaluated: # FIXME: Merge with EvaluatedExpression if possible
|
|
240
248
|
|
241
249
|
return int(self.value)
|
242
250
|
|
243
|
-
def emit(self) -> ArgValue:
|
251
|
+
def emit(self, param: Optional[AnonPositionalArg] = None) -> ArgValue:
|
244
252
|
from classiq.model_expansions.closure import FunctionClosure
|
245
253
|
|
246
254
|
if isinstance(self.value, (QuantumVariable, FunctionClosure)):
|
247
255
|
return self.value.emit()
|
248
|
-
if
|
249
|
-
isinstance(
|
256
|
+
if (
|
257
|
+
isinstance(param, AnonQuantumOperandDeclaration)
|
258
|
+
and isinstance(self.value, list)
|
259
|
+
and all(isinstance(item, FunctionClosure) for item in self.value)
|
250
260
|
):
|
251
261
|
return [item.emit() for item in self.value]
|
252
262
|
|
@@ -1,12 +1,7 @@
|
|
1
1
|
from collections.abc import Sequence
|
2
2
|
|
3
|
-
from classiq.interface.exceptions import ClassiqError
|
3
|
+
from classiq.interface.exceptions import ClassiqError
|
4
4
|
from classiq.interface.generator.constant import Constant
|
5
|
-
from classiq.interface.generator.functions.classical_type import (
|
6
|
-
ClassicalArray,
|
7
|
-
ClassicalList,
|
8
|
-
)
|
9
|
-
from classiq.interface.generator.functions.concrete_types import ConcreteClassicalType
|
10
5
|
from classiq.interface.model.classical_parameter_declaration import (
|
11
6
|
ClassicalParameterDeclaration,
|
12
7
|
)
|
@@ -138,11 +133,3 @@ def init_top_level_scope(model: Model, scope: Scope) -> None:
|
|
138
133
|
def init_builtin_types() -> None:
|
139
134
|
QMODULE.enum_decls |= BUILTIN_ENUM_DECLARATIONS
|
140
135
|
QMODULE.type_decls |= BUILTIN_STRUCT_DECLARATIONS
|
141
|
-
|
142
|
-
|
143
|
-
def _get_shape(classical_type: ConcreteClassicalType) -> tuple[int, ...]:
|
144
|
-
if isinstance(classical_type, ClassicalList):
|
145
|
-
raise ClassiqInternalExpansionError("Unexpected classical list")
|
146
|
-
if isinstance(classical_type, ClassicalArray):
|
147
|
-
return classical_type.size, *_get_shape(classical_type.element_type)
|
148
|
-
return ()
|
@@ -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]
|
@@ -105,19 +125,8 @@ class SymbolicParamInference(ModelVisitor):
|
|
105
125
|
for param in func.positional_arg_declarations:
|
106
126
|
for expr in _get_param_expressions(param):
|
107
127
|
self._process_compile_time_expression(expr.expr)
|
108
|
-
self._set_enums_generative(func)
|
109
128
|
self.visit(func.body)
|
110
129
|
|
111
|
-
def _set_enums_generative(self, decl: AnonQuantumFunctionDeclaration) -> None:
|
112
|
-
for param in decl.positional_arg_declarations:
|
113
|
-
if (
|
114
|
-
isinstance(param, AnonClassicalParameterDeclaration)
|
115
|
-
and param.name is not None
|
116
|
-
and isinstance(param.classical_type, TypeName)
|
117
|
-
and param.classical_type.is_enum
|
118
|
-
):
|
119
|
-
self._scope[param.name].set_generative()
|
120
|
-
|
121
130
|
def visit_QuantumLambdaFunction(self, func: QuantumLambdaFunction) -> None:
|
122
131
|
func.set_op_decl(func.func_decl.model_copy(deep=True))
|
123
132
|
scope = dict(self._scope) | {
|
@@ -131,7 +140,6 @@ class SymbolicParamInference(ModelVisitor):
|
|
131
140
|
)
|
132
141
|
)
|
133
142
|
with self.function_context(None, scope, scope_operands):
|
134
|
-
self._set_enums_generative(func.named_func_decl)
|
135
143
|
self.visit(func.body)
|
136
144
|
|
137
145
|
def visit_QuantumFunctionCall(self, call: QuantumFunctionCall) -> None:
|
@@ -140,14 +148,20 @@ class SymbolicParamInference(ModelVisitor):
|
|
140
148
|
for param, arg in zip_longest(params, call.positional_args):
|
141
149
|
if (
|
142
150
|
not isinstance(param, AnonClassicalParameterDeclaration)
|
143
|
-
or param.classical_type.
|
151
|
+
or param.classical_type.is_purely_generative
|
144
152
|
):
|
145
153
|
self._process_compile_time_expressions(arg)
|
146
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)
|
147
158
|
for expr in _get_expressions(arg):
|
148
159
|
self._process_nested_compile_time_expression(expr.expr)
|
149
160
|
self.generic_visit(call)
|
150
161
|
|
162
|
+
def get_generative_expression_parts(self, expr: str) -> list[str]:
|
163
|
+
return []
|
164
|
+
|
151
165
|
def _get_params(self, call: QuantumFunctionCall) -> Sequence[AnonPositionalArg]:
|
152
166
|
name = call.func_name
|
153
167
|
if name in self._scope_operands:
|
@@ -174,7 +188,7 @@ class SymbolicParamInference(ModelVisitor):
|
|
174
188
|
not isinstance(handle, FieldHandleBinding)
|
175
189
|
or handle.field not in CLASSICAL_ATTRIBUTES
|
176
190
|
):
|
177
|
-
self._scope[handle.name]
|
191
|
+
set_generative_recursively(self._scope[handle.name])
|
178
192
|
|
179
193
|
def _process_nested_compile_time_expression(self, expr: str) -> None:
|
180
194
|
vrc = VarRefCollector(
|
@@ -184,3 +198,5 @@ class SymbolicParamInference(ModelVisitor):
|
|
184
198
|
for handle in vrc.var_handles:
|
185
199
|
for nested_expr in handle.expressions():
|
186
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:
|
@@ -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
|
|
@@ -47,6 +47,7 @@ class ASTToQMODCode(ast.NodeVisitor):
|
|
47
47
|
imports: dict[str, int]
|
48
48
|
symbolic_imports: dict[str, int]
|
49
49
|
decimal_precision: int
|
50
|
+
one_line: bool
|
50
51
|
indent_seq: str = " "
|
51
52
|
|
52
53
|
@property
|
@@ -140,6 +141,10 @@ class ASTToQMODCode(ast.NodeVisitor):
|
|
140
141
|
if not IDENTIFIER.match(field):
|
141
142
|
raise AssertionError("Error parsing struct field access.")
|
142
143
|
return f"{self.visit(node.args[0])}.{field}"
|
144
|
+
elif func == "do_subscript":
|
145
|
+
if len(node.args) != 2:
|
146
|
+
raise AssertionError("Error parsing array subscript.")
|
147
|
+
return f"{self.visit(node.args[0])}[{self.visit(node.args[1])}]"
|
143
148
|
elif func == "struct_literal":
|
144
149
|
if len(node.args) != 1 or not isinstance(node.args[0], ast.Name):
|
145
150
|
raise AssertionError("Error parsing struct literal.")
|
@@ -164,7 +169,7 @@ class ASTToQMODCode(ast.NodeVisitor):
|
|
164
169
|
raise AssertionError("Cannot parse node of type: " + type(node).__name__)
|
165
170
|
|
166
171
|
def indent_items(self, items: Callable[[], list[str]]) -> str:
|
167
|
-
should_indent = (
|
172
|
+
should_indent = not self.one_line and (
|
168
173
|
len("".join([i.strip() for i in items()])) >= LIST_FORMAT_CHAR_LIMIT
|
169
174
|
)
|
170
175
|
if should_indent:
|
@@ -213,10 +218,12 @@ def transform_expression(
|
|
213
218
|
symbolic_imports: dict[str, int],
|
214
219
|
level: int = 0,
|
215
220
|
decimal_precision: int = DEFAULT_DECIMAL_PRECISION,
|
221
|
+
one_line: bool = False,
|
216
222
|
) -> str:
|
217
223
|
return ASTToQMODCode(
|
218
224
|
level=level,
|
219
225
|
decimal_precision=decimal_precision,
|
220
226
|
imports=imports,
|
227
|
+
one_line=one_line,
|
221
228
|
symbolic_imports=symbolic_imports,
|
222
229
|
).visit(ast.parse(expr))
|
@@ -10,6 +10,7 @@ from classiq.interface.generator.functions.classical_type import (
|
|
10
10
|
Bool,
|
11
11
|
ClassicalArray,
|
12
12
|
ClassicalList,
|
13
|
+
ClassicalTuple,
|
13
14
|
Integer,
|
14
15
|
Real,
|
15
16
|
)
|
@@ -345,6 +346,12 @@ class PythonPrettyPrinter(ModelVisitor):
|
|
345
346
|
self._imports["CArray"] = 1
|
346
347
|
return f"CArray[{self.visit(ctarray.element_type)}, {ctarray.size}]"
|
347
348
|
|
349
|
+
def visit_ClassicalTuple(self, classical_tuple: ClassicalTuple) -> str:
|
350
|
+
raw_type = classical_tuple.get_raw_type()
|
351
|
+
if isinstance(raw_type, ClassicalTuple):
|
352
|
+
raise ClassiqInternalError("Empty tuple pretty-print not supported")
|
353
|
+
return self.visit(raw_type)
|
354
|
+
|
348
355
|
def visit_TypeName(self, type_: TypeName) -> str:
|
349
356
|
self._import_type_name(type_)
|
350
357
|
return type_.name
|
@@ -62,7 +62,7 @@ class PythonClassicalType:
|
|
62
62
|
return self.register_struct(py_type)
|
63
63
|
elif inspect.isclass(py_type) and isinstance(py_type, EnumMeta):
|
64
64
|
self.register_enum(py_type)
|
65
|
-
return Enum(name=py_type.__name__)
|
65
|
+
return Enum(name=py_type.__name__).set_generative()
|
66
66
|
elif py_type in (CArray, list):
|
67
67
|
raise ClassiqValueError(CARRAY_ERROR_MESSAGE)
|
68
68
|
return None
|
classiq/qmod/qmod_parameter.py
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
from typing import TYPE_CHECKING, Any, Union
|
2
2
|
|
3
|
-
from classiq.interface.exceptions import ClassiqValueError
|
3
|
+
from classiq.interface.exceptions import ClassiqInternalError, ClassiqValueError
|
4
4
|
from classiq.interface.generator.functions.classical_type import (
|
5
5
|
Bool,
|
6
6
|
ClassicalArray,
|
7
7
|
ClassicalList,
|
8
|
+
ClassicalTuple,
|
8
9
|
ClassicalType,
|
9
10
|
Integer,
|
10
11
|
Real,
|
@@ -41,7 +42,7 @@ class CParamList(CParam):
|
|
41
42
|
def __init__(
|
42
43
|
self,
|
43
44
|
expr: str,
|
44
|
-
list_type: Union[ClassicalList, ClassicalArray],
|
45
|
+
list_type: Union[ClassicalList, ClassicalArray, ClassicalTuple],
|
45
46
|
qmodule: ModelStateContainer,
|
46
47
|
) -> None:
|
47
48
|
super().__init__(expr)
|
@@ -49,9 +50,35 @@ class CParamList(CParam):
|
|
49
50
|
self._list_type = list_type
|
50
51
|
|
51
52
|
def __getitem__(self, key: Any) -> CParam:
|
52
|
-
param_type
|
53
|
-
if isinstance(key, slice):
|
54
|
-
|
53
|
+
param_type: ClassicalType
|
54
|
+
if not isinstance(key, slice):
|
55
|
+
if isinstance(self._list_type, ClassicalTuple):
|
56
|
+
if isinstance(key, int) and 0 <= key < len(
|
57
|
+
self._list_type.element_types
|
58
|
+
):
|
59
|
+
param_type = self._list_type.element_types[key]
|
60
|
+
elif len(self._list_type.element_types) == 0:
|
61
|
+
raise ClassiqValueError("Array is empty")
|
62
|
+
else:
|
63
|
+
param_type = self._list_type.element_types[0].get_raw_type()
|
64
|
+
else:
|
65
|
+
param_type = self._list_type.element_type
|
66
|
+
else:
|
67
|
+
if not isinstance(self._list_type, ClassicalTuple):
|
68
|
+
param_type = self._list_type
|
69
|
+
else:
|
70
|
+
if (
|
71
|
+
(isinstance(key.start, int) or key.start is None)
|
72
|
+
and (isinstance(key.stop, int) or key.stop is None)
|
73
|
+
and (isinstance(key.step, int) or key.step is None)
|
74
|
+
):
|
75
|
+
param_type = ClassicalTuple(
|
76
|
+
element_types=self._list_type.element_types.__getitem__(key)
|
77
|
+
)
|
78
|
+
elif len(self._list_type.element_types) == 0:
|
79
|
+
param_type = self._list_type
|
80
|
+
else:
|
81
|
+
param_type = self._list_type.element_types[0].get_raw_type()
|
55
82
|
start = key.start if key.start is not None else ""
|
56
83
|
stop = key.stop if key.stop is not None else ""
|
57
84
|
if key.step is not None:
|
@@ -110,7 +137,7 @@ class CParamStruct(CParam):
|
|
110
137
|
|
111
138
|
return create_param(
|
112
139
|
f"get_field({variable_name},{field_name!r})",
|
113
|
-
field_type,
|
140
|
+
field_type.model_copy(deep=True),
|
114
141
|
qmodule=qmodule,
|
115
142
|
)
|
116
143
|
|
@@ -118,7 +145,11 @@ class CParamStruct(CParam):
|
|
118
145
|
def create_param(
|
119
146
|
expr_str: str, ctype: ClassicalType, qmodule: ModelStateContainer
|
120
147
|
) -> CParam:
|
121
|
-
if isinstance(ctype,
|
148
|
+
if isinstance(ctype, TypeName) and ctype.has_classical_struct_decl:
|
149
|
+
decl = ctype.classical_struct_decl
|
150
|
+
ctype = Struct(name=ctype.name)
|
151
|
+
ctype.set_classical_struct_decl(decl)
|
152
|
+
if isinstance(ctype, (ClassicalList, ClassicalArray, ClassicalTuple)):
|
122
153
|
return CParamList(expr_str, ctype, qmodule=qmodule)
|
123
154
|
elif isinstance(ctype, Struct):
|
124
155
|
return CParamStruct(expr_str, ctype, qmodule=qmodule)
|
@@ -137,6 +168,11 @@ def get_qmod_type(ctype: ClassicalType) -> type:
|
|
137
168
|
return CArray[get_qmod_type(ctype.element_type)] # type: ignore[misc]
|
138
169
|
elif isinstance(ctype, ClassicalArray):
|
139
170
|
return CArray[get_qmod_type(ctype.element_type), ctype.size] # type: ignore[misc]
|
171
|
+
elif isinstance(ctype, ClassicalTuple):
|
172
|
+
raw_type = ctype.get_raw_type()
|
173
|
+
if isinstance(raw_type, ClassicalTuple):
|
174
|
+
raise ClassiqInternalError("Tuple is empty")
|
175
|
+
return get_qmod_type(raw_type)
|
140
176
|
elif isinstance(ctype, TypeName):
|
141
177
|
type_ = type(ctype.name, (TypeName,), dict())
|
142
178
|
if isinstance(ctype, Struct):
|