classiq 0.70.0__py3-none-any.whl → 0.71.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/debug_info/debug_info.py +4 -0
- classiq/interface/generator/expressions/expression_constants.py +0 -3
- classiq/interface/generator/expressions/expression_types.py +4 -2
- classiq/interface/generator/expressions/proxies/classical/classical_array_proxy.py +4 -0
- classiq/interface/generator/expressions/proxies/classical/classical_struct_proxy.py +5 -1
- classiq/interface/generator/expressions/proxies/classical/utils.py +34 -0
- classiq/interface/generator/functions/classical_type.py +1 -1
- classiq/interface/generator/functions/type_name.py +16 -0
- classiq/interface/generator/functions/type_qualifier.py +7 -0
- classiq/interface/generator/generated_circuit_data.py +1 -1
- classiq/interface/generator/quantum_function_call.py +8 -1
- classiq/interface/generator/synthesis_execution_parameter.py +1 -0
- classiq/interface/generator/types/compilation_metadata.py +1 -0
- classiq/interface/ide/visual_model.py +1 -0
- classiq/interface/interface_version.py +1 -1
- classiq/interface/model/allocate.py +7 -0
- classiq/interface/model/block.py +12 -0
- classiq/interface/model/classical_if.py +4 -0
- classiq/interface/model/inplace_binary_operation.py +4 -0
- classiq/interface/model/model.py +3 -1
- classiq/interface/model/phase_operation.py +4 -0
- classiq/interface/model/port_declaration.py +3 -0
- classiq/interface/model/power.py +4 -0
- classiq/interface/model/quantum_expressions/quantum_expression.py +4 -0
- classiq/interface/model/quantum_function_call.py +4 -0
- classiq/interface/model/quantum_function_declaration.py +1 -1
- classiq/interface/model/quantum_statement.py +5 -0
- classiq/interface/model/quantum_type.py +22 -0
- classiq/interface/model/repeat.py +4 -0
- classiq/interface/model/statement_block.py +3 -0
- classiq/interface/model/variable_declaration_statement.py +5 -0
- classiq/model_expansions/capturing/captured_vars.py +156 -34
- classiq/model_expansions/evaluators/classical_type_inference.py +70 -0
- classiq/model_expansions/evaluators/parameter_types.py +14 -0
- classiq/model_expansions/expression_evaluator.py +0 -11
- classiq/model_expansions/function_builder.py +2 -8
- classiq/model_expansions/generative_functions.py +7 -30
- classiq/model_expansions/interpreters/base_interpreter.py +3 -4
- classiq/model_expansions/quantum_operations/call_emitter.py +46 -6
- classiq/model_expansions/quantum_operations/emitter.py +41 -0
- classiq/model_expansions/quantum_operations/expression_evaluator.py +4 -0
- classiq/model_expansions/scope.py +0 -8
- classiq/model_expansions/scope_initialization.py +20 -28
- classiq/qmod/__init__.py +3 -1
- classiq/qmod/declaration_inferrer.py +52 -24
- classiq/qmod/native/pretty_printer.py +25 -3
- classiq/qmod/pretty_print/pretty_printer.py +31 -14
- classiq/qmod/python_classical_type.py +12 -1
- classiq/qmod/qfunc.py +33 -8
- classiq/qmod/qmod_variable.py +188 -147
- classiq/qmod/quantum_function.py +3 -4
- classiq/qmod/semantics/validation/type_hints.py +19 -10
- {classiq-0.70.0.dist-info → classiq-0.71.0.dist-info}/METADATA +1 -1
- {classiq-0.70.0.dist-info → classiq-0.71.0.dist-info}/RECORD +56 -52
- {classiq-0.70.0.dist-info → classiq-0.71.0.dist-info}/WHEEL +0 -0
@@ -2,17 +2,29 @@ from collections.abc import Sequence
|
|
2
2
|
from itertools import chain, combinations
|
3
3
|
from typing import (
|
4
4
|
TYPE_CHECKING,
|
5
|
+
Any,
|
5
6
|
Generic,
|
6
7
|
cast,
|
7
8
|
)
|
8
9
|
from uuid import UUID
|
9
10
|
|
11
|
+
import sympy
|
12
|
+
|
10
13
|
from classiq.interface.debug_info.debug_info import FunctionDebugInfo
|
11
14
|
from classiq.interface.exceptions import ClassiqExpansionError
|
15
|
+
from classiq.interface.generator.expressions.proxies.classical.classical_proxy import (
|
16
|
+
ClassicalProxy,
|
17
|
+
)
|
18
|
+
from classiq.interface.generator.expressions.proxies.classical.qmod_struct_instance import (
|
19
|
+
QmodStructInstance,
|
20
|
+
)
|
12
21
|
from classiq.interface.generator.functions.port_declaration import (
|
13
22
|
PortDeclarationDirection,
|
14
23
|
)
|
15
24
|
from classiq.interface.generator.generated_circuit_data import OperationLevel
|
25
|
+
from classiq.interface.model.classical_parameter_declaration import (
|
26
|
+
ClassicalParameterDeclaration,
|
27
|
+
)
|
16
28
|
from classiq.interface.model.handle_binding import HandleBinding
|
17
29
|
from classiq.interface.model.native_function_definition import NativeFunctionDefinition
|
18
30
|
from classiq.interface.model.port_declaration import PortDeclaration
|
@@ -75,6 +87,16 @@ def _validate_cloning(evaluated_args: list[Evaluated]) -> None:
|
|
75
87
|
)
|
76
88
|
|
77
89
|
|
90
|
+
def _is_symbolic(arg: Any) -> bool:
|
91
|
+
if isinstance(arg, list):
|
92
|
+
return any(_is_symbolic(item) for item in arg)
|
93
|
+
if isinstance(arg, QmodStructInstance):
|
94
|
+
return any(_is_symbolic(item) for item in arg.fields.values())
|
95
|
+
if isinstance(arg, sympy.Basic):
|
96
|
+
return len(arg.free_symbols) > 0
|
97
|
+
return isinstance(arg, ClassicalProxy)
|
98
|
+
|
99
|
+
|
78
100
|
class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSplitter):
|
79
101
|
def __init__(self, interpreter: "BaseInterpreter") -> None:
|
80
102
|
Emitter.__init__(self, interpreter)
|
@@ -197,7 +219,7 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
|
|
197
219
|
].occurrences_number += 1
|
198
220
|
return function_def
|
199
221
|
|
200
|
-
function_def = self._create_function_definition(function_context)
|
222
|
+
function_def = self._create_function_definition(function_context, args)
|
201
223
|
self._expanded_functions[closure_id] = function_def
|
202
224
|
self._top_level_scope[function_def.name] = Evaluated(
|
203
225
|
value=function_context.closure.with_new_declaration(function_def)
|
@@ -210,14 +232,23 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
|
|
210
232
|
return function_def
|
211
233
|
|
212
234
|
def _create_function_definition(
|
213
|
-
self, function_context: FunctionContext
|
235
|
+
self, function_context: FunctionContext, args: list[Evaluated]
|
214
236
|
) -> NativeFunctionDefinition:
|
215
|
-
|
237
|
+
params = [
|
238
|
+
param
|
239
|
+
for arg, param in zip(args, function_context.positional_arg_declarations)
|
240
|
+
if isinstance(param, PortDeclaration)
|
241
|
+
or (
|
242
|
+
isinstance(param, ClassicalParameterDeclaration)
|
243
|
+
and _is_symbolic(arg.value)
|
244
|
+
)
|
245
|
+
]
|
246
|
+
func_def = self._builder.create_definition(function_context, params)
|
216
247
|
|
217
248
|
captured_vars = function_context.closure.captured_vars.filter_vars(
|
218
249
|
function_context.closure
|
219
250
|
)
|
220
|
-
captured_ports = captured_vars.
|
251
|
+
captured_ports = captured_vars.get_captured_parameters()
|
221
252
|
if len(captured_ports) == 0:
|
222
253
|
return func_def
|
223
254
|
func_def.positional_arg_declarations = list(
|
@@ -239,15 +270,22 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
|
|
239
270
|
closure: FunctionClosure,
|
240
271
|
) -> None:
|
241
272
|
for parameter, argument in zip(parameters, arguments):
|
273
|
+
param_handle = HandleBinding(name=parameter.name)
|
242
274
|
if isinstance(argument.value, QuantumSymbol):
|
243
275
|
assert isinstance(parameter, PortDeclaration)
|
244
276
|
closure.scope[parameter.name] = Evaluated(
|
245
277
|
QuantumSymbol(
|
246
|
-
handle=
|
278
|
+
handle=param_handle,
|
247
279
|
quantum_type=parameter.quantum_type,
|
248
280
|
),
|
249
281
|
defining_function=closure,
|
250
282
|
)
|
283
|
+
elif _is_symbolic(argument.value):
|
284
|
+
assert isinstance(parameter, ClassicalParameterDeclaration)
|
285
|
+
closure.scope[parameter.name] = Evaluated(
|
286
|
+
value=parameter.classical_type.get_classical_proxy(param_handle),
|
287
|
+
defining_function=closure,
|
288
|
+
)
|
251
289
|
else:
|
252
290
|
closure.scope[parameter.name] = argument
|
253
291
|
|
@@ -264,7 +302,9 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
|
|
264
302
|
return [arg.emit() for arg in evaluated_args]
|
265
303
|
|
266
304
|
positional_args = [
|
267
|
-
arg.emit()
|
305
|
+
arg.emit()
|
306
|
+
for arg in evaluated_args
|
307
|
+
if isinstance(arg.value, QuantumSymbol) or _is_symbolic(arg.value)
|
268
308
|
]
|
269
309
|
|
270
310
|
return positional_args
|
@@ -19,6 +19,13 @@ from classiq.interface.generator.expressions.evaluated_expression import (
|
|
19
19
|
EvaluatedExpression,
|
20
20
|
)
|
21
21
|
from classiq.interface.generator.expressions.expression import Expression
|
22
|
+
from classiq.interface.generator.expressions.proxies.classical.classical_proxy import (
|
23
|
+
ClassicalProxy,
|
24
|
+
)
|
25
|
+
from classiq.interface.generator.expressions.proxies.classical.utils import (
|
26
|
+
get_proxy_type,
|
27
|
+
)
|
28
|
+
from classiq.interface.generator.functions.classical_type import ClassicalType
|
22
29
|
from classiq.interface.generator.functions.port_declaration import (
|
23
30
|
PortDeclarationDirection,
|
24
31
|
)
|
@@ -145,6 +152,7 @@ class Emitter(Generic[QuantumStatementT], ABC):
|
|
145
152
|
return new_expression
|
146
153
|
|
147
154
|
def emit_statement(self, statement: QuantumStatement) -> None:
|
155
|
+
self._update_captured_classical_vars(statement)
|
148
156
|
if isinstance(statement, QuantumOperation):
|
149
157
|
self._update_captured_vars(statement)
|
150
158
|
if statement.uuid not in self._interpreter._model.debug_info:
|
@@ -153,6 +161,11 @@ class Emitter(Generic[QuantumStatementT], ABC):
|
|
153
161
|
)
|
154
162
|
self._builder.emit_statement(statement)
|
155
163
|
|
164
|
+
def _update_captured_classical_vars(self, stmt: QuantumStatement) -> None:
|
165
|
+
for expr in stmt.expressions:
|
166
|
+
for var_name, var_type in self._get_classical_vars_in_expression(expr):
|
167
|
+
self._capture_classical_var(var_name, var_type)
|
168
|
+
|
156
169
|
def _update_captured_vars(self, op: QuantumOperation) -> None:
|
157
170
|
handles = (
|
158
171
|
[(handle, PortDeclarationDirection.Input) for handle in op.inputs]
|
@@ -178,6 +191,18 @@ class Emitter(Generic[QuantumStatementT], ABC):
|
|
178
191
|
direction=direction,
|
179
192
|
)
|
180
193
|
|
194
|
+
def _capture_classical_var(self, var_name: str, var_type: ClassicalType) -> None:
|
195
|
+
if var_name not in self._current_scope:
|
196
|
+
return
|
197
|
+
defining_function = self._current_scope[var_name].defining_function
|
198
|
+
if defining_function is None:
|
199
|
+
raise ClassiqInternalExpansionError
|
200
|
+
self._builder.current_block.captured_vars.capture_classical_var(
|
201
|
+
var_name=var_name,
|
202
|
+
var_type=var_type,
|
203
|
+
defining_function=defining_function,
|
204
|
+
)
|
205
|
+
|
181
206
|
def _get_symbols_in_expression(self, expr: Expression) -> list[QuantumSymbol]:
|
182
207
|
vrc = VarRefCollector(ignore_duplicated_handles=True)
|
183
208
|
vrc.visit(ast.parse(expr.expr))
|
@@ -187,3 +212,19 @@ class Emitter(Generic[QuantumStatementT], ABC):
|
|
187
212
|
if isinstance(self._current_scope[handle.name].value, QuantumSymbol)
|
188
213
|
)
|
189
214
|
return [self._interpreter.evaluate(handle).value for handle in handles]
|
215
|
+
|
216
|
+
def _get_classical_vars_in_expression(
|
217
|
+
self, expr: Expression
|
218
|
+
) -> list[tuple[str, ClassicalType]]:
|
219
|
+
vrc = VarRefCollector(ignore_duplicated_handles=True, ignore_sympy_symbols=True)
|
220
|
+
vrc.visit(ast.parse(expr.expr))
|
221
|
+
return list(
|
222
|
+
{
|
223
|
+
handle.name: get_proxy_type(proxy)
|
224
|
+
for handle in vrc.var_handles
|
225
|
+
if handle.name in self._current_scope
|
226
|
+
and isinstance(
|
227
|
+
proxy := self._current_scope[handle.name].value, ClassicalProxy
|
228
|
+
)
|
229
|
+
}.items()
|
230
|
+
)
|
@@ -26,6 +26,10 @@ class ExpressionEvaluator(Emitter[QuantumOperation]):
|
|
26
26
|
)
|
27
27
|
for symbol in self._get_symbols_in_expression(evaluated_expression):
|
28
28
|
self._capture_handle(symbol.handle, PortDeclarationDirection.Inout)
|
29
|
+
for var_name, var_type in self._get_classical_vars_in_expression(
|
30
|
+
evaluated_expression
|
31
|
+
):
|
32
|
+
self._capture_classical_var(var_name, var_type)
|
29
33
|
op = op.model_copy(
|
30
34
|
update={self._expression_name: evaluated_expression, "back_ref": op.uuid}
|
31
35
|
)
|
@@ -1,13 +1,10 @@
|
|
1
1
|
import itertools
|
2
|
-
import re
|
3
2
|
from collections import UserDict
|
4
3
|
from collections.abc import Iterator
|
5
4
|
from dataclasses import dataclass
|
6
5
|
from functools import singledispatch
|
7
6
|
from typing import TYPE_CHECKING, Any, Optional, TypeVar, Union
|
8
7
|
|
9
|
-
from sympy import Symbol
|
10
|
-
|
11
8
|
from classiq.interface.exceptions import (
|
12
9
|
ClassiqExpansionError,
|
13
10
|
ClassiqInternalExpansionError,
|
@@ -16,9 +13,6 @@ from classiq.interface.generator.expressions.evaluated_expression import (
|
|
16
13
|
EvaluatedExpression,
|
17
14
|
)
|
18
15
|
from classiq.interface.generator.expressions.expression import Expression
|
19
|
-
from classiq.interface.generator.expressions.expression_constants import (
|
20
|
-
CPARAM_EXECUTION_SUFFIX_PATTERN,
|
21
|
-
)
|
22
16
|
from classiq.interface.generator.expressions.proxies.classical.qmod_struct_instance import (
|
23
17
|
QmodStructInstance,
|
24
18
|
)
|
@@ -204,8 +198,6 @@ class Scope(EvaluatedUserDict):
|
|
204
198
|
return self.data[name]
|
205
199
|
if self._parent is not None:
|
206
200
|
return self._parent[name]
|
207
|
-
if re.search(CPARAM_EXECUTION_SUFFIX_PATTERN, name):
|
208
|
-
return Evaluated(value=Symbol(name))
|
209
201
|
raise ClassiqExpansionError(f"Variable {name!r} is undefined")
|
210
202
|
|
211
203
|
def __contains__(self, item: Any) -> bool:
|
@@ -7,6 +7,9 @@ from classiq.interface.generator.functions.classical_type import (
|
|
7
7
|
ClassicalList,
|
8
8
|
)
|
9
9
|
from classiq.interface.generator.functions.concrete_types import ConcreteClassicalType
|
10
|
+
from classiq.interface.model.classical_parameter_declaration import (
|
11
|
+
ClassicalParameterDeclaration,
|
12
|
+
)
|
10
13
|
from classiq.interface.model.handle_binding import HandleBinding
|
11
14
|
from classiq.interface.model.model import MAIN_FUNCTION_NAME, Model
|
12
15
|
from classiq.interface.model.native_function_definition import NativeFunctionDefinition
|
@@ -102,17 +105,24 @@ def add_entry_point_params_to_scope(
|
|
102
105
|
parameters: Sequence[PositionalArg], main_closure: FunctionClosure
|
103
106
|
) -> None:
|
104
107
|
for parameter in parameters:
|
105
|
-
if
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
108
|
+
if isinstance(parameter, PortDeclaration):
|
109
|
+
main_closure.scope[parameter.name] = Evaluated(
|
110
|
+
value=QuantumSymbol(
|
111
|
+
handle=HandleBinding(name=parameter.name),
|
112
|
+
quantum_type=evaluate_type_in_quantum_symbol(
|
113
|
+
parameter.quantum_type, main_closure.scope, parameter.name
|
114
|
+
),
|
112
115
|
),
|
113
|
-
|
114
|
-
|
115
|
-
)
|
116
|
+
defining_function=main_closure,
|
117
|
+
)
|
118
|
+
elif isinstance(parameter, ClassicalParameterDeclaration):
|
119
|
+
param_val = parameter.classical_type.get_classical_proxy(
|
120
|
+
handle=HandleBinding(name=parameter.name)
|
121
|
+
)
|
122
|
+
main_closure.scope[parameter.name] = Evaluated(
|
123
|
+
value=param_val,
|
124
|
+
defining_function=main_closure,
|
125
|
+
)
|
116
126
|
|
117
127
|
|
118
128
|
def init_top_level_scope(model: Model, scope: Scope) -> None:
|
@@ -126,24 +136,6 @@ def init_builtin_types() -> None:
|
|
126
136
|
QMODULE.type_decls |= BUILTIN_STRUCT_DECLARATIONS
|
127
137
|
|
128
138
|
|
129
|
-
def init_exec_params(model: Model, scope: Scope) -> dict[str, ConcreteClassicalType]:
|
130
|
-
if model.execution_parameters is not None:
|
131
|
-
exec_params = model.execution_parameters
|
132
|
-
else:
|
133
|
-
exec_params = {
|
134
|
-
param.name: param.classical_type
|
135
|
-
for param in model.function_dict.get(
|
136
|
-
"_dec_main", model.main_func
|
137
|
-
).param_decls
|
138
|
-
}
|
139
|
-
for param_name, param_type in exec_params.items():
|
140
|
-
param_val = param_type.get_classical_proxy(
|
141
|
-
handle=HandleBinding(name=param_name)
|
142
|
-
)
|
143
|
-
scope[param_name] = Evaluated(value=param_val)
|
144
|
-
return exec_params
|
145
|
-
|
146
|
-
|
147
139
|
def _get_shape(classical_type: ConcreteClassicalType) -> tuple[int, ...]:
|
148
140
|
if isinstance(classical_type, ClassicalList):
|
149
141
|
raise ClassiqInternalExpansionError("Unexpected classical list")
|
classiq/qmod/__init__.py
CHANGED
@@ -6,7 +6,7 @@ from .expression_query import get_expression_numeric_attributes
|
|
6
6
|
from .qfunc import qfunc
|
7
7
|
from .qmod_constant import QConstant
|
8
8
|
from .qmod_parameter import Array, CArray, CBool, CInt, CReal
|
9
|
-
from .qmod_variable import Input, Output, QArray, QBit, QNum, QStruct
|
9
|
+
from .qmod_variable import Const, Input, Output, QArray, QBit, QFree, QNum, QStruct
|
10
10
|
from .quantum_callable import QCallable, QCallableList
|
11
11
|
from .write_qmod import write_qmod
|
12
12
|
|
@@ -18,6 +18,8 @@ __all__ = [
|
|
18
18
|
"CReal",
|
19
19
|
"Input",
|
20
20
|
"Output",
|
21
|
+
"Const",
|
22
|
+
"QFree",
|
21
23
|
"QArray",
|
22
24
|
"QBit",
|
23
25
|
"QNum",
|
@@ -19,6 +19,7 @@ from classiq.interface.generator.functions.concrete_types import ConcreteClassic
|
|
19
19
|
from classiq.interface.generator.functions.port_declaration import (
|
20
20
|
PortDeclarationDirection,
|
21
21
|
)
|
22
|
+
from classiq.interface.generator.functions.type_qualifier import TypeQualifier
|
22
23
|
from classiq.interface.generator.types.enum_declaration import declaration_from_enum
|
23
24
|
from classiq.interface.generator.types.struct_declaration import StructDeclaration
|
24
25
|
from classiq.interface.model.classical_parameter_declaration import (
|
@@ -36,7 +37,7 @@ from classiq.qmod.builtins.enums import BUILTIN_ENUM_DECLARATIONS
|
|
36
37
|
from classiq.qmod.builtins.structs import BUILTIN_STRUCT_DECLARATIONS
|
37
38
|
from classiq.qmod.model_state_container import ModelStateContainer
|
38
39
|
from classiq.qmod.python_classical_type import PythonClassicalType
|
39
|
-
from classiq.qmod.qmod_variable import QVar
|
40
|
+
from classiq.qmod.qmod_variable import QVar, get_port_from_type_hint
|
40
41
|
from classiq.qmod.quantum_callable import QCallableList
|
41
42
|
from classiq.qmod.semantics.validation.type_hints import validate_annotation
|
42
43
|
from classiq.qmod.utilities import unmangle_keyword, version_portable_get_args
|
@@ -85,15 +86,12 @@ def python_type_to_qmod(
|
|
85
86
|
|
86
87
|
|
87
88
|
def _extract_port_decl(name: Optional[str], py_type: Any) -> AnonPortDeclaration:
|
88
|
-
|
89
|
-
qtype: type[QVar] = QVar.from_type_hint(py_type) # type:ignore[assignment]
|
90
|
-
direction = qtype.port_direction(py_type)
|
91
|
-
if isinstance(py_type, _AnnotatedAlias):
|
92
|
-
py_type = py_type.__args__[0]
|
89
|
+
quantum_type, direction, qualifier = get_port_from_type_hint(py_type)
|
93
90
|
param = AnonPortDeclaration(
|
94
91
|
name=None,
|
95
92
|
direction=direction,
|
96
|
-
quantum_type=
|
93
|
+
quantum_type=quantum_type,
|
94
|
+
type_qualifier=qualifier,
|
97
95
|
)
|
98
96
|
if name is not None:
|
99
97
|
param = param.rename(name)
|
@@ -126,23 +124,45 @@ def _extract_operand_decl(
|
|
126
124
|
def _extract_operand_param(py_type: Any) -> tuple[Optional[str], Any]:
|
127
125
|
if get_origin(py_type) is not Annotated:
|
128
126
|
return None, py_type
|
127
|
+
|
129
128
|
args = get_args(py_type)
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
129
|
+
_validate_annotations(args, py_type)
|
130
|
+
param_name = _get_param_name(args)
|
131
|
+
|
132
|
+
if param_name is None:
|
133
|
+
if len(args) > 1:
|
134
|
+
return None, _unpacked_annotated(args[0], args[1:])
|
135
|
+
return None, args[0]
|
136
|
+
|
137
|
+
if len(args) > 2:
|
138
|
+
return param_name, _unpacked_annotated(args[0], args[1:-1])
|
139
|
+
return param_name, args[0]
|
140
|
+
|
141
|
+
|
142
|
+
def _unpacked_annotated(arg_0: Any, args: Any) -> _AnnotatedAlias:
|
143
|
+
return Annotated.__class_getitem__((arg_0, *args)) # type:ignore[attr-defined]
|
144
|
+
|
145
|
+
|
146
|
+
def _get_param_name(py_type_args: Any) -> Optional[str]:
|
147
|
+
if isinstance(py_type_args[-1], str) and not isinstance(
|
148
|
+
py_type_args[-1], (PortDeclarationDirection, TypeQualifier)
|
149
|
+
):
|
150
|
+
return py_type_args[-1]
|
151
|
+
elif py_type_args[-1] is Literal:
|
152
|
+
return str(version_portable_get_args(py_type_args[-1])[0]) # type: ignore[arg-type]
|
153
|
+
else:
|
154
|
+
return None
|
155
|
+
|
156
|
+
|
157
|
+
def _validate_annotations(py_type_args: Any, py_type: Any) -> None:
|
158
|
+
for arg in py_type_args[1:-1]:
|
159
|
+
if (
|
160
|
+
isinstance(arg, str) and not isinstance(arg, PortDeclarationDirection)
|
161
|
+
) or arg is Literal:
|
162
|
+
raise ClassiqValueError(
|
163
|
+
f"Operand parameter declaration must be of the form <param-type> or "
|
164
|
+
f"Annotated[<param-type>, <param-name>]. Got {py_type}"
|
165
|
+
)
|
146
166
|
|
147
167
|
|
148
168
|
@overload
|
@@ -177,7 +197,7 @@ def _extract_positional_args(
|
|
177
197
|
if name is not None:
|
178
198
|
param = param.rename(name)
|
179
199
|
result.append(param)
|
180
|
-
elif
|
200
|
+
elif is_qvar(py_type):
|
181
201
|
result.append(_extract_port_decl(name, py_type))
|
182
202
|
else:
|
183
203
|
result.append(_extract_operand_decl(name, py_type, qmodule=qmodule))
|
@@ -193,3 +213,11 @@ def infer_func_decl(
|
|
193
213
|
list(py_func.__annotations__.items()), qmodule=qmodule
|
194
214
|
),
|
195
215
|
)
|
216
|
+
|
217
|
+
|
218
|
+
def is_qvar(type_hint: Any) -> Any:
|
219
|
+
non_annotated_type = (
|
220
|
+
type_hint.__origin__ if isinstance(type_hint, _AnnotatedAlias) else type_hint
|
221
|
+
)
|
222
|
+
type_ = get_origin(non_annotated_type) or non_annotated_type
|
223
|
+
return issubclass(type_, QVar)
|
@@ -18,6 +18,8 @@ from classiq.interface.generator.functions.port_declaration import (
|
|
18
18
|
PortDeclarationDirection,
|
19
19
|
)
|
20
20
|
from classiq.interface.generator.functions.type_name import TypeName
|
21
|
+
from classiq.interface.generator.functions.type_qualifier import TypeQualifier
|
22
|
+
from classiq.interface.generator.types.compilation_metadata import CompilationMetadata
|
21
23
|
from classiq.interface.generator.types.enum_declaration import EnumDeclaration
|
22
24
|
from classiq.interface.generator.types.qstruct_declaration import QStructDeclaration
|
23
25
|
from classiq.interface.generator.types.struct_declaration import StructDeclaration
|
@@ -95,6 +97,7 @@ class DSLPrettyPrinter(ModelVisitor):
|
|
95
97
|
self._level = 0
|
96
98
|
self._decimal_precision = decimal_precision
|
97
99
|
self._emit_open_lib_functions = emit_open_lib_functions
|
100
|
+
self._compilation_metadata: dict[str, CompilationMetadata] = {}
|
98
101
|
|
99
102
|
def visit(self, node: NodeType) -> str:
|
100
103
|
res = super().visit(node)
|
@@ -105,6 +108,8 @@ class DSLPrettyPrinter(ModelVisitor):
|
|
105
108
|
def visit_Model(self, model: Model) -> str:
|
106
109
|
# FIXME - CAD-20149: Remove this line once the froggies are removed, and the visit of lambdas can be done without accessing the func_decl property (with rename_params values only).
|
107
110
|
resolve_function_calls(model, model.function_dict)
|
111
|
+
self._compilation_metadata = model.functions_compilation_metadata
|
112
|
+
|
108
113
|
enum_decls = [self.visit(enum_decl) for enum_decl in model.enums]
|
109
114
|
struct_decls = [self.visit(struct_decl) for struct_decl in model.types]
|
110
115
|
qstruct_decls = [self.visit(qstruct_decl) for qstruct_decl in model.qstructs]
|
@@ -135,10 +140,20 @@ class DSLPrettyPrinter(ModelVisitor):
|
|
135
140
|
)
|
136
141
|
return f"({positional_args})"
|
137
142
|
|
143
|
+
def _get_decl_string(self, func_decl: QuantumFunctionDeclaration) -> str:
|
144
|
+
no_qualifiers_decl = "qfunc"
|
145
|
+
if func_decl.name not in self._compilation_metadata:
|
146
|
+
return no_qualifiers_decl
|
147
|
+
atomic_qualifiers = self._compilation_metadata[func_decl.name].atomic_qualifiers
|
148
|
+
if len(atomic_qualifiers) == 0:
|
149
|
+
return no_qualifiers_decl
|
150
|
+
return f"atomic_qualifiers ({', '.join(atomic_qualifiers)})\n" f"qfunc"
|
151
|
+
|
138
152
|
def visit_QuantumFunctionDeclaration(
|
139
153
|
self, func_decl: QuantumFunctionDeclaration
|
140
154
|
) -> str:
|
141
|
-
|
155
|
+
qfunc_decl = self._get_decl_string(func_decl)
|
156
|
+
return f"{qfunc_decl} {func_decl.name}{self._visit_arg_decls(func_decl)}"
|
142
157
|
|
143
158
|
def visit_EnumDeclaration(self, enum_decl: EnumDeclaration) -> str:
|
144
159
|
return f"enum {enum_decl.name} {{\n{self._visit_members(enum_decl.members)}}}\n"
|
@@ -175,13 +190,20 @@ class DSLPrettyPrinter(ModelVisitor):
|
|
175
190
|
return f"{var_decl.name}: {self.visit(var_decl.quantum_type)}"
|
176
191
|
|
177
192
|
def visit_AnonPortDeclaration(self, port_decl: AnonPortDeclaration) -> str:
|
193
|
+
qualifier_str = (
|
194
|
+
f"{port_decl.type_qualifier} "
|
195
|
+
if port_decl.type_qualifier is not TypeQualifier.Quantum
|
196
|
+
else ""
|
197
|
+
)
|
178
198
|
dir_str = (
|
179
199
|
f"{port_decl.direction} "
|
180
|
-
if port_decl.direction
|
200
|
+
if port_decl.direction is not PortDeclarationDirection.Inout
|
181
201
|
else ""
|
182
202
|
)
|
183
203
|
param_name = f"{port_decl.name}: " if port_decl.name is not None else ""
|
184
|
-
return
|
204
|
+
return (
|
205
|
+
f"{qualifier_str}{dir_str}{param_name}{self.visit(port_decl.quantum_type)}"
|
206
|
+
)
|
185
207
|
|
186
208
|
def visit_QuantumBit(self, qtype: QuantumBit) -> str:
|
187
209
|
return "qbit"
|
@@ -21,6 +21,8 @@ from classiq.interface.generator.functions.port_declaration import (
|
|
21
21
|
PortDeclarationDirection,
|
22
22
|
)
|
23
23
|
from classiq.interface.generator.functions.type_name import TypeName
|
24
|
+
from classiq.interface.generator.functions.type_qualifier import TypeQualifier
|
25
|
+
from classiq.interface.generator.types.compilation_metadata import CompilationMetadata
|
24
26
|
from classiq.interface.generator.types.enum_declaration import EnumDeclaration
|
25
27
|
from classiq.interface.generator.types.qstruct_declaration import QStructDeclaration
|
26
28
|
from classiq.interface.generator.types.struct_declaration import StructDeclaration
|
@@ -131,6 +133,7 @@ class PythonPrettyPrinter(ModelVisitor):
|
|
131
133
|
self._import_annotated = False
|
132
134
|
self._symbolic_imports: dict[str, int] = dict()
|
133
135
|
self._functions: Optional[Mapping[str, QuantumFunctionDeclaration]] = None
|
136
|
+
self._compilation_metadata: dict[str, CompilationMetadata] = dict()
|
134
137
|
|
135
138
|
def visit(self, node: NodeType) -> str:
|
136
139
|
res = super().visit(node)
|
@@ -140,6 +143,7 @@ class PythonPrettyPrinter(ModelVisitor):
|
|
140
143
|
|
141
144
|
def visit_Model(self, model: Model) -> str:
|
142
145
|
self._functions = {**model.function_dict, **BUILTIN_FUNCTION_DECLARATIONS}
|
146
|
+
self._compilation_metadata = model.functions_compilation_metadata
|
143
147
|
enum_decls = [self.visit(decl) for decl in model.enums]
|
144
148
|
struct_decls = [self.visit(decl) for decl in model.types]
|
145
149
|
qstruct_decls = [self.visit(qstruct_decl) for qstruct_decl in model.qstructs]
|
@@ -196,12 +200,23 @@ class PythonPrettyPrinter(ModelVisitor):
|
|
196
200
|
self.visit(arg_decl) for arg_decl in func_def.positional_arg_declarations
|
197
201
|
)
|
198
202
|
|
203
|
+
def _get_qfunc_decorator(self, func_decl: QuantumFunctionDeclaration) -> str:
|
204
|
+
no_qualifiers_decorator = "@qfunc"
|
205
|
+
if func_decl.name not in self._compilation_metadata:
|
206
|
+
return no_qualifiers_decorator
|
207
|
+
atomic_qualifiers = self._compilation_metadata[func_decl.name].atomic_qualifiers
|
208
|
+
if len(atomic_qualifiers) == 0:
|
209
|
+
return no_qualifiers_decorator
|
210
|
+
|
211
|
+
qualifiers = (f'"{qualifier}"' for qualifier in atomic_qualifiers)
|
212
|
+
qualifier_list = f"[{', '.join(qualifiers)}]"
|
213
|
+
return no_qualifiers_decorator + f" (atomic_qualifiers={qualifier_list})"
|
214
|
+
|
199
215
|
def visit_QuantumFunctionDeclaration(
|
200
216
|
self, func_decl: QuantumFunctionDeclaration
|
201
217
|
) -> str:
|
202
|
-
|
203
|
-
|
204
|
-
)
|
218
|
+
qfunc_decorator = self._get_qfunc_decorator(func_decl)
|
219
|
+
return f"{qfunc_decorator}\ndef {func_decl.name}({self._visit_arg_decls(func_decl)}) -> None:"
|
205
220
|
|
206
221
|
def visit_EnumDeclaration(self, enum_decl: EnumDeclaration) -> str:
|
207
222
|
self._import_enum = True
|
@@ -241,16 +256,18 @@ class PythonPrettyPrinter(ModelVisitor):
|
|
241
256
|
return f"{var_decl.name}: {self.visit(var_decl.quantum_type)}"
|
242
257
|
|
243
258
|
def visit_AnonPortDeclaration(self, port_decl: AnonPortDeclaration) -> str:
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
259
|
+
var_type = self._extract_port_type(port_decl)
|
260
|
+
return f"{port_decl.name}: {var_type}"
|
261
|
+
|
262
|
+
def _extract_port_type(self, port_decl: AnonPortDeclaration) -> str:
|
263
|
+
var_type = self.visit(port_decl.quantum_type)
|
264
|
+
if port_decl.direction is not PortDeclarationDirection.Inout:
|
265
|
+
self._imports[port_decl.direction.name] = 1
|
266
|
+
var_type = f"{port_decl.direction.name}[{var_type}]"
|
267
|
+
if port_decl.type_qualifier is not TypeQualifier.Quantum:
|
268
|
+
self._imports[port_decl.type_qualifier.name] = 1
|
269
|
+
var_type = f"{port_decl.type_qualifier.name}[{var_type}]"
|
270
|
+
return var_type
|
254
271
|
|
255
272
|
def visit_QuantumBit(self, qtype: QuantumBit) -> str:
|
256
273
|
self._imports["QBit"] = 1
|
@@ -349,7 +366,7 @@ class PythonPrettyPrinter(ModelVisitor):
|
|
349
366
|
|
350
367
|
def _visit_operand_arg_decl(self, arg_decl: AnonPositionalArg) -> str:
|
351
368
|
if isinstance(arg_decl, AnonPortDeclaration):
|
352
|
-
type_str = self.
|
369
|
+
type_str = self._extract_port_type(arg_decl)
|
353
370
|
elif isinstance(arg_decl, AnonClassicalParameterDeclaration):
|
354
371
|
type_str = self.visit(arg_decl.classical_type)
|
355
372
|
else:
|
@@ -2,6 +2,9 @@ import dataclasses
|
|
2
2
|
import inspect
|
3
3
|
from enum import EnumMeta
|
4
4
|
from typing import (
|
5
|
+
Any,
|
6
|
+
ForwardRef,
|
7
|
+
Literal,
|
5
8
|
Optional,
|
6
9
|
get_args,
|
7
10
|
get_origin,
|
@@ -19,7 +22,6 @@ from classiq.interface.generator.functions.concrete_types import ConcreteClassic
|
|
19
22
|
from classiq.interface.generator.functions.type_name import Enum, Struct
|
20
23
|
|
21
24
|
from classiq.qmod.cparam import CArray, CBool, CInt, CReal
|
22
|
-
from classiq.qmod.qmod_variable import get_type_hint_expr
|
23
25
|
from classiq.qmod.utilities import version_portable_get_args
|
24
26
|
|
25
27
|
CARRAY_ERROR_MESSAGE = (
|
@@ -71,3 +73,12 @@ class PythonClassicalType:
|
|
71
73
|
|
72
74
|
def register_enum(self, py_type: EnumMeta) -> None:
|
73
75
|
pass
|
76
|
+
|
77
|
+
|
78
|
+
def get_type_hint_expr(type_hint: Any) -> str:
|
79
|
+
if isinstance(type_hint, ForwardRef): # expression in string literal
|
80
|
+
return str(type_hint.__forward_arg__)
|
81
|
+
if get_origin(type_hint) == Literal: # explicit numeric literal
|
82
|
+
return str(get_args(type_hint)[0])
|
83
|
+
else:
|
84
|
+
return str(type_hint) # implicit numeric literal
|