classiq 0.74.0__py3-none-any.whl → 0.75.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- classiq/analyzer/show_interactive_hack.py +1 -1
- classiq/applications/qnn/qlayer.py +9 -0
- classiq/interface/_version.py +1 -1
- classiq/interface/debug_info/debug_info.py +2 -11
- classiq/interface/generator/expressions/proxies/classical/classical_array_proxy.py +5 -5
- classiq/interface/generator/expressions/proxies/classical/utils.py +2 -2
- classiq/interface/generator/functions/classical_type.py +30 -0
- classiq/interface/generator/functions/type_name.py +25 -3
- classiq/interface/generator/generated_circuit_data.py +11 -25
- classiq/interface/generator/synthesis_metadata/synthesis_execution_data.py +10 -13
- classiq/interface/helpers/versioned_model.py +12 -0
- classiq/interface/interface_version.py +1 -1
- classiq/interface/model/handle_binding.py +12 -0
- classiq/interface/model/statement_block.py +9 -1
- classiq/interface/model/within_apply_operation.py +12 -0
- classiq/model_expansions/atomic_expression_functions_defs.py +24 -8
- classiq/model_expansions/closure.py +13 -0
- classiq/model_expansions/evaluators/argument_types.py +6 -5
- classiq/model_expansions/evaluators/type_type_match.py +2 -2
- classiq/model_expansions/generative_functions.py +14 -8
- classiq/model_expansions/interpreters/base_interpreter.py +10 -13
- classiq/model_expansions/interpreters/frontend_generative_interpreter.py +21 -0
- classiq/model_expansions/interpreters/generative_interpreter.py +12 -0
- classiq/model_expansions/quantum_operations/allocate.py +22 -11
- classiq/model_expansions/quantum_operations/assignment_result_processor.py +2 -0
- classiq/model_expansions/quantum_operations/call_emitter.py +0 -5
- classiq/model_expansions/quantum_operations/emitter.py +1 -5
- classiq/model_expansions/quantum_operations/expression_evaluator.py +1 -0
- classiq/model_expansions/quantum_operations/handle_evaluator.py +1 -0
- classiq/model_expansions/transformers/model_renamer.py +3 -1
- classiq/model_expansions/visitors/symbolic_param_inference.py +197 -0
- classiq/qmod/qmod_variable.py +23 -1
- classiq/qmod/symbolic_expr.py +8 -2
- classiq/qmod/write_qmod.py +5 -1
- {classiq-0.74.0.dist-info → classiq-0.75.0.dist-info}/METADATA +1 -1
- {classiq-0.74.0.dist-info → classiq-0.75.0.dist-info}/RECORD +37 -36
- {classiq-0.74.0.dist-info → classiq-0.75.0.dist-info}/WHEEL +1 -1
@@ -4,21 +4,42 @@ import os
|
|
4
4
|
from pydantic import ValidationError
|
5
5
|
|
6
6
|
from classiq.interface.exceptions import ClassiqError
|
7
|
+
from classiq.interface.model.allocate import Allocate
|
8
|
+
from classiq.interface.model.native_function_definition import NativeFunctionDefinition
|
7
9
|
from classiq.interface.model.quantum_function_call import QuantumFunctionCall
|
10
|
+
from classiq.interface.model.quantum_function_declaration import (
|
11
|
+
NamedParamsQuantumFunctionDeclaration,
|
12
|
+
)
|
8
13
|
from classiq.interface.source_reference import SourceReference
|
9
14
|
|
10
15
|
from classiq.model_expansions.closure import FunctionClosure, GenerativeFunctionClosure
|
11
16
|
from classiq.model_expansions.interpreters.generative_interpreter import (
|
12
17
|
GenerativeInterpreter,
|
13
18
|
)
|
19
|
+
from classiq.model_expansions.quantum_operations.allocate import AllocateEmitter
|
14
20
|
from classiq.model_expansions.quantum_operations.quantum_function_call import (
|
15
21
|
DeclarativeQuantumFunctionCallEmitter,
|
16
22
|
)
|
17
23
|
from classiq.model_expansions.scope import Scope
|
24
|
+
from classiq.model_expansions.visitors.symbolic_param_inference import (
|
25
|
+
SymbolicParamInference,
|
26
|
+
)
|
18
27
|
from classiq.qmod.model_state_container import QMODULE
|
19
28
|
|
20
29
|
|
21
30
|
class FrontendGenerativeInterpreter(GenerativeInterpreter):
|
31
|
+
def infer_symbolic_parameters(
|
32
|
+
self,
|
33
|
+
functions: list[NativeFunctionDefinition],
|
34
|
+
additional_signatures: (
|
35
|
+
list[NamedParamsQuantumFunctionDeclaration] | None
|
36
|
+
) = None,
|
37
|
+
) -> None:
|
38
|
+
SymbolicParamInference(functions, additional_signatures).infer()
|
39
|
+
|
40
|
+
def emit_allocate(self, allocate: Allocate) -> None:
|
41
|
+
AllocateEmitter(self, allow_symbolic_size=True).emit(allocate)
|
42
|
+
|
22
43
|
def emit_quantum_function_call(self, call: QuantumFunctionCall) -> None:
|
23
44
|
DeclarativeQuantumFunctionCallEmitter(self).emit(call)
|
24
45
|
|
@@ -93,6 +93,18 @@ class GenerativeInterpreter(BaseInterpreter):
|
|
93
93
|
add_generative_functions_to_scope(
|
94
94
|
generative_functions, self._top_level_scope, override_atomic=True
|
95
95
|
)
|
96
|
+
self.infer_symbolic_parameters(
|
97
|
+
model.functions, [gen_func.func_decl for gen_func in generative_functions]
|
98
|
+
)
|
99
|
+
|
100
|
+
def infer_symbolic_parameters(
|
101
|
+
self,
|
102
|
+
functions: list[NativeFunctionDefinition],
|
103
|
+
additional_signatures: (
|
104
|
+
list[NamedParamsQuantumFunctionDeclaration] | None
|
105
|
+
) = None,
|
106
|
+
) -> None:
|
107
|
+
pass
|
96
108
|
|
97
109
|
def evaluate_lambda(self, function: QuantumLambdaFunction) -> Evaluated:
|
98
110
|
func_decl = NamedParamsQuantumFunctionDeclaration(
|
@@ -1,7 +1,10 @@
|
|
1
|
+
from typing import TYPE_CHECKING
|
2
|
+
|
3
|
+
import sympy
|
4
|
+
|
1
5
|
from classiq.interface.debug_info.debug_info import FunctionDebugInfo
|
2
6
|
from classiq.interface.exceptions import ClassiqValueError
|
3
7
|
from classiq.interface.generator.expressions.expression import Expression
|
4
|
-
from classiq.interface.generator.generated_circuit_data import OperationLevel
|
5
8
|
from classiq.interface.model.allocate import Allocate
|
6
9
|
from classiq.interface.model.handle_binding import NestedHandleBinding
|
7
10
|
from classiq.interface.model.quantum_type import QuantumBitvector
|
@@ -10,8 +13,17 @@ from classiq.model_expansions.evaluators.quantum_type_utils import copy_type_inf
|
|
10
13
|
from classiq.model_expansions.quantum_operations.emitter import Emitter
|
11
14
|
from classiq.model_expansions.scope import QuantumSymbol
|
12
15
|
|
16
|
+
if TYPE_CHECKING:
|
17
|
+
from classiq.model_expansions.interpreters.base_interpreter import BaseInterpreter
|
18
|
+
|
13
19
|
|
14
20
|
class AllocateEmitter(Emitter[Allocate]):
|
21
|
+
def __init__(
|
22
|
+
self, interpreter: "BaseInterpreter", allow_symbolic_size: bool = False
|
23
|
+
) -> None:
|
24
|
+
super().__init__(interpreter)
|
25
|
+
self._allow_symbolic_size = allow_symbolic_size
|
26
|
+
|
15
27
|
def emit(self, allocate: Allocate, /) -> bool:
|
16
28
|
target: QuantumSymbol = self._interpreter.evaluate(allocate.target).as_type(
|
17
29
|
QuantumSymbol
|
@@ -22,10 +34,10 @@ class AllocateEmitter(Emitter[Allocate]):
|
|
22
34
|
f"Cannot allocate partial quantum variable {str(target.handle)!r}"
|
23
35
|
)
|
24
36
|
|
25
|
-
|
37
|
+
size_expr = self._get_var_size(target, allocate.size)
|
26
38
|
allocate = allocate.model_copy(
|
27
39
|
update=dict(
|
28
|
-
size=Expression(expr=
|
40
|
+
size=Expression(expr=size_expr),
|
29
41
|
target=target.handle,
|
30
42
|
back_ref=allocate.uuid,
|
31
43
|
)
|
@@ -34,27 +46,29 @@ class AllocateEmitter(Emitter[Allocate]):
|
|
34
46
|
self.emit_statement(allocate)
|
35
47
|
return True
|
36
48
|
|
37
|
-
def _get_var_size(self, target: QuantumSymbol, size: Expression | None) ->
|
49
|
+
def _get_var_size(self, target: QuantumSymbol, size: Expression | None) -> str:
|
38
50
|
if size is None:
|
39
51
|
if not target.quantum_type.is_evaluated:
|
40
52
|
raise ClassiqValueError(
|
41
53
|
f"Could not infer the size of variable {str(target.handle)!r}"
|
42
54
|
)
|
43
|
-
return target.quantum_type.size_in_bits
|
55
|
+
return str(target.quantum_type.size_in_bits)
|
44
56
|
|
45
57
|
size_value = self._interpreter.evaluate(size).value
|
58
|
+
if self._allow_symbolic_size and isinstance(size_value, sympy.Basic):
|
59
|
+
return str(size_value)
|
46
60
|
if not isinstance(size_value, (int, float)):
|
47
61
|
raise ClassiqValueError(
|
48
62
|
f"The number of allocated qubits must be an integer. Got "
|
49
63
|
f"{str(size_value)!r}"
|
50
64
|
)
|
51
|
-
|
65
|
+
size_expr = str(size_value)
|
52
66
|
copy_type_information(
|
53
|
-
QuantumBitvector(length=Expression(expr=
|
67
|
+
QuantumBitvector(length=Expression(expr=size_expr)),
|
54
68
|
target.quantum_type,
|
55
69
|
str(target.handle),
|
56
70
|
)
|
57
|
-
return
|
71
|
+
return size_expr
|
58
72
|
|
59
73
|
def _register_debug_info(self, allocate: Allocate) -> None:
|
60
74
|
if (
|
@@ -67,9 +81,6 @@ class AllocateEmitter(Emitter[Allocate]):
|
|
67
81
|
parameters["num_qubits"] = allocate.size.expr
|
68
82
|
self._debug_info[allocate.uuid] = FunctionDebugInfo(
|
69
83
|
name="allocate",
|
70
|
-
parameters=parameters,
|
71
|
-
level=OperationLevel.QMOD_STATEMENT,
|
72
|
-
is_allocate_or_free=True,
|
73
84
|
port_to_passed_variable_map={"ARG": str(allocate.target)},
|
74
85
|
node=allocate._as_back_ref(),
|
75
86
|
)
|
@@ -36,6 +36,8 @@ class AssignmentResultProcessor(Emitter[QuantumAssignmentOperation]):
|
|
36
36
|
|
37
37
|
def _update_result_type(self, op: ArithmeticOperation) -> None:
|
38
38
|
expr = self._evaluate_expression(op.expression)
|
39
|
+
if len(self._get_classical_vars_in_expression(expr)):
|
40
|
+
return
|
39
41
|
symbols = self._get_symbols_in_expression(expr)
|
40
42
|
expr_str = rename_variables(
|
41
43
|
expr.expr,
|
@@ -24,7 +24,6 @@ from classiq.interface.generator.expressions.proxies.classical.qmod_struct_insta
|
|
24
24
|
from classiq.interface.generator.functions.port_declaration import (
|
25
25
|
PortDeclarationDirection,
|
26
26
|
)
|
27
|
-
from classiq.interface.generator.generated_circuit_data import OperationLevel
|
28
27
|
from classiq.interface.model.classical_parameter_declaration import (
|
29
28
|
ClassicalParameterDeclaration,
|
30
29
|
)
|
@@ -63,7 +62,6 @@ from classiq.model_expansions.quantum_operations.emitter import (
|
|
63
62
|
from classiq.model_expansions.scope import Evaluated, QuantumSymbol, Scope
|
64
63
|
from classiq.model_expansions.transformers.var_splitter import VarSplitter
|
65
64
|
from classiq.model_expansions.utils.text_utils import are, readable_list, s
|
66
|
-
from classiq.qmod.builtins.functions import free
|
67
65
|
from classiq.qmod.semantics.validation.signature_validation import (
|
68
66
|
validate_function_signature,
|
69
67
|
)
|
@@ -188,7 +186,6 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
|
|
188
186
|
positional_args=new_positional_args,
|
189
187
|
back_ref=self._get_back_ref(propagated_debug_info),
|
190
188
|
)
|
191
|
-
is_allocate_or_free = new_call.func_name == free.func_decl.name
|
192
189
|
|
193
190
|
port_to_passed_variable_map = {
|
194
191
|
arg_decl.name: str(evaluated_arg.value.handle)
|
@@ -197,8 +194,6 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
|
|
197
194
|
}
|
198
195
|
self._debug_info[new_call.uuid] = FunctionDebugInfo(
|
199
196
|
name=new_call.func_name,
|
200
|
-
level=OperationLevel.QMOD_FUNCTION_CALL,
|
201
|
-
is_allocate_or_free=is_allocate_or_free,
|
202
197
|
port_to_passed_variable_map=port_to_passed_variable_map,
|
203
198
|
node=new_call._as_back_ref(),
|
204
199
|
)
|
@@ -13,7 +13,6 @@ import sympy
|
|
13
13
|
|
14
14
|
from classiq.interface.debug_info.debug_info import (
|
15
15
|
DebugInfoCollection,
|
16
|
-
new_function_debug_info_by_node,
|
17
16
|
)
|
18
17
|
from classiq.interface.exceptions import ClassiqInternalExpansionError
|
19
18
|
from classiq.interface.generator.expressions.evaluated_expression import (
|
@@ -161,10 +160,7 @@ class Emitter(Generic[QuantumStatementT], ABC):
|
|
161
160
|
self._update_captured_classical_vars(statement)
|
162
161
|
if isinstance(statement, QuantumOperation):
|
163
162
|
self._update_captured_vars(statement)
|
164
|
-
|
165
|
-
self._interpreter._model.debug_info[statement.uuid] = (
|
166
|
-
new_function_debug_info_by_node(statement) # type:ignore[arg-type]
|
167
|
-
)
|
163
|
+
self._interpreter.add_to_debug_info(statement)
|
168
164
|
self._builder.emit_statement(statement)
|
169
165
|
|
170
166
|
def _update_captured_classical_vars(self, stmt: QuantumStatement) -> None:
|
@@ -92,7 +92,9 @@ class _ReplaceSplitVarsExpressions(ModelTransformer):
|
|
92
92
|
) -> QuantumExpressionOperation:
|
93
93
|
op = cast(QuantumExpressionOperation, self.generic_visit(op))
|
94
94
|
previous_var_handles = list(op._var_handles)
|
95
|
-
op._var_handles = self.visit(
|
95
|
+
op._var_handles = _ReplaceSplitVarsHandles(self._symbol_mapping).visit(
|
96
|
+
op._var_handles
|
97
|
+
)
|
96
98
|
op._var_types = {
|
97
99
|
new_handle.name: op._var_types.get(
|
98
100
|
new_handle.name, op._var_types[previous_handle.name]
|
@@ -0,0 +1,197 @@
|
|
1
|
+
import ast
|
2
|
+
from collections.abc import Iterator, Mapping, Sequence
|
3
|
+
from contextlib import contextmanager
|
4
|
+
from itertools import chain, zip_longest
|
5
|
+
from typing import Optional, cast
|
6
|
+
|
7
|
+
from classiq.interface.generator.expressions.atomic_expression_functions import (
|
8
|
+
CLASSICAL_ATTRIBUTES,
|
9
|
+
)
|
10
|
+
from classiq.interface.generator.expressions.expression import Expression
|
11
|
+
from classiq.interface.generator.functions.classical_type import ClassicalType
|
12
|
+
from classiq.interface.generator.functions.type_name import TypeName
|
13
|
+
from classiq.interface.helpers.pydantic_model_helpers import nameables_to_dict
|
14
|
+
from classiq.interface.model.classical_parameter_declaration import (
|
15
|
+
AnonClassicalParameterDeclaration,
|
16
|
+
)
|
17
|
+
from classiq.interface.model.handle_binding import FieldHandleBinding, HandleBinding
|
18
|
+
from classiq.interface.model.model_visitor import ModelVisitor
|
19
|
+
from classiq.interface.model.native_function_definition import NativeFunctionDefinition
|
20
|
+
from classiq.interface.model.quantum_function_call import ArgValue, QuantumFunctionCall
|
21
|
+
from classiq.interface.model.quantum_function_declaration import (
|
22
|
+
AnonPositionalArg,
|
23
|
+
AnonQuantumFunctionDeclaration,
|
24
|
+
AnonQuantumOperandDeclaration,
|
25
|
+
NamedParamsQuantumFunctionDeclaration,
|
26
|
+
QuantumOperandDeclaration,
|
27
|
+
)
|
28
|
+
from classiq.interface.model.quantum_lambda_function import (
|
29
|
+
OperandIdentifier,
|
30
|
+
QuantumLambdaFunction,
|
31
|
+
)
|
32
|
+
|
33
|
+
from classiq.model_expansions.visitors.variable_references import VarRefCollector
|
34
|
+
|
35
|
+
|
36
|
+
def _get_expressions(arg: ArgValue) -> list[Expression]:
|
37
|
+
if isinstance(arg, Expression):
|
38
|
+
return [arg]
|
39
|
+
if isinstance(arg, HandleBinding):
|
40
|
+
return arg.expressions()
|
41
|
+
if isinstance(arg, OperandIdentifier):
|
42
|
+
return [arg.index]
|
43
|
+
if isinstance(arg, list):
|
44
|
+
return list(chain.from_iterable(_get_expressions(item) for item in arg))
|
45
|
+
return []
|
46
|
+
|
47
|
+
|
48
|
+
def _get_param_expressions(param: AnonPositionalArg) -> list[Expression]:
|
49
|
+
if isinstance(param, AnonClassicalParameterDeclaration):
|
50
|
+
return param.classical_type.expressions
|
51
|
+
if isinstance(param, AnonQuantumOperandDeclaration):
|
52
|
+
return list(
|
53
|
+
chain.from_iterable(
|
54
|
+
_get_param_expressions(nested_param)
|
55
|
+
for nested_param in param.positional_arg_declarations
|
56
|
+
)
|
57
|
+
)
|
58
|
+
return param.quantum_type.expressions
|
59
|
+
|
60
|
+
|
61
|
+
class SymbolicParamInference(ModelVisitor):
|
62
|
+
def __init__(
|
63
|
+
self,
|
64
|
+
functions: list[NativeFunctionDefinition],
|
65
|
+
additional_signatures: (
|
66
|
+
list[NamedParamsQuantumFunctionDeclaration] | None
|
67
|
+
) = None,
|
68
|
+
) -> None:
|
69
|
+
self._functions = nameables_to_dict(functions)
|
70
|
+
self._additional_signatures = (
|
71
|
+
{}
|
72
|
+
if additional_signatures is None
|
73
|
+
else nameables_to_dict(additional_signatures)
|
74
|
+
)
|
75
|
+
self._inferred_funcs: set[str] = set()
|
76
|
+
self._call_stack: list[str] = []
|
77
|
+
self._scope: Mapping[str, ClassicalType] = {}
|
78
|
+
self._scope_operands: dict[str, QuantumOperandDeclaration] = {}
|
79
|
+
|
80
|
+
def infer(self) -> None:
|
81
|
+
for func in self._functions.values():
|
82
|
+
self._infer_func_params(func)
|
83
|
+
|
84
|
+
def _is_recursive_call(self, func: str) -> bool:
|
85
|
+
return func in self._call_stack
|
86
|
+
|
87
|
+
@contextmanager
|
88
|
+
def function_context(
|
89
|
+
self,
|
90
|
+
func_name: Optional[str],
|
91
|
+
scope: Mapping[str, ClassicalType],
|
92
|
+
scope_operands: dict[str, QuantumOperandDeclaration],
|
93
|
+
) -> Iterator[None]:
|
94
|
+
if func_name is not None:
|
95
|
+
self._call_stack.append(func_name)
|
96
|
+
prev_scope = self._scope
|
97
|
+
self._scope = scope
|
98
|
+
prev_scope_ops = self._scope_operands
|
99
|
+
self._scope_operands = scope_operands
|
100
|
+
yield
|
101
|
+
self._scope = prev_scope
|
102
|
+
self._scope_operands = prev_scope_ops
|
103
|
+
if func_name is not None:
|
104
|
+
self._call_stack.pop()
|
105
|
+
|
106
|
+
def _infer_func_params(self, func: NativeFunctionDefinition) -> None:
|
107
|
+
if func.name in self._inferred_funcs:
|
108
|
+
return
|
109
|
+
scope = {param.name: param.classical_type for param in func.param_decls}
|
110
|
+
scope_operands = func.operand_declarations_dict
|
111
|
+
with self.function_context(func.name, scope, scope_operands):
|
112
|
+
for param in func.positional_arg_declarations:
|
113
|
+
for expr in _get_param_expressions(param):
|
114
|
+
self._process_compile_time_expression(expr.expr)
|
115
|
+
self._set_enums_generative(func)
|
116
|
+
self.visit(func.body)
|
117
|
+
self._inferred_funcs.add(func.name)
|
118
|
+
|
119
|
+
def _set_enums_generative(self, decl: AnonQuantumFunctionDeclaration) -> None:
|
120
|
+
for param in decl.positional_arg_declarations:
|
121
|
+
if (
|
122
|
+
isinstance(param, AnonClassicalParameterDeclaration)
|
123
|
+
and param.name is not None
|
124
|
+
and isinstance(param.classical_type, TypeName)
|
125
|
+
and param.classical_type.is_enum
|
126
|
+
):
|
127
|
+
self._scope[param.name].set_generative()
|
128
|
+
|
129
|
+
def visit_QuantumLambdaFunction(self, func: QuantumLambdaFunction) -> None:
|
130
|
+
func.set_op_decl(func.func_decl.model_copy(deep=True))
|
131
|
+
scope = dict(self._scope) | {
|
132
|
+
cast(str, param.name): param.classical_type
|
133
|
+
for param in func.named_func_decl.param_decls
|
134
|
+
}
|
135
|
+
scope_operands = self._scope_operands | nameables_to_dict(
|
136
|
+
cast(
|
137
|
+
Sequence[QuantumOperandDeclaration],
|
138
|
+
func.named_func_decl.operand_declarations,
|
139
|
+
)
|
140
|
+
)
|
141
|
+
with self.function_context(None, scope, scope_operands):
|
142
|
+
self._set_enums_generative(func.named_func_decl)
|
143
|
+
self.visit(func.body)
|
144
|
+
|
145
|
+
def visit_QuantumFunctionCall(self, call: QuantumFunctionCall) -> None:
|
146
|
+
self._process_compile_time_expressions(call.function)
|
147
|
+
name = call.func_name
|
148
|
+
if self._is_recursive_call(name):
|
149
|
+
return # Recursion is not fully supported
|
150
|
+
params = self._get_params(call)
|
151
|
+
for param, arg in zip_longest(params, call.positional_args):
|
152
|
+
if (
|
153
|
+
not isinstance(param, AnonClassicalParameterDeclaration)
|
154
|
+
or param.classical_type.is_generative
|
155
|
+
):
|
156
|
+
self._process_compile_time_expressions(arg)
|
157
|
+
else:
|
158
|
+
for expr in _get_expressions(arg):
|
159
|
+
self._process_nested_compile_time_expression(expr.expr)
|
160
|
+
self.generic_visit(call)
|
161
|
+
|
162
|
+
def _get_params(self, call: QuantumFunctionCall) -> Sequence[AnonPositionalArg]:
|
163
|
+
name = call.func_name
|
164
|
+
if name in self._scope_operands:
|
165
|
+
return self._scope_operands[name].positional_arg_declarations
|
166
|
+
elif name in self._functions:
|
167
|
+
func = self._functions[name]
|
168
|
+
self._infer_func_params(func)
|
169
|
+
return func.positional_arg_declarations
|
170
|
+
elif name in self._additional_signatures:
|
171
|
+
return self._additional_signatures[name].positional_arg_declarations
|
172
|
+
return call.func_decl.positional_arg_declarations
|
173
|
+
|
174
|
+
def _process_compile_time_expressions(self, arg: ArgValue) -> None:
|
175
|
+
for expr in _get_expressions(arg):
|
176
|
+
self._process_compile_time_expression(expr.expr)
|
177
|
+
|
178
|
+
def _process_compile_time_expression(self, expr: str) -> None:
|
179
|
+
vrc = VarRefCollector(
|
180
|
+
ignore_duplicated_handles=True, ignore_sympy_symbols=True, unevaluated=True
|
181
|
+
)
|
182
|
+
vrc.visit(ast.parse(expr))
|
183
|
+
for handle in vrc.var_handles:
|
184
|
+
if handle.name in self._scope and (
|
185
|
+
not isinstance(handle, FieldHandleBinding)
|
186
|
+
or handle.field not in CLASSICAL_ATTRIBUTES
|
187
|
+
):
|
188
|
+
self._scope[handle.name].set_generative()
|
189
|
+
|
190
|
+
def _process_nested_compile_time_expression(self, expr: str) -> None:
|
191
|
+
vrc = VarRefCollector(
|
192
|
+
ignore_duplicated_handles=True, ignore_sympy_symbols=True, unevaluated=True
|
193
|
+
)
|
194
|
+
vrc.visit(ast.parse(expr))
|
195
|
+
for handle in vrc.var_handles:
|
196
|
+
for nested_expr in handle.expressions():
|
197
|
+
self._process_compile_time_expression(nested_expr.expr)
|
classiq/qmod/qmod_variable.py
CHANGED
@@ -22,7 +22,12 @@ from typing import ( # type: ignore[attr-defined]
|
|
22
22
|
|
23
23
|
from typing_extensions import ParamSpec, Self, _AnnotatedAlias
|
24
24
|
|
25
|
-
from classiq.interface.exceptions import
|
25
|
+
from classiq.interface.exceptions import (
|
26
|
+
ClassiqInternalError,
|
27
|
+
ClassiqNotImplementedError,
|
28
|
+
ClassiqValueError,
|
29
|
+
)
|
30
|
+
from classiq.interface.generator.arith.register_user_input import RegisterArithmeticInfo
|
26
31
|
from classiq.interface.generator.expressions.expression import Expression
|
27
32
|
from classiq.interface.generator.expressions.non_symbolic_expr import NonSymbolicExpr
|
28
33
|
from classiq.interface.generator.expressions.proxies.quantum.qmod_qarray_proxy import (
|
@@ -340,6 +345,23 @@ class QNum(Generic[_P], QScalar):
|
|
340
345
|
return interpret_expression(str(self.is_signed))
|
341
346
|
return CParamScalar(f"get_field({self}, 'is_signed')")
|
342
347
|
|
348
|
+
def get_maximal_bounds(self) -> tuple[float, float]:
|
349
|
+
if not is_generative_mode():
|
350
|
+
raise ClassiqNotImplementedError(
|
351
|
+
"get_maximal_bounds() is supported in generative mode only"
|
352
|
+
)
|
353
|
+
|
354
|
+
if TYPE_CHECKING:
|
355
|
+
assert isinstance(self.size, int)
|
356
|
+
assert isinstance(self.is_signed, bool)
|
357
|
+
assert isinstance(self.fraction_digits, int)
|
358
|
+
|
359
|
+
return RegisterArithmeticInfo.get_maximal_bounds(
|
360
|
+
size=self.size,
|
361
|
+
is_signed=self.is_signed,
|
362
|
+
fraction_places=self.fraction_digits,
|
363
|
+
)
|
364
|
+
|
343
365
|
# Support comma-separated generic args in older Python versions
|
344
366
|
if sys.version_info[0:2] < (3, 10):
|
345
367
|
|
classiq/qmod/symbolic_expr.py
CHANGED
@@ -4,6 +4,8 @@ import ast
|
|
4
4
|
from enum import Enum as PythonEnum
|
5
5
|
from typing import Any
|
6
6
|
|
7
|
+
import sympy
|
8
|
+
|
7
9
|
from classiq.qmod.utilities import qmod_val_to_expr_str
|
8
10
|
|
9
11
|
|
@@ -33,10 +35,14 @@ class SymbolicExpr(Symbolic):
|
|
33
35
|
|
34
36
|
@staticmethod
|
35
37
|
def _binary_op(lhs: Any, rhs: Any, op: str) -> SymbolicExpr:
|
36
|
-
if not isinstance(
|
38
|
+
if not isinstance(
|
39
|
+
lhs, (SymbolicExpr, int, float, bool, PythonEnum, sympy.Basic)
|
40
|
+
):
|
37
41
|
raise TypeError(f"Invalid lhs argument {lhs!r} for binary operation {op!r}")
|
38
42
|
|
39
|
-
if not isinstance(
|
43
|
+
if not isinstance(
|
44
|
+
rhs, (SymbolicExpr, int, float, bool, PythonEnum, sympy.Basic)
|
45
|
+
):
|
40
46
|
raise TypeError(f"Invalid lhs argument {rhs!r} for binary operation {op!r}")
|
41
47
|
|
42
48
|
lhs_str = qmod_val_to_expr_str(lhs)
|
classiq/qmod/write_qmod.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import json
|
2
2
|
from pathlib import Path
|
3
|
-
from typing import Optional, Union
|
3
|
+
from typing import Any, Optional, Union
|
4
4
|
|
5
5
|
from classiq.interface.model.model import Model, SerializedModel
|
6
6
|
|
@@ -17,6 +17,8 @@ def write_qmod(
|
|
17
17
|
name: str,
|
18
18
|
directory: Optional[Path] = None,
|
19
19
|
decimal_precision: int = DEFAULT_DECIMAL_PRECISION,
|
20
|
+
*args: Any,
|
21
|
+
**kwargs: Any,
|
20
22
|
) -> None:
|
21
23
|
"""
|
22
24
|
Creates a native Qmod file from a serialized model and outputs the synthesis options (Preferences and Constraints) to a file.
|
@@ -27,6 +29,8 @@ def write_qmod(
|
|
27
29
|
name: The name to save the file by.
|
28
30
|
directory: The directory to save the files in. If None, the current working directory is used.
|
29
31
|
decimal_precision: The number of decimal places to use for numbers, set to 4 by default.
|
32
|
+
args: (placeholder)
|
33
|
+
kwargs: (placeholder)
|
30
34
|
|
31
35
|
Returns:
|
32
36
|
None
|