classiq 0.69.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/analyzer/analyzer.py +0 -18
- classiq/analyzer/url_utils.py +9 -4
- classiq/applications/combinatorial_helpers/pyomo_utils.py +2 -0
- classiq/interface/_version.py +1 -1
- classiq/interface/backend/quantum_backend_providers.py +6 -0
- classiq/interface/chemistry/operator.py +1 -21
- classiq/interface/debug_info/debug_info.py +4 -0
- classiq/interface/executor/quantum_instruction_set.py +1 -0
- classiq/interface/generator/arith/arithmetic.py +21 -6
- classiq/interface/generator/circuit_code/circuit_code.py +4 -0
- classiq/interface/generator/circuit_code/types_and_constants.py +1 -0
- classiq/interface/generator/expressions/atomic_expression_functions.py +1 -2
- classiq/interface/generator/expressions/expression_constants.py +0 -3
- classiq/interface/generator/expressions/expression_types.py +12 -4
- classiq/interface/generator/expressions/proxies/__init__.py +0 -0
- classiq/interface/generator/expressions/proxies/classical/__init__.py +0 -0
- classiq/interface/generator/expressions/proxies/classical/classical_array_proxy.py +79 -0
- classiq/interface/generator/expressions/proxies/classical/classical_proxy.py +26 -0
- classiq/interface/generator/expressions/proxies/classical/classical_scalar_proxy.py +32 -0
- classiq/interface/generator/expressions/proxies/classical/classical_struct_proxy.py +35 -0
- classiq/interface/generator/expressions/proxies/classical/utils.py +34 -0
- classiq/interface/generator/expressions/proxies/quantum/__init__.py +0 -0
- classiq/interface/generator/expressions/{qmod_qarray_proxy.py → proxies/quantum/qmod_qarray_proxy.py} +3 -1
- classiq/interface/generator/expressions/{qmod_qscalar_proxy.py → proxies/quantum/qmod_qscalar_proxy.py} +3 -1
- classiq/interface/generator/expressions/{qmod_qstruct_proxy.py → proxies/quantum/qmod_qstruct_proxy.py} +3 -1
- classiq/interface/generator/functions/classical_type.py +24 -30
- classiq/interface/generator/functions/type_name.py +42 -2
- classiq/interface/generator/functions/type_qualifier.py +7 -0
- classiq/interface/generator/generated_circuit_data.py +22 -4
- classiq/interface/generator/model/preferences/preferences.py +1 -0
- classiq/interface/generator/quantum_function_call.py +8 -1
- classiq/interface/generator/quantum_program.py +0 -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/native_function_definition.py +0 -10
- 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 +37 -3
- 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/atomic_expression_functions_defs.py +9 -3
- classiq/model_expansions/capturing/captured_vars.py +156 -34
- classiq/model_expansions/evaluators/arg_type_match.py +4 -2
- classiq/model_expansions/evaluators/classical_expression.py +2 -2
- classiq/model_expansions/evaluators/classical_type_inference.py +70 -0
- classiq/model_expansions/evaluators/control.py +1 -1
- classiq/model_expansions/evaluators/parameter_types.py +72 -16
- classiq/model_expansions/evaluators/quantum_type_utils.py +7 -57
- classiq/model_expansions/expression_evaluator.py +3 -12
- classiq/model_expansions/function_builder.py +2 -8
- classiq/model_expansions/generative_functions.py +39 -3
- classiq/model_expansions/interpreters/base_interpreter.py +3 -4
- classiq/model_expansions/quantum_operations/arithmetic/__init__.py +0 -0
- classiq/model_expansions/quantum_operations/arithmetic/explicit_boolean_expressions.py +60 -0
- classiq/model_expansions/quantum_operations/assignment_result_processor.py +9 -0
- 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/quantum_operations/quantum_function_call.py +0 -22
- classiq/model_expansions/scope.py +7 -14
- classiq/model_expansions/scope_initialization.py +32 -39
- classiq/model_expansions/transformers/model_renamer.py +13 -4
- classiq/model_expansions/visitors/variable_references.py +8 -4
- classiq/open_library/functions/__init__.py +2 -0
- classiq/open_library/functions/lookup_table.py +58 -0
- classiq/qmod/__init__.py +3 -1
- classiq/qmod/declaration_inferrer.py +55 -25
- 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_parameter.py +8 -0
- classiq/qmod/qmod_variable.py +189 -151
- classiq/qmod/quantum_function.py +3 -4
- classiq/qmod/semantics/annotation/call_annotation.py +0 -28
- classiq/qmod/semantics/annotation/qstruct_annotator.py +21 -1
- classiq/qmod/semantics/validation/main_validation.py +1 -1
- classiq/qmod/semantics/validation/type_hints.py +38 -0
- classiq/qmod/utilities.py +38 -1
- {classiq-0.69.0.dist-info → classiq-0.71.0.dist-info}/METADATA +10 -12
- {classiq-0.69.0.dist-info → classiq-0.71.0.dist-info}/RECORD +97 -82
- {classiq-0.69.0.dist-info → classiq-0.71.0.dist-info}/WHEEL +1 -1
- /classiq/interface/generator/expressions/{qmod_struct_instance.py → proxies/classical/qmod_struct_instance.py} +0 -0
- /classiq/interface/generator/expressions/{qmod_sized_proxy.py → proxies/quantum/qmod_sized_proxy.py} +0 -0
@@ -1,4 +1,5 @@
|
|
1
1
|
import ast
|
2
|
+
import re
|
2
3
|
from collections.abc import Mapping, Sequence
|
3
4
|
from dataclasses import dataclass
|
4
5
|
from typing import TypeVar, cast
|
@@ -16,6 +17,12 @@ from classiq.model_expansions.visitors.variable_references import VarRefCollecto
|
|
16
17
|
AST_NODE = TypeVar("AST_NODE", bound=NodeType)
|
17
18
|
|
18
19
|
|
20
|
+
def _replace_full_word(pattern: str, substitution: str, target: str) -> str:
|
21
|
+
return re.sub(
|
22
|
+
rf"(^|\b|\W)({re.escape(pattern)})($|\b|\W)", rf"\1{substitution}\3", target
|
23
|
+
)
|
24
|
+
|
25
|
+
|
19
26
|
@dataclass(frozen=True)
|
20
27
|
class HandleRenaming:
|
21
28
|
source_handle: HandleBinding
|
@@ -74,7 +81,7 @@ class ModelRenamer:
|
|
74
81
|
symbol_mapping: SymbolRenaming,
|
75
82
|
expression: Expression,
|
76
83
|
) -> Expression:
|
77
|
-
vrc = VarRefCollector(ignore_duplicated_handles=True)
|
84
|
+
vrc = VarRefCollector(ignore_duplicated_handles=True, ignore_sympy_symbols=True)
|
78
85
|
vrc.visit(ast.parse(expression.expr))
|
79
86
|
|
80
87
|
handle_names = {
|
@@ -87,10 +94,12 @@ class ModelRenamer:
|
|
87
94
|
new_handle = handle.collapse()
|
88
95
|
for handle_to_replace, replacement in handle_names.items():
|
89
96
|
new_handle = new_handle.replace_prefix(handle_to_replace, replacement)
|
90
|
-
new_expr_str =
|
97
|
+
new_expr_str = _replace_full_word(
|
98
|
+
str(handle), str(new_handle), new_expr_str
|
99
|
+
)
|
91
100
|
if handle.qmod_expr != str(handle):
|
92
|
-
new_expr_str =
|
93
|
-
handle.qmod_expr, new_handle.qmod_expr
|
101
|
+
new_expr_str = _replace_full_word(
|
102
|
+
str(handle.qmod_expr), str(new_handle.qmod_expr), new_expr_str
|
94
103
|
)
|
95
104
|
|
96
105
|
new_expr = Expression(expr=new_expr_str)
|
@@ -24,10 +24,14 @@ from classiq.interface.model.handle_binding import (
|
|
24
24
|
|
25
25
|
class VarRefCollector(ast.NodeVisitor):
|
26
26
|
def __init__(
|
27
|
-
self,
|
27
|
+
self,
|
28
|
+
ignore_duplicated_handles: bool = False,
|
29
|
+
unevaluated: bool = False,
|
30
|
+
ignore_sympy_symbols: bool = False,
|
28
31
|
) -> None:
|
29
32
|
self._var_handles: dict[HandleBinding, bool] = {}
|
30
33
|
self._ignore_duplicated_handles = ignore_duplicated_handles
|
34
|
+
self._ignore_sympy_symbols = ignore_sympy_symbols
|
31
35
|
self._unevaluated = unevaluated
|
32
36
|
self._is_nested = False
|
33
37
|
|
@@ -117,9 +121,9 @@ class VarRefCollector(ast.NodeVisitor):
|
|
117
121
|
return handle
|
118
122
|
|
119
123
|
def visit_Name(self, node: ast.Name) -> Optional[HandleBinding]:
|
120
|
-
if node.id in set(
|
121
|
-
|
122
|
-
):
|
124
|
+
if not self._ignore_sympy_symbols and node.id in set(
|
125
|
+
SYMPY_SUPPORTED_EXPRESSIONS
|
126
|
+
) | set(DEFAULT_SUPPORTED_FUNC_NAMES):
|
123
127
|
return None
|
124
128
|
handle = HandleBinding(name=node.id)
|
125
129
|
if not self._is_nested:
|
@@ -9,6 +9,7 @@ from .grover import *
|
|
9
9
|
from .hea import *
|
10
10
|
from .linear_pauli_rotation import *
|
11
11
|
from .linear_pauli_rotation import _single_pauli
|
12
|
+
from .lookup_table import span_lookup_table
|
12
13
|
from .modular_exponentiation import *
|
13
14
|
from .modular_exponentiation import _check_msb
|
14
15
|
from .qaoa_penalty import *
|
@@ -131,6 +132,7 @@ __all__ = [
|
|
131
132
|
"qsvt_lcu_step",
|
132
133
|
"qsvt_step",
|
133
134
|
"reflect_about_zero",
|
135
|
+
"span_lookup_table",
|
134
136
|
"suzuki_trotter",
|
135
137
|
"swap_test",
|
136
138
|
"switch",
|
@@ -0,0 +1,58 @@
|
|
1
|
+
from itertools import product
|
2
|
+
|
3
|
+
from classiq.interface.exceptions import ClassiqValueError
|
4
|
+
|
5
|
+
from classiq.qmod.builtins.operations import assign, bind, within_apply
|
6
|
+
from classiq.qmod.qmod_variable import QNum
|
7
|
+
from classiq.qmod.symbolic import subscript
|
8
|
+
from classiq.qmod.utilities import RealFunction, get_temp_var_name, qnum_values
|
9
|
+
|
10
|
+
|
11
|
+
def _get_qnum_values(num: QNum) -> list[float]:
|
12
|
+
size = num.size
|
13
|
+
is_signed = num.is_signed
|
14
|
+
fraction_digits = num.fraction_digits
|
15
|
+
if (
|
16
|
+
not isinstance(size, int)
|
17
|
+
or not isinstance(is_signed, bool)
|
18
|
+
or not isinstance(fraction_digits, int)
|
19
|
+
):
|
20
|
+
raise ClassiqValueError(
|
21
|
+
"Must call 'span_lookup_table' inside a generative qfunc"
|
22
|
+
)
|
23
|
+
|
24
|
+
return qnum_values(size, is_signed, fraction_digits)
|
25
|
+
|
26
|
+
|
27
|
+
def span_lookup_table(func: RealFunction, *targets: QNum) -> QNum:
|
28
|
+
"""
|
29
|
+
Applies a classical function to quantum numbers.
|
30
|
+
|
31
|
+
Works by reducing the function into a lookup table over all the possible values
|
32
|
+
of the quantum numbers.
|
33
|
+
|
34
|
+
Args:
|
35
|
+
func: A Python function
|
36
|
+
*targets: One or more initialized quantum numbers
|
37
|
+
|
38
|
+
Returns:
|
39
|
+
The quantum result of applying func to targets
|
40
|
+
|
41
|
+
Notes:
|
42
|
+
Must be called inside a generative function (`@qfunc(generative=True)`)
|
43
|
+
"""
|
44
|
+
if len(targets) == 0:
|
45
|
+
raise ClassiqValueError("No targets specified")
|
46
|
+
|
47
|
+
target_vals = [_get_qnum_values(target) for target in targets]
|
48
|
+
lookup_table = [func(*vals[::-1]) for vals in product(*target_vals[::-1])]
|
49
|
+
|
50
|
+
index_size = sum(target.size for target in targets)
|
51
|
+
index: QNum = QNum(get_temp_var_name(), size=index_size)
|
52
|
+
result: QNum = QNum(get_temp_var_name("result"))
|
53
|
+
|
54
|
+
within_apply(
|
55
|
+
lambda: bind(list(targets), index),
|
56
|
+
lambda: assign(subscript(lookup_table, index), result),
|
57
|
+
)
|
58
|
+
return result
|
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,8 +37,9 @@ 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
|
42
|
+
from classiq.qmod.semantics.validation.type_hints import validate_annotation
|
41
43
|
from classiq.qmod.utilities import unmangle_keyword, version_portable_get_args
|
42
44
|
|
43
45
|
if sys.version_info[0:2] >= (3, 9):
|
@@ -84,15 +86,12 @@ def python_type_to_qmod(
|
|
84
86
|
|
85
87
|
|
86
88
|
def _extract_port_decl(name: Optional[str], py_type: Any) -> AnonPortDeclaration:
|
87
|
-
|
88
|
-
qtype: type[QVar] = QVar.from_type_hint(py_type) # type:ignore[assignment]
|
89
|
-
direction = qtype.port_direction(py_type)
|
90
|
-
if isinstance(py_type, _AnnotatedAlias):
|
91
|
-
py_type = py_type.__args__[0]
|
89
|
+
quantum_type, direction, qualifier = get_port_from_type_hint(py_type)
|
92
90
|
param = AnonPortDeclaration(
|
93
91
|
name=None,
|
94
92
|
direction=direction,
|
95
|
-
quantum_type=
|
93
|
+
quantum_type=quantum_type,
|
94
|
+
type_qualifier=qualifier,
|
96
95
|
)
|
97
96
|
if name is not None:
|
98
97
|
param = param.rename(name)
|
@@ -123,25 +122,47 @@ def _extract_operand_decl(
|
|
123
122
|
|
124
123
|
|
125
124
|
def _extract_operand_param(py_type: Any) -> tuple[Optional[str], Any]:
|
126
|
-
if
|
125
|
+
if get_origin(py_type) is not Annotated:
|
127
126
|
return None, py_type
|
127
|
+
|
128
128
|
args = get_args(py_type)
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
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
|
+
)
|
145
166
|
|
146
167
|
|
147
168
|
@overload
|
@@ -163,6 +184,7 @@ def _extract_positional_args(
|
|
163
184
|
) -> Sequence[AnonPositionalArg]:
|
164
185
|
result: list[AnonPositionalArg] = []
|
165
186
|
for name, py_type in args:
|
187
|
+
validate_annotation(py_type)
|
166
188
|
if name == "return":
|
167
189
|
continue
|
168
190
|
name = unmangle_keyword(name)
|
@@ -175,7 +197,7 @@ def _extract_positional_args(
|
|
175
197
|
if name is not None:
|
176
198
|
param = param.rename(name)
|
177
199
|
result.append(param)
|
178
|
-
elif
|
200
|
+
elif is_qvar(py_type):
|
179
201
|
result.append(_extract_port_decl(name, py_type))
|
180
202
|
else:
|
181
203
|
result.append(_extract_operand_decl(name, py_type, qmodule=qmodule))
|
@@ -191,3 +213,11 @@ def infer_func_decl(
|
|
191
213
|
list(py_func.__annotations__.items()), qmodule=qmodule
|
192
214
|
),
|
193
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
|
classiq/qmod/qfunc.py
CHANGED
@@ -35,6 +35,7 @@ def qfunc(
|
|
35
35
|
*,
|
36
36
|
external: Literal[True],
|
37
37
|
synthesize_separately: Literal[False] = False,
|
38
|
+
atomic_qualifiers: Optional[list[str]] = None,
|
38
39
|
) -> Callable[[Callable], ExternalQFunc]: ...
|
39
40
|
|
40
41
|
|
@@ -43,11 +44,22 @@ def qfunc(
|
|
43
44
|
*,
|
44
45
|
generative: Literal[True],
|
45
46
|
synthesize_separately: bool = False,
|
47
|
+
atomic_qualifiers: Optional[list[str]] = None,
|
46
48
|
) -> Callable[[Callable], GenerativeQFunc]: ...
|
47
49
|
|
48
50
|
|
49
51
|
@overload
|
50
|
-
def qfunc(
|
52
|
+
def qfunc(
|
53
|
+
*, synthesize_separately: bool, atomic_qualifiers: Optional[list[str]] = None
|
54
|
+
) -> Callable[[Callable], QFunc]: ...
|
55
|
+
|
56
|
+
|
57
|
+
@overload
|
58
|
+
def qfunc(
|
59
|
+
*,
|
60
|
+
synthesize_separately: bool = False,
|
61
|
+
atomic_qualifiers: Optional[list[str]] = None,
|
62
|
+
) -> Callable[[Callable], QFunc]: ...
|
51
63
|
|
52
64
|
|
53
65
|
def qfunc(
|
@@ -56,24 +68,37 @@ def qfunc(
|
|
56
68
|
external: bool = False,
|
57
69
|
generative: bool = False,
|
58
70
|
synthesize_separately: bool = False,
|
71
|
+
atomic_qualifiers: Optional[list[str]] = None,
|
59
72
|
) -> Union[Callable[[Callable], QCallable], QCallable]:
|
60
73
|
def wrapper(func: Callable) -> QCallable:
|
61
74
|
qfunc: BaseQFunc
|
75
|
+
|
76
|
+
if external:
|
77
|
+
_validate_directives(synthesize_separately, atomic_qualifiers)
|
78
|
+
return ExternalQFunc(func)
|
79
|
+
|
62
80
|
if generative or _GENERATIVE_SWITCH:
|
63
81
|
qfunc = GenerativeQFunc(func)
|
64
|
-
elif external:
|
65
|
-
if synthesize_separately:
|
66
|
-
raise ClassiqInternalError(
|
67
|
-
"External functions can't be marked as synthesized separately"
|
68
|
-
)
|
69
|
-
return ExternalQFunc(func)
|
70
82
|
else:
|
71
83
|
qfunc = QFunc(func)
|
72
84
|
if synthesize_separately:
|
73
85
|
qfunc.update_compilation_metadata(should_synthesize_separately=True)
|
86
|
+
if atomic_qualifiers is not None and len(atomic_qualifiers) > 0:
|
87
|
+
qfunc.update_compilation_metadata(atomic_qualifiers=atomic_qualifiers)
|
74
88
|
return qfunc
|
75
89
|
|
76
90
|
if func is not None:
|
77
91
|
return wrapper(func)
|
78
|
-
|
79
92
|
return wrapper
|
93
|
+
|
94
|
+
|
95
|
+
def _validate_directives(
|
96
|
+
synthesize_separately: bool, atomic_qualifiers: Optional[list[str]] = None
|
97
|
+
) -> None:
|
98
|
+
error_msg = ""
|
99
|
+
if synthesize_separately:
|
100
|
+
error_msg += "External functions can't be marked as synthesized separately. \n"
|
101
|
+
if atomic_qualifiers is not None and len(atomic_qualifiers) > 0:
|
102
|
+
error_msg += "External functions can't have atomic qualifiers."
|
103
|
+
if error_msg:
|
104
|
+
raise ClassiqInternalError(error_msg)
|
classiq/qmod/qmod_parameter.py
CHANGED
@@ -22,6 +22,11 @@ from classiq.qmod.cparam import ( # noqa: F401
|
|
22
22
|
CParamScalar,
|
23
23
|
CReal,
|
24
24
|
)
|
25
|
+
from classiq.qmod.generative import (
|
26
|
+
generative_mode_context,
|
27
|
+
interpret_expression,
|
28
|
+
is_generative_mode,
|
29
|
+
)
|
25
30
|
from classiq.qmod.model_state_container import ModelStateContainer
|
26
31
|
from classiq.qmod.symbolic_expr import Symbolic, SymbolicExpr
|
27
32
|
|
@@ -66,6 +71,9 @@ class CParamList(CParam):
|
|
66
71
|
|
67
72
|
@property
|
68
73
|
def len(self) -> CParamScalar:
|
74
|
+
if is_generative_mode():
|
75
|
+
with generative_mode_context(False):
|
76
|
+
return interpret_expression(str(self.len))
|
69
77
|
return CParamScalar(f"get_field({self}, 'len')")
|
70
78
|
|
71
79
|
|