classiq 0.70.0__py3-none-any.whl → 0.72.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/__init__.py +0 -6
- classiq/_internals/client.py +11 -1
- classiq/applications/chemistry/chemistry_model_constructor.py +18 -16
- classiq/applications/combinatorial_helpers/optimization_model.py +9 -2
- classiq/applications/combinatorial_helpers/pyomo_utils.py +6 -1
- classiq/applications/finance/__init__.py +0 -3
- classiq/applications/qsvm/__init__.py +0 -2
- classiq/interface/_version.py +1 -1
- classiq/interface/backend/backend_preferences.py +22 -0
- classiq/interface/backend/quantum_backend_providers.py +2 -0
- 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 +8 -3
- classiq/interface/generator/expressions/proxies/classical/any_classical_value.py +135 -0
- 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/builtins/internal_operators.py +1 -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 +14 -1
- classiq/interface/generator/hardware/hardware_data.py +3 -1
- classiq/interface/generator/quantum_function_call.py +8 -1
- classiq/interface/generator/synthesis_execution_parameter.py +1 -0
- classiq/interface/generator/transpiler_basis_gates.py +3 -1
- classiq/interface/generator/types/compilation_metadata.py +1 -0
- classiq/interface/hardware.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/handle_binding.py +21 -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/interface/server/routes.py +0 -2
- classiq/model_expansions/atomic_expression_functions_defs.py +35 -13
- classiq/model_expansions/capturing/captured_vars.py +156 -34
- classiq/model_expansions/closure.py +0 -9
- classiq/model_expansions/evaluators/classical_type_inference.py +70 -0
- classiq/model_expansions/evaluators/parameter_types.py +20 -10
- 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 +7 -8
- classiq/model_expansions/interpreters/generative_interpreter.py +33 -5
- classiq/model_expansions/quantum_operations/__init__.py +0 -2
- classiq/model_expansions/quantum_operations/block_evaluator.py +16 -2
- classiq/model_expansions/quantum_operations/call_emitter.py +49 -6
- classiq/model_expansions/quantum_operations/emitter.py +64 -6
- classiq/model_expansions/quantum_operations/expression_evaluator.py +4 -0
- classiq/model_expansions/quantum_operations/handle_evaluator.py +1 -1
- classiq/model_expansions/quantum_operations/quantum_function_call.py +49 -0
- classiq/model_expansions/quantum_operations/repeat_block_evaluator.py +34 -0
- classiq/model_expansions/scope.py +33 -21
- classiq/model_expansions/scope_initialization.py +28 -32
- classiq/model_expansions/transformers/model_renamer.py +69 -63
- classiq/model_expansions/utils/sympy_utils.py +24 -0
- classiq/model_expansions/visitors/variable_references.py +1 -0
- classiq/qmod/__init__.py +3 -1
- classiq/qmod/builtins/functions/__init__.py +8 -0
- classiq/qmod/builtins/functions/allocation.py +36 -0
- classiq/qmod/builtins/functions/arithmetic.py +10 -5
- classiq/qmod/builtins/functions/mid_circuit_measurement.py +3 -0
- classiq/qmod/builtins/operations.py +2 -2
- classiq/qmod/declaration_inferrer.py +52 -24
- classiq/qmod/model_state_container.py +9 -0
- 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/qmod/symbolic.py +16 -3
- {classiq-0.70.0.dist-info → classiq-0.72.0.dist-info}/METADATA +1 -1
- {classiq-0.70.0.dist-info → classiq-0.72.0.dist-info}/RECORD +90 -91
- classiq/applications/finance/finance_model_constructor.py +0 -137
- classiq/applications/grover/__init__.py +0 -9
- classiq/applications/grover/grover_model_constructor.py +0 -167
- classiq/applications/libraries/__init__.py +0 -0
- classiq/applications/libraries/qmci_library.py +0 -22
- classiq/applications/qsvm/qsvm_model_constructor.py +0 -131
- classiq/model_expansions/quantum_operations/classicalif.py +0 -57
- classiq/model_expansions/quantum_operations/repeat.py +0 -62
- {classiq-0.70.0.dist-info → classiq-0.72.0.dist-info}/WHEEL +0 -0
@@ -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
|
@@ -55,14 +58,18 @@ def add_functions_to_scope(
|
|
55
58
|
|
56
59
|
|
57
60
|
def add_generative_functions_to_scope(
|
58
|
-
functions: Sequence[GenerativeQFunc], scope: Scope
|
61
|
+
functions: Sequence[GenerativeQFunc], scope: Scope, override_atomic: bool = False
|
59
62
|
) -> None:
|
60
63
|
for function in functions:
|
61
64
|
name = function.func_decl.name
|
62
|
-
if
|
63
|
-
|
65
|
+
if (
|
66
|
+
name == MAIN_FUNCTION_NAME
|
67
|
+
or name not in scope
|
68
|
+
or (override_atomic and scope[name].value.is_atomic)
|
69
|
+
):
|
70
|
+
scope[name] = Evaluated(
|
64
71
|
value=GenerativeFunctionClosure.create(
|
65
|
-
name=
|
72
|
+
name=name,
|
66
73
|
positional_arg_declarations=function.func_decl.positional_arg_declarations,
|
67
74
|
scope=Scope(parent=scope),
|
68
75
|
generative_blocks={"body": function},
|
@@ -102,17 +109,24 @@ def add_entry_point_params_to_scope(
|
|
102
109
|
parameters: Sequence[PositionalArg], main_closure: FunctionClosure
|
103
110
|
) -> None:
|
104
111
|
for parameter in parameters:
|
105
|
-
if
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
+
if isinstance(parameter, PortDeclaration):
|
113
|
+
main_closure.scope[parameter.name] = Evaluated(
|
114
|
+
value=QuantumSymbol(
|
115
|
+
handle=HandleBinding(name=parameter.name),
|
116
|
+
quantum_type=evaluate_type_in_quantum_symbol(
|
117
|
+
parameter.quantum_type, main_closure.scope, parameter.name
|
118
|
+
),
|
112
119
|
),
|
113
|
-
|
114
|
-
|
115
|
-
)
|
120
|
+
defining_function=main_closure,
|
121
|
+
)
|
122
|
+
elif isinstance(parameter, ClassicalParameterDeclaration):
|
123
|
+
param_val = parameter.classical_type.get_classical_proxy(
|
124
|
+
handle=HandleBinding(name=parameter.name)
|
125
|
+
)
|
126
|
+
main_closure.scope[parameter.name] = Evaluated(
|
127
|
+
value=param_val,
|
128
|
+
defining_function=main_closure,
|
129
|
+
)
|
116
130
|
|
117
131
|
|
118
132
|
def init_top_level_scope(model: Model, scope: Scope) -> None:
|
@@ -126,24 +140,6 @@ def init_builtin_types() -> None:
|
|
126
140
|
QMODULE.type_decls |= BUILTIN_STRUCT_DECLARATIONS
|
127
141
|
|
128
142
|
|
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
143
|
def _get_shape(classical_type: ConcreteClassicalType) -> tuple[int, ...]:
|
148
144
|
if isinstance(classical_type, ClassicalList):
|
149
145
|
raise ClassiqInternalExpansionError("Unexpected classical list")
|
@@ -36,72 +36,78 @@ class HandleRenaming:
|
|
36
36
|
SymbolRenaming = Mapping[HandleBinding, Sequence[HandleRenaming]]
|
37
37
|
|
38
38
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
39
|
+
def _rewrite_expression(
|
40
|
+
symbol_mapping: SymbolRenaming, expression: Expression
|
41
|
+
) -> Expression:
|
42
|
+
vrc = VarRefCollector(
|
43
|
+
ignore_duplicated_handles=True, ignore_sympy_symbols=True, unevaluated=True
|
44
|
+
)
|
45
|
+
vrc.visit(ast.parse(expression.expr))
|
46
|
+
|
47
|
+
handle_names = {
|
48
|
+
part.source_handle: part.target_var_handle
|
49
|
+
for parts in symbol_mapping.values()
|
50
|
+
for part in parts
|
51
|
+
}
|
52
|
+
new_expr_str = expression.expr
|
53
|
+
for handle in vrc.var_handles:
|
54
|
+
new_handle = handle.collapse()
|
55
|
+
for handle_to_replace, replacement in handle_names.items():
|
56
|
+
new_handle = new_handle.replace_prefix(handle_to_replace, replacement)
|
57
|
+
new_expr_str = _replace_full_word(str(handle), str(new_handle), new_expr_str)
|
58
|
+
if handle.qmod_expr != str(handle):
|
59
|
+
new_expr_str = _replace_full_word(
|
60
|
+
str(handle.qmod_expr), str(new_handle.qmod_expr), new_expr_str
|
61
|
+
)
|
62
|
+
|
63
|
+
new_expr = Expression(expr=new_expr_str)
|
64
|
+
new_expr._evaluated_expr = expression._evaluated_expr
|
65
|
+
return new_expr
|
48
66
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
for handle_to_replace, replacement in handle_replacements.items():
|
54
|
-
handle = handle.replace_prefix(handle_to_replace, replacement)
|
55
|
-
return handle
|
56
|
-
|
57
|
-
@staticmethod
|
58
|
-
def visit_Expression(expr: Expression) -> Expression:
|
59
|
-
return self._rewrite_expression(symbol_mapping, expr)
|
60
|
-
|
61
|
-
def visit_QuantumExpressionOperation(
|
62
|
-
self, op: QuantumExpressionOperation
|
63
|
-
) -> QuantumExpressionOperation:
|
64
|
-
op = cast(QuantumExpressionOperation, self.generic_visit(op))
|
65
|
-
previous_var_handles = list(op._var_handles)
|
66
|
-
op._var_handles = self.visit(op._var_handles)
|
67
|
-
op._var_types = {
|
68
|
-
new_handle.name: op._var_types.get(
|
69
|
-
new_handle.name, op._var_types[previous_handle.name]
|
70
|
-
)
|
71
|
-
for previous_handle, new_handle in zip(
|
72
|
-
previous_var_handles, op._var_handles
|
73
|
-
)
|
74
|
-
}
|
75
|
-
return op
|
76
|
-
|
77
|
-
return ReplaceSplitVars().visit(subject)
|
78
|
-
|
79
|
-
def _rewrite_expression(
|
80
|
-
self,
|
81
|
-
symbol_mapping: SymbolRenaming,
|
82
|
-
expression: Expression,
|
83
|
-
) -> Expression:
|
84
|
-
vrc = VarRefCollector(ignore_duplicated_handles=True, ignore_sympy_symbols=True)
|
85
|
-
vrc.visit(ast.parse(expression.expr))
|
86
|
-
|
87
|
-
handle_names = {
|
67
|
+
|
68
|
+
class _ReplaceSplitVarsHandles(ModelTransformer):
|
69
|
+
def __init__(self, symbol_mapping: SymbolRenaming) -> None:
|
70
|
+
self._handle_replacements = {
|
88
71
|
part.source_handle: part.target_var_handle
|
89
72
|
for parts in symbol_mapping.values()
|
90
73
|
for part in parts
|
91
74
|
}
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
75
|
+
|
76
|
+
def visit_HandleBinding(self, handle: HandleBinding) -> HandleBinding:
|
77
|
+
handle = handle.collapse()
|
78
|
+
for handle_to_replace, replacement in self._handle_replacements.items():
|
79
|
+
handle = handle.replace_prefix(handle_to_replace, replacement)
|
80
|
+
return handle
|
81
|
+
|
82
|
+
|
83
|
+
class _ReplaceSplitVarsExpressions(ModelTransformer):
|
84
|
+
def __init__(self, symbol_mapping: SymbolRenaming) -> None:
|
85
|
+
self._symbol_mapping = symbol_mapping
|
86
|
+
|
87
|
+
def visit_Expression(self, expr: Expression) -> Expression:
|
88
|
+
return _rewrite_expression(self._symbol_mapping, expr)
|
89
|
+
|
90
|
+
def visit_QuantumExpressionOperation(
|
91
|
+
self, op: QuantumExpressionOperation
|
92
|
+
) -> QuantumExpressionOperation:
|
93
|
+
op = cast(QuantumExpressionOperation, self.generic_visit(op))
|
94
|
+
previous_var_handles = list(op._var_handles)
|
95
|
+
op._var_handles = self.visit(op._var_handles)
|
96
|
+
op._var_types = {
|
97
|
+
new_handle.name: op._var_types.get(
|
98
|
+
new_handle.name, op._var_types[previous_handle.name]
|
99
99
|
)
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
100
|
+
for previous_handle, new_handle in zip(
|
101
|
+
previous_var_handles, op._var_handles
|
102
|
+
)
|
103
|
+
}
|
104
|
+
return op
|
105
|
+
|
106
|
+
|
107
|
+
class ModelRenamer:
|
108
|
+
def rewrite(self, subject: AST_NODE, symbol_mapping: SymbolRenaming) -> AST_NODE:
|
109
|
+
if len(symbol_mapping) == 0:
|
110
|
+
return subject
|
111
|
+
subject = _ReplaceSplitVarsHandles(symbol_mapping).visit(subject)
|
112
|
+
subject = _ReplaceSplitVarsExpressions(symbol_mapping).visit(subject)
|
113
|
+
return subject
|
@@ -0,0 +1,24 @@
|
|
1
|
+
from typing import Any
|
2
|
+
|
3
|
+
from sympy import Number
|
4
|
+
|
5
|
+
|
6
|
+
def unwrap_sympy_numeric(n: Any) -> Any:
|
7
|
+
if not isinstance(n, Number) or not n.is_constant():
|
8
|
+
return n
|
9
|
+
if n.is_Integer:
|
10
|
+
return int(n)
|
11
|
+
return float(n)
|
12
|
+
|
13
|
+
|
14
|
+
def is_constant_subscript(index: Any) -> bool:
|
15
|
+
if not isinstance(index, slice):
|
16
|
+
return isinstance(unwrap_sympy_numeric(index), int)
|
17
|
+
start = unwrap_sympy_numeric(index.start)
|
18
|
+
stop = unwrap_sympy_numeric(index.stop)
|
19
|
+
step = unwrap_sympy_numeric(index.step)
|
20
|
+
return (
|
21
|
+
(start is None or isinstance(start, int))
|
22
|
+
and (stop is None or isinstance(stop, int))
|
23
|
+
and (step is None or isinstance(step, int))
|
24
|
+
)
|
@@ -58,6 +58,7 @@ class VarRefCollector(ast.NodeVisitor):
|
|
58
58
|
def visit_Subscript(
|
59
59
|
self, node: ast.Subscript
|
60
60
|
) -> Union[SubscriptHandleBinding, SlicedHandleBinding, None]:
|
61
|
+
self.visit(node.slice)
|
61
62
|
with self.set_nested():
|
62
63
|
base_handle = self.visit(node.value)
|
63
64
|
if base_handle is None:
|
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",
|
@@ -50,7 +50,9 @@ CORE_LIB_DECLS = [
|
|
50
50
|
SWAP,
|
51
51
|
IDENTITY,
|
52
52
|
prepare_state,
|
53
|
+
prepare_state_approx,
|
53
54
|
prepare_amplitudes,
|
55
|
+
prepare_amplitudes_approx,
|
54
56
|
unitary,
|
55
57
|
add,
|
56
58
|
modular_add,
|
@@ -62,7 +64,9 @@ CORE_LIB_DECLS = [
|
|
62
64
|
free,
|
63
65
|
randomized_benchmarking,
|
64
66
|
inplace_prepare_state,
|
67
|
+
inplace_prepare_state_approx,
|
65
68
|
inplace_prepare_amplitudes,
|
69
|
+
inplace_prepare_amplitudes_approx,
|
66
70
|
single_pauli_exponent,
|
67
71
|
commuting_paulis_exponent,
|
68
72
|
suzuki_trotter,
|
@@ -114,7 +118,9 @@ __all__ = [ # noqa: RUF022
|
|
114
118
|
"free",
|
115
119
|
"gaussian_finance",
|
116
120
|
"inplace_prepare_amplitudes",
|
121
|
+
"inplace_prepare_amplitudes_approx",
|
117
122
|
"inplace_prepare_state",
|
123
|
+
"inplace_prepare_state_approx",
|
118
124
|
"integer_xor",
|
119
125
|
"log_normal_finance",
|
120
126
|
"modular_add",
|
@@ -125,7 +131,9 @@ __all__ = [ # noqa: RUF022
|
|
125
131
|
"pauli_feature_map",
|
126
132
|
"permute",
|
127
133
|
"prepare_amplitudes",
|
134
|
+
"prepare_amplitudes_approx",
|
128
135
|
"prepare_state",
|
136
|
+
"prepare_state_approx",
|
129
137
|
"qdrift",
|
130
138
|
"randomized_benchmarking",
|
131
139
|
"real_xor_constant",
|
@@ -123,3 +123,39 @@ def inplace_prepare_amplitudes(
|
|
123
123
|
|
124
124
|
"""
|
125
125
|
pass
|
126
|
+
|
127
|
+
|
128
|
+
@qfunc(external=True)
|
129
|
+
def inplace_prepare_amplitudes_approx(
|
130
|
+
amplitudes: CArray[CReal],
|
131
|
+
bound: CReal,
|
132
|
+
target: QArray[QBit, Literal["log(get_field(amplitudes, 'len'), 2)"]],
|
133
|
+
) -> None:
|
134
|
+
pass
|
135
|
+
|
136
|
+
|
137
|
+
@qfunc(external=True)
|
138
|
+
def prepare_amplitudes_approx(
|
139
|
+
amplitudes: CArray[CReal],
|
140
|
+
bound: CReal,
|
141
|
+
out: Output[QArray[QBit, Literal["log(get_field(amplitudes, 'len'), 2)"]]],
|
142
|
+
) -> None:
|
143
|
+
pass
|
144
|
+
|
145
|
+
|
146
|
+
@qfunc(external=True)
|
147
|
+
def inplace_prepare_state_approx(
|
148
|
+
probabilities: CArray[CReal],
|
149
|
+
bound: CReal,
|
150
|
+
target: QArray[QBit, Literal["log(get_field(probabilities, 'len'), 2)"]],
|
151
|
+
) -> None:
|
152
|
+
pass
|
153
|
+
|
154
|
+
|
155
|
+
@qfunc(external=True)
|
156
|
+
def prepare_state_approx(
|
157
|
+
probabilities: CArray[CReal],
|
158
|
+
bound: CReal,
|
159
|
+
out: Output[QArray[QBit, Literal["log(get_field(probabilities, 'len'), 2)"]]],
|
160
|
+
) -> None:
|
161
|
+
pass
|
@@ -1,7 +1,7 @@
|
|
1
1
|
from typing import Literal
|
2
2
|
|
3
3
|
from classiq.qmod.qfunc import qfunc
|
4
|
-
from classiq.qmod.qmod_parameter import CArray, CReal
|
4
|
+
from classiq.qmod.qmod_parameter import CArray, CBool, CReal
|
5
5
|
from classiq.qmod.qmod_variable import Output, QArray, QBit, QNum
|
6
6
|
|
7
7
|
|
@@ -24,13 +24,18 @@ def unitary(
|
|
24
24
|
|
25
25
|
@qfunc(external=True)
|
26
26
|
def add(
|
27
|
-
left:
|
28
|
-
right:
|
27
|
+
left: QNum,
|
28
|
+
right: QNum,
|
29
29
|
result: Output[
|
30
|
-
|
31
|
-
|
30
|
+
QNum[
|
31
|
+
Literal["result_size"],
|
32
|
+
Literal["result_is_signed"],
|
33
|
+
Literal["result_fraction_places"],
|
32
34
|
]
|
33
35
|
],
|
36
|
+
result_size: CReal,
|
37
|
+
result_is_signed: CBool,
|
38
|
+
result_fraction_places: CReal,
|
34
39
|
) -> None:
|
35
40
|
pass
|
36
41
|
|
@@ -142,13 +142,13 @@ def if_(
|
|
142
142
|
if_stmt = ClassicalIf(
|
143
143
|
condition=Expression(expr=str(condition)),
|
144
144
|
then=_operand_to_body(then, "then"),
|
145
|
-
else_=_operand_to_body(else_, "
|
145
|
+
else_=_operand_to_body(else_, "else_") if else_ != _MISSING_VALUE else [], # type: ignore[arg-type]
|
146
146
|
source_ref=source_ref,
|
147
147
|
)
|
148
148
|
if is_generative_mode():
|
149
149
|
if_stmt.set_generative_block("then", then)
|
150
150
|
if callable(else_):
|
151
|
-
if_stmt.set_generative_block("
|
151
|
+
if_stmt.set_generative_block("else_", else_)
|
152
152
|
QCallable.CURRENT_EXPANDABLE.append_statement_to_body(if_stmt)
|
153
153
|
|
154
154
|
|
@@ -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)
|
@@ -1,3 +1,4 @@
|
|
1
|
+
from collections import defaultdict
|
1
2
|
from typing import TYPE_CHECKING
|
2
3
|
|
3
4
|
from classiq.interface.generator.constant import Constant
|
@@ -25,3 +26,11 @@ class ModelStateContainer:
|
|
25
26
|
|
26
27
|
|
27
28
|
QMODULE = ModelStateContainer()
|
29
|
+
QMODULE.enum_decls = {}
|
30
|
+
QMODULE.type_decls = {}
|
31
|
+
QMODULE.qstruct_decls = {}
|
32
|
+
QMODULE.native_defs = {}
|
33
|
+
QMODULE.constants = {}
|
34
|
+
QMODULE.functions_compilation_metadata = {}
|
35
|
+
QMODULE.generative_functions = {}
|
36
|
+
QMODULE.function_dependencies = defaultdict(list)
|
@@ -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"
|