classiq 0.37.1__py3-none-any.whl → 0.39.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 +23 -24
- classiq/_analyzer_extras/_ipywidgets_async_extension.py +1 -1
- classiq/_analyzer_extras/interactive_hardware.py +3 -3
- classiq/_internals/api_wrapper.py +37 -17
- classiq/_internals/async_utils.py +1 -74
- classiq/_internals/authentication/device.py +9 -4
- classiq/_internals/authentication/password_manager.py +25 -10
- classiq/_internals/authentication/token_manager.py +2 -2
- classiq/_internals/client.py +24 -6
- classiq/_internals/jobs.py +10 -7
- classiq/analyzer/analyzer.py +29 -29
- classiq/analyzer/analyzer_utilities.py +5 -5
- classiq/analyzer/rb.py +4 -5
- classiq/analyzer/show_interactive_hack.py +6 -6
- classiq/applications/__init__.py +1 -8
- classiq/applications/chemistry/__init__.py +6 -0
- classiq/{applications_model_constructors → applications/chemistry}/chemistry_model_constructor.py +9 -16
- classiq/applications/combinatorial_helpers/allowed_constraints.py +20 -0
- classiq/applications/combinatorial_helpers/arithmetic/arithmetic_expression.py +35 -0
- classiq/applications/combinatorial_helpers/arithmetic/isolation.py +42 -0
- classiq/applications/combinatorial_helpers/combinatorial_problem_utils.py +150 -0
- classiq/applications/combinatorial_helpers/encoding_mapping.py +107 -0
- classiq/applications/combinatorial_helpers/encoding_utils.py +122 -0
- classiq/applications/combinatorial_helpers/memory.py +77 -0
- classiq/applications/combinatorial_helpers/optimization_model.py +162 -0
- classiq/applications/combinatorial_helpers/pauli_helpers/pauli_sparsing.py +31 -0
- classiq/applications/combinatorial_helpers/pauli_helpers/pauli_utils.py +75 -0
- classiq/applications/combinatorial_helpers/py.typed +0 -0
- classiq/applications/combinatorial_helpers/pyomo_utils.py +245 -0
- classiq/applications/combinatorial_helpers/solvers/__init__.py +0 -0
- classiq/applications/combinatorial_helpers/sympy_utils.py +22 -0
- classiq/applications/combinatorial_helpers/transformations/__init__.py +0 -0
- classiq/applications/combinatorial_helpers/transformations/encoding.py +187 -0
- classiq/applications/combinatorial_helpers/transformations/fixed_variables.py +142 -0
- classiq/applications/combinatorial_helpers/transformations/ising_converter.py +122 -0
- classiq/applications/combinatorial_helpers/transformations/penalty.py +32 -0
- classiq/applications/combinatorial_helpers/transformations/penalty_support.py +37 -0
- classiq/applications/combinatorial_helpers/transformations/sign_seperation.py +75 -0
- classiq/applications/combinatorial_helpers/transformations/slack_variables.py +88 -0
- classiq/applications/combinatorial_optimization/__init__.py +13 -2
- classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +134 -0
- classiq/applications/finance/__init__.py +3 -2
- classiq/{applications_model_constructors → applications/finance}/finance_model_constructor.py +27 -30
- classiq/applications/grover/__init__.py +11 -0
- classiq/{applications_model_constructors → applications/grover}/grover_model_constructor.py +20 -91
- classiq/applications/libraries/__init__.py +0 -0
- classiq/applications/libraries/qmci_library.py +35 -0
- classiq/applications/qnn/circuit_utils.py +2 -2
- classiq/applications/qnn/gradients/quantum_gradient.py +2 -2
- classiq/applications/qnn/types.py +2 -2
- classiq/applications/qsvm/__init__.py +5 -1
- classiq/applications/qsvm/qsvm.py +4 -7
- classiq/applications/qsvm/qsvm_data_generation.py +2 -5
- classiq/exceptions.py +43 -1
- classiq/execution/all_hardware_devices.py +13 -0
- classiq/executor.py +12 -10
- classiq/interface/_version.py +1 -1
- classiq/interface/analyzer/analysis_params.py +6 -3
- classiq/interface/analyzer/result.py +12 -8
- classiq/interface/applications/qsvm.py +17 -3
- classiq/interface/ast_node.py +23 -0
- classiq/interface/backend/backend_preferences.py +4 -2
- classiq/interface/backend/pydantic_backend.py +3 -1
- classiq/interface/backend/quantum_backend_providers.py +1 -0
- classiq/interface/chemistry/fermionic_operator.py +15 -13
- classiq/interface/chemistry/ground_state_problem.py +18 -3
- classiq/interface/chemistry/molecule.py +8 -6
- classiq/interface/chemistry/operator.py +20 -14
- classiq/interface/combinatorial_optimization/examples/ascending_sequence.py +1 -1
- classiq/interface/combinatorial_optimization/examples/greater_than_ilp.py +1 -1
- classiq/interface/combinatorial_optimization/examples/ilp.py +2 -1
- classiq/interface/combinatorial_optimization/examples/integer_portfolio_optimization.py +2 -2
- classiq/interface/combinatorial_optimization/examples/mds.py +2 -1
- classiq/interface/combinatorial_optimization/examples/mht.py +8 -3
- classiq/interface/combinatorial_optimization/examples/mis.py +4 -1
- classiq/interface/combinatorial_optimization/examples/mvc.py +2 -1
- classiq/interface/combinatorial_optimization/examples/set_cover.py +2 -1
- classiq/interface/combinatorial_optimization/examples/tsp.py +4 -3
- classiq/interface/combinatorial_optimization/examples/tsp_digraph.py +6 -2
- classiq/interface/combinatorial_optimization/mht_qaoa_input.py +9 -3
- classiq/interface/executor/aws_execution_cost.py +4 -3
- classiq/interface/executor/estimation.py +2 -2
- classiq/interface/executor/execution_preferences.py +5 -34
- classiq/interface/executor/execution_request.py +15 -48
- classiq/interface/executor/optimizer_preferences.py +22 -13
- classiq/interface/executor/{quantum_program.py → quantum_code.py} +21 -15
- classiq/interface/executor/quantum_instruction_set.py +2 -1
- classiq/interface/executor/register_initialization.py +1 -3
- classiq/interface/executor/result.py +41 -10
- classiq/interface/executor/vqe_result.py +2 -2
- classiq/interface/finance/function_input.py +17 -4
- classiq/interface/finance/gaussian_model_input.py +3 -1
- classiq/interface/finance/log_normal_model_input.py +3 -1
- classiq/interface/finance/model_input.py +2 -0
- classiq/interface/generator/amplitude_loading.py +6 -3
- classiq/interface/generator/application_apis/__init__.py +1 -0
- classiq/interface/generator/application_apis/arithmetic_declarations.py +14 -0
- classiq/interface/generator/arith/argument_utils.py +14 -4
- classiq/interface/generator/arith/arithmetic.py +3 -1
- classiq/interface/generator/arith/arithmetic_arg_type_validator.py +12 -13
- classiq/interface/generator/arith/arithmetic_expression_abc.py +4 -1
- classiq/interface/generator/arith/arithmetic_expression_parser.py +8 -2
- classiq/interface/generator/arith/arithmetic_expression_validator.py +16 -2
- classiq/interface/generator/arith/arithmetic_operations.py +5 -10
- classiq/interface/generator/arith/ast_node_rewrite.py +1 -1
- classiq/interface/generator/arith/binary_ops.py +202 -54
- classiq/interface/generator/arith/extremum_operations.py +5 -3
- classiq/interface/generator/arith/logical_ops.py +4 -2
- classiq/interface/generator/arith/machine_precision.py +3 -0
- classiq/interface/generator/arith/number_utils.py +34 -44
- classiq/interface/generator/arith/register_user_input.py +21 -1
- classiq/interface/generator/arith/unary_ops.py +16 -25
- classiq/interface/generator/builtin_api_builder.py +0 -5
- classiq/interface/generator/chemistry_function_params.py +4 -4
- classiq/interface/generator/commuting_pauli_exponentiation.py +3 -1
- classiq/interface/generator/compiler_keywords.py +4 -0
- classiq/interface/generator/complex_type.py +3 -10
- classiq/interface/generator/constant.py +2 -3
- classiq/interface/generator/control_state.py +5 -3
- classiq/interface/generator/credit_risk_example/linear_gci.py +10 -3
- classiq/interface/generator/credit_risk_example/weighted_adder.py +14 -4
- classiq/interface/generator/expressions/atomic_expression_functions.py +5 -3
- classiq/interface/generator/expressions/evaluated_expression.py +18 -4
- classiq/interface/generator/expressions/expression.py +3 -5
- classiq/interface/generator/expressions/qmod_qscalar_proxy.py +33 -0
- classiq/interface/generator/expressions/sympy_supported_expressions.py +2 -1
- classiq/interface/generator/finance.py +1 -1
- classiq/interface/generator/function_params.py +7 -6
- classiq/interface/generator/functions/__init__.py +2 -2
- classiq/interface/generator/functions/builtins/__init__.py +15 -0
- classiq/interface/generator/functions/builtins/core_library/__init__.py +14 -0
- classiq/interface/generator/functions/builtins/core_library/chemistry_functions.py +0 -0
- classiq/interface/generator/functions/builtins/internal_operators.py +62 -0
- classiq/interface/generator/functions/{core_lib_declarations/quantum_functions/std_lib_functions.py → builtins/open_lib_functions.py} +612 -219
- classiq/interface/generator/functions/builtins/quantum_operators.py +37 -0
- classiq/interface/generator/functions/classical_type.py +2 -4
- classiq/interface/generator/functions/foreign_function_definition.py +12 -4
- classiq/interface/generator/functions/function_declaration.py +2 -2
- classiq/interface/generator/functions/function_implementation.py +8 -4
- classiq/interface/generator/functions/native_function_definition.py +4 -2
- classiq/interface/generator/functions/register.py +4 -2
- classiq/interface/generator/functions/register_mapping_data.py +14 -10
- classiq/interface/generator/generated_circuit_data.py +2 -2
- classiq/interface/generator/grover_operator.py +5 -3
- classiq/interface/generator/hamiltonian_evolution/suzuki_trotter.py +5 -1
- classiq/interface/generator/hardware/hardware_data.py +6 -4
- classiq/interface/generator/hardware_efficient_ansatz.py +25 -8
- classiq/interface/generator/hartree_fock.py +13 -3
- classiq/interface/generator/linear_pauli_rotations.py +3 -1
- classiq/interface/generator/mcu.py +5 -3
- classiq/interface/generator/mcx.py +7 -5
- classiq/interface/generator/model/classical_main_validator.py +1 -1
- classiq/interface/generator/model/constraints.py +2 -1
- classiq/interface/generator/model/model.py +12 -20
- classiq/interface/generator/model/preferences/preferences.py +4 -3
- classiq/interface/generator/oracles/custom_oracle.py +4 -2
- classiq/interface/generator/oracles/oracle_abc.py +2 -2
- classiq/interface/generator/qpe.py +6 -4
- classiq/interface/generator/qsvm.py +5 -8
- classiq/interface/generator/quantum_function_call.py +21 -16
- classiq/interface/generator/{generated_circuit.py → quantum_program.py} +10 -14
- classiq/interface/generator/range_types.py +3 -1
- classiq/interface/generator/slice_parsing_utils.py +8 -3
- classiq/interface/generator/standard_gates/controlled_standard_gates.py +4 -2
- classiq/interface/generator/state_preparation/metrics.py +2 -1
- classiq/interface/generator/state_preparation/state_preparation.py +7 -5
- classiq/interface/generator/state_propagator.py +16 -5
- classiq/interface/generator/types/builtin_struct_declarations/__init__.py +0 -1
- classiq/interface/generator/types/struct_declaration.py +10 -7
- classiq/interface/generator/ucc.py +6 -4
- classiq/interface/generator/unitary_gate.py +7 -3
- classiq/interface/generator/validations/flow_graph.py +6 -4
- classiq/interface/generator/validations/validator_functions.py +6 -4
- classiq/interface/hardware.py +2 -2
- classiq/interface/helpers/custom_encoders.py +3 -0
- classiq/interface/helpers/pydantic_model_helpers.py +0 -6
- classiq/interface/helpers/validation_helpers.py +1 -1
- classiq/interface/helpers/versioned_model.py +4 -1
- classiq/interface/ide/show.py +2 -2
- classiq/interface/jobs.py +72 -3
- classiq/interface/model/bind_operation.py +18 -11
- classiq/interface/model/call_synthesis_data.py +68 -0
- classiq/interface/model/classical_if.py +13 -0
- classiq/interface/model/classical_parameter_declaration.py +2 -3
- classiq/interface/model/control.py +16 -0
- classiq/interface/model/handle_binding.py +3 -2
- classiq/interface/model/inplace_binary_operation.py +2 -2
- classiq/interface/model/invert.py +10 -0
- classiq/interface/model/model.py +29 -22
- classiq/interface/model/native_function_definition.py +3 -5
- classiq/interface/model/power.py +12 -0
- classiq/interface/model/quantum_expressions/amplitude_loading_operation.py +9 -4
- classiq/interface/model/quantum_expressions/control_state.py +2 -2
- classiq/interface/model/quantum_function_call.py +33 -142
- classiq/interface/model/quantum_function_declaration.py +8 -0
- classiq/interface/model/quantum_if_operation.py +4 -5
- classiq/interface/model/quantum_lambda_function.py +58 -0
- classiq/{quantum_register.py → interface/model/quantum_register.py} +17 -9
- classiq/interface/model/quantum_statement.py +3 -2
- classiq/interface/model/quantum_type.py +58 -59
- classiq/interface/model/quantum_variable_declaration.py +3 -3
- classiq/interface/model/repeat.py +13 -0
- classiq/interface/model/resolvers/function_call_resolver.py +26 -0
- classiq/interface/model/statement_block.py +49 -0
- classiq/interface/model/validations/handles_validator.py +16 -18
- classiq/interface/model/within_apply_operation.py +11 -0
- classiq/interface/pyomo_extension/pyomo_sympy_bimap.py +4 -1
- classiq/interface/server/routes.py +5 -4
- classiq/qmod/__init__.py +13 -6
- classiq/qmod/builtins/classical_execution_primitives.py +27 -36
- classiq/qmod/builtins/classical_functions.py +22 -12
- classiq/qmod/builtins/functions.py +272 -328
- classiq/qmod/builtins/operations.py +171 -35
- classiq/qmod/builtins/structs.py +15 -15
- classiq/qmod/cfunc.py +42 -0
- classiq/qmod/classical_function.py +6 -14
- classiq/qmod/declaration_inferrer.py +12 -21
- classiq/qmod/expression_query.py +23 -0
- classiq/qmod/model_state_container.py +2 -0
- classiq/qmod/native/__init__.py +0 -0
- classiq/qmod/native/expression_to_qmod.py +189 -0
- classiq/qmod/native/pretty_printer.py +340 -0
- classiq/qmod/qfunc.py +27 -0
- classiq/qmod/qmod_constant.py +100 -0
- classiq/qmod/qmod_parameter.py +36 -13
- classiq/qmod/qmod_struct.py +3 -3
- classiq/qmod/qmod_variable.py +148 -31
- classiq/qmod/quantum_callable.py +1 -0
- classiq/qmod/quantum_expandable.py +18 -19
- classiq/qmod/quantum_function.py +41 -8
- classiq/qmod/symbolic.py +48 -5
- classiq/qmod/symbolic_expr.py +9 -0
- classiq/qmod/utilities.py +13 -0
- classiq/qmod/write_qmod.py +39 -0
- {classiq-0.37.1.dist-info → classiq-0.39.0.dist-info}/METADATA +2 -1
- {classiq-0.37.1.dist-info → classiq-0.39.0.dist-info}/RECORD +244 -225
- {classiq-0.37.1.dist-info → classiq-0.39.0.dist-info}/WHEEL +1 -1
- classiq/applications/benchmarking/__init__.py +0 -9
- classiq/applications/benchmarking/mirror_benchmarking.py +0 -67
- classiq/applications/numpy_utils.py +0 -37
- classiq/applications_model_constructors/__init__.py +0 -17
- classiq/applications_model_constructors/combinatorial_optimization_model_constructor.py +0 -178
- classiq/applications_model_constructors/libraries/qmci_library.py +0 -109
- classiq/builtin_functions/__init__.py +0 -43
- classiq/builtin_functions/amplitude_loading.py +0 -3
- classiq/builtin_functions/binary_ops.py +0 -1
- classiq/builtin_functions/exponentiation.py +0 -5
- classiq/builtin_functions/qpe.py +0 -4
- classiq/builtin_functions/qsvm.py +0 -7
- classiq/builtin_functions/range_types.py +0 -5
- classiq/builtin_functions/standard_gates.py +0 -1
- classiq/builtin_functions/state_preparation.py +0 -6
- classiq/builtin_functions/suzuki_trotter.py +0 -3
- classiq/interface/generator/expressions/qmod_qnum_proxy.py +0 -22
- classiq/interface/generator/functions/core_lib_declarations/quantum_functions/__init__.py +0 -18
- classiq/interface/generator/functions/core_lib_declarations/quantum_operators.py +0 -169
- classiq/interface/generator/types/builtin_struct_declarations/qaoa_declarations.py +0 -23
- classiq/interface/generator/types/combinatorial_problem.py +0 -26
- classiq/interface/model/numeric_reinterpretation.py +0 -25
- classiq/interface/model/operator_synthesis_data.py +0 -48
- classiq/model/__init__.py +0 -14
- classiq/model/composite_function_generator.py +0 -33
- classiq/model/function_handler.py +0 -466
- classiq/model/function_handler.pyi +0 -152
- classiq/model/logic_flow.py +0 -149
- classiq/model/logic_flow_change_handler.py +0 -71
- classiq/model/model.py +0 -246
- classiq/quantum_functions/__init__.py +0 -17
- classiq/quantum_functions/annotation_parser.py +0 -207
- classiq/quantum_functions/decorators.py +0 -22
- classiq/quantum_functions/function_library.py +0 -181
- classiq/quantum_functions/function_parser.py +0 -74
- classiq/quantum_functions/quantum_function.py +0 -236
- /classiq/{applications_model_constructors/libraries → applications/combinatorial_helpers}/__init__.py +0 -0
- /classiq/{interface/generator/functions/core_lib_declarations → applications/combinatorial_helpers/arithmetic}/__init__.py +0 -0
- /classiq/{interface/generator/functions/core_lib_declarations/quantum_functions/chemistry_functions.py → applications/combinatorial_helpers/pauli_helpers/__init__.py} +0 -0
- /classiq/{applications_model_constructors → applications}/libraries/ampltitude_estimation_library.py +0 -0
- /classiq/{applications_model_constructors → applications/qsvm}/qsvm_model_constructor.py +0 -0
- /classiq/interface/generator/functions/{core_lib_declarations/quantum_functions → builtins/core_library}/atomic_quantum_functions.py +0 -0
- /classiq/interface/generator/functions/{core_lib_declarations/quantum_functions → builtins/core_library}/exponentiation_functions.py +0 -0
classiq/qmod/qmod_parameter.py
CHANGED
@@ -1,6 +1,12 @@
|
|
1
1
|
import sys
|
2
|
-
from typing import
|
3
|
-
|
2
|
+
from typing import ( # type: ignore[attr-defined]
|
3
|
+
TYPE_CHECKING,
|
4
|
+
Any,
|
5
|
+
Generic,
|
6
|
+
TypeVar,
|
7
|
+
Union,
|
8
|
+
_GenericAlias,
|
9
|
+
)
|
4
10
|
|
5
11
|
from typing_extensions import ParamSpec
|
6
12
|
|
@@ -19,8 +25,15 @@ from classiq.qmod.symbolic_expr import Symbolic, SymbolicExpr
|
|
19
25
|
_T = TypeVar("_T")
|
20
26
|
|
21
27
|
|
22
|
-
|
23
|
-
|
28
|
+
if TYPE_CHECKING:
|
29
|
+
|
30
|
+
class QParam(SymbolicExpr, Generic[_T]):
|
31
|
+
pass
|
32
|
+
|
33
|
+
else:
|
34
|
+
|
35
|
+
class QParam(Symbolic, Generic[_T]):
|
36
|
+
pass
|
24
37
|
|
25
38
|
|
26
39
|
class QParamScalar(QParam, SymbolicExpr):
|
@@ -46,10 +59,11 @@ class QParamList(QParam):
|
|
46
59
|
)
|
47
60
|
|
48
61
|
def __len__(self) -> int:
|
49
|
-
raise
|
50
|
-
"len(<expr>) is not supported for QMod lists - use <expr>.len
|
62
|
+
raise ClassiqValueError(
|
63
|
+
"len(<expr>) is not supported for QMod lists - use <expr>.len instead"
|
51
64
|
)
|
52
65
|
|
66
|
+
@property
|
53
67
|
def len(self) -> QParamScalar:
|
54
68
|
return QParamScalar(f"len({self})")
|
55
69
|
|
@@ -63,22 +77,31 @@ class QParamStruct(QParam):
|
|
63
77
|
self._struct_type = struct_type
|
64
78
|
|
65
79
|
def __getattr__(self, field_name: str) -> QParam:
|
80
|
+
return QParamStruct.get_field(
|
81
|
+
self._qmodule, str(self), self._struct_type.name, field_name
|
82
|
+
)
|
83
|
+
|
84
|
+
@staticmethod
|
85
|
+
def get_field(
|
86
|
+
qmodule: ModelStateContainer,
|
87
|
+
variable_name: str,
|
88
|
+
struct_name: str,
|
89
|
+
field_name: str,
|
90
|
+
) -> QParam:
|
66
91
|
struct_decl = StructDeclaration.BUILTIN_STRUCT_DECLARATIONS.get(
|
67
|
-
|
92
|
+
struct_name, qmodule.type_decls.get(struct_name)
|
68
93
|
)
|
69
|
-
if struct_decl is None:
|
70
|
-
struct_decl = self._qmodule.type_decls.get(self._struct_type.name)
|
71
94
|
assert struct_decl is not None
|
72
95
|
field_type = struct_decl.variables.get(field_name)
|
73
96
|
if field_type is None:
|
74
97
|
raise ClassiqValueError(
|
75
|
-
f"Struct {
|
98
|
+
f"Struct {struct_name!r} doesn't have field {field_name!r}"
|
76
99
|
)
|
77
100
|
|
78
101
|
return create_param(
|
79
|
-
f"get_field({
|
102
|
+
f"get_field({variable_name},{field_name!r})",
|
80
103
|
field_type,
|
81
|
-
qmodule=
|
104
|
+
qmodule=qmodule,
|
82
105
|
)
|
83
106
|
|
84
107
|
|
@@ -100,7 +123,7 @@ class Array(ArrayBase[_P]):
|
|
100
123
|
def create_param(
|
101
124
|
expr_str: str, ctype: ClassicalType, qmodule: ModelStateContainer
|
102
125
|
) -> QParam:
|
103
|
-
if isinstance(ctype, ClassicalList
|
126
|
+
if isinstance(ctype, (ClassicalList, ClassicalArray)):
|
104
127
|
return QParamList(expr_str, ctype, qmodule=qmodule)
|
105
128
|
elif isinstance(ctype, Struct):
|
106
129
|
return QParamStruct(expr_str, ctype, qmodule=qmodule)
|
classiq/qmod/qmod_struct.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
import dataclasses
|
2
|
-
from typing import Any,
|
2
|
+
from typing import Any, Type
|
3
3
|
|
4
4
|
from typing_extensions import dataclass_transform
|
5
5
|
|
@@ -16,7 +16,7 @@ def _qmod_val_to_expr_str(val: Any) -> str:
|
|
16
16
|
)
|
17
17
|
return f"struct_literal({type(val).__name__}, {kwargs_str})"
|
18
18
|
|
19
|
-
if isinstance(val,
|
19
|
+
if isinstance(val, list):
|
20
20
|
elements_str = ", ".join([_qmod_val_to_expr_str(elem) for elem in val])
|
21
21
|
return f"[{elements_str}]"
|
22
22
|
|
@@ -24,7 +24,7 @@ def _qmod_val_to_expr_str(val: Any) -> str:
|
|
24
24
|
|
25
25
|
|
26
26
|
@dataclass_transform()
|
27
|
-
def
|
27
|
+
def struct(user_class: Type) -> Type:
|
28
28
|
def _new_repr(self: Any) -> str:
|
29
29
|
return _qmod_val_to_expr_str(self)
|
30
30
|
|
classiq/qmod/qmod_variable.py
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
import abc
|
2
|
+
import sys
|
2
3
|
from contextlib import contextmanager
|
3
|
-
from typing import (
|
4
|
+
from typing import ( # type: ignore[attr-defined]
|
4
5
|
TYPE_CHECKING,
|
5
6
|
Any,
|
6
7
|
ForwardRef,
|
@@ -12,8 +13,10 @@ from typing import (
|
|
12
13
|
Type,
|
13
14
|
TypeVar,
|
14
15
|
Union,
|
16
|
+
_GenericAlias,
|
15
17
|
get_args,
|
16
18
|
get_origin,
|
19
|
+
overload,
|
17
20
|
)
|
18
21
|
|
19
22
|
from typing_extensions import Annotated, ParamSpec, Self, _AnnotatedAlias
|
@@ -22,7 +25,11 @@ from classiq.interface.generator.expressions.expression import Expression
|
|
22
25
|
from classiq.interface.generator.functions.port_declaration import (
|
23
26
|
PortDeclarationDirection,
|
24
27
|
)
|
25
|
-
from classiq.interface.model.handle_binding import
|
28
|
+
from classiq.interface.model.handle_binding import (
|
29
|
+
HandleBinding,
|
30
|
+
SlicedHandleBinding,
|
31
|
+
SubscriptHandleBinding,
|
32
|
+
)
|
26
33
|
from classiq.interface.model.port_declaration import PortDeclaration
|
27
34
|
from classiq.interface.model.quantum_expressions.amplitude_loading_operation import (
|
28
35
|
AmplitudeLoadingOperation,
|
@@ -40,11 +47,14 @@ from classiq.interface.model.quantum_type import (
|
|
40
47
|
from classiq.exceptions import ClassiqValueError
|
41
48
|
from classiq.qmod.qmod_parameter import ArrayBase, QParam, QParamScalar
|
42
49
|
from classiq.qmod.quantum_callable import QCallable
|
43
|
-
from classiq.qmod.symbolic_expr import SymbolicExpr
|
50
|
+
from classiq.qmod.symbolic_expr import Symbolic, SymbolicExpr
|
44
51
|
from classiq.qmod.symbolic_type import SymbolicTypes
|
52
|
+
from classiq.qmod.utilities import version_portable_get_args
|
45
53
|
|
46
54
|
ILLEGAL_SLICING_STEP_MSG = "Slicing with a step of a quantum variable is not supported"
|
47
55
|
SLICE_OUT_OF_BOUNDS_MSG = "Slice end index out of bounds"
|
56
|
+
UNSUPPORTED_ELEMENT_TYPE = "Only QBit is supported as element type for QArray"
|
57
|
+
QARRAY_ELEMENT_NOT_SUBSCRIPTABLE = "Subscripting an element in QArray is illegal"
|
48
58
|
|
49
59
|
|
50
60
|
def _is_input_output_typehint(type_hint: Any) -> bool:
|
@@ -183,22 +193,72 @@ class QBit(QScalar):
|
|
183
193
|
return QuantumBit()
|
184
194
|
|
185
195
|
|
186
|
-
|
196
|
+
_P = ParamSpec("_P")
|
187
197
|
|
188
198
|
|
189
|
-
class QNum(Generic[
|
199
|
+
class QNum(Generic[_P], QScalar):
|
190
200
|
QMOD_TYPE = QuantumNumeric
|
191
201
|
|
202
|
+
@overload
|
203
|
+
def __init__(self, name: str):
|
204
|
+
pass
|
205
|
+
|
206
|
+
@overload
|
207
|
+
def __init__(
|
208
|
+
self,
|
209
|
+
name: str,
|
210
|
+
size: Union[int, QParam[int]],
|
211
|
+
is_signed: Union[bool, QParam[bool]],
|
212
|
+
fraction_digits: Union[int, QParam[int]],
|
213
|
+
):
|
214
|
+
pass
|
215
|
+
|
216
|
+
def __init__(
|
217
|
+
self,
|
218
|
+
name: str,
|
219
|
+
size: Union[int, QParam[int], None] = None,
|
220
|
+
is_signed: Union[bool, QParam[bool], None] = None,
|
221
|
+
fraction_digits: Union[int, QParam[int], None] = None,
|
222
|
+
):
|
223
|
+
if (
|
224
|
+
size is None
|
225
|
+
and (is_signed is not None or fraction_digits is not None)
|
226
|
+
or size is not None
|
227
|
+
and (is_signed is None or fraction_digits is None)
|
228
|
+
):
|
229
|
+
raise ClassiqValueError(
|
230
|
+
"Assign none or all of size, is_signed, and fraction_digits"
|
231
|
+
)
|
232
|
+
self._size = None if size is None else Expression(expr=str(size))
|
233
|
+
self._is_signed = None if is_signed is None else Expression(expr=str(is_signed))
|
234
|
+
self._fraction_digits = (
|
235
|
+
None if fraction_digits is None else Expression(expr=str(fraction_digits))
|
236
|
+
)
|
237
|
+
super().__init__(name)
|
238
|
+
|
192
239
|
@classmethod
|
193
240
|
def to_qmod_quantum_type(cls, type_hint: Any) -> QuantumType:
|
194
|
-
|
195
|
-
if
|
196
|
-
|
197
|
-
|
198
|
-
|
241
|
+
type_args = get_args(type_hint)
|
242
|
+
if len(type_args) == 0:
|
243
|
+
return cls.QMOD_TYPE()
|
244
|
+
type_args = type_args[0]
|
245
|
+
if len(type_args) != 3:
|
246
|
+
raise ClassiqValueError(
|
247
|
+
"QNum receives three type arguments: QNum[size: int | QParam[int], "
|
248
|
+
"is_signed: bool | QParam[bool], fraction_digits: int | QParam[int]]"
|
249
|
+
)
|
250
|
+
return cls.QMOD_TYPE(
|
251
|
+
size=Expression(expr=get_type_hint_expr(type_args[0])),
|
252
|
+
is_signed=Expression(expr=get_type_hint_expr(type_args[1])),
|
253
|
+
fraction_digits=Expression(expr=get_type_hint_expr(type_args[2])),
|
254
|
+
)
|
199
255
|
|
200
256
|
def get_qmod_type(self) -> QuantumType:
|
201
|
-
return self.QMOD_TYPE(
|
257
|
+
return self.QMOD_TYPE(
|
258
|
+
size=self._size,
|
259
|
+
is_signed=self._is_signed,
|
260
|
+
fraction_digits=self._fraction_digits,
|
261
|
+
)
|
202
262
|
|
203
263
|
@property
|
204
264
|
def size(self) -> QParamScalar:
|
@@ -212,62 +272,119 @@ class QNum(Generic[_T], QScalar):
|
|
212
272
|
def is_signed(self) -> QParamScalar:
|
213
273
|
return QParamScalar(f"is_signed({self._name})")
|
214
274
|
|
275
|
+
# Support comma-separated generic args in older Python versions
|
276
|
+
if sys.version_info[0:2] < (3, 10):
|
215
277
|
|
216
|
-
|
278
|
+
def __class_getitem__(cls, args) -> _GenericAlias:
|
279
|
+
return _GenericAlias(cls, args)
|
217
280
|
|
218
281
|
|
219
282
|
class QArray(ArrayBase[_P], QVar):
|
220
|
-
def __init__(
|
221
|
-
|
283
|
+
def __init__(
|
284
|
+
self,
|
285
|
+
name: str,
|
286
|
+
element_type: _GenericAlias = QBit,
|
287
|
+
length: Optional[Union[int, QParam[int]]] = None,
|
288
|
+
# TODO [CAD-18620]: improve type hints
|
289
|
+
slice_: Optional[Tuple[int, int]] = None,
|
290
|
+
index_: Optional[Union[int, QParam[int]]] = None,
|
291
|
+
) -> None:
|
292
|
+
if element_type is not QBit:
|
293
|
+
raise ClassiqValueError(UNSUPPORTED_ELEMENT_TYPE)
|
294
|
+
self._element_type = element_type
|
295
|
+
self._length = length
|
222
296
|
self._slice = slice_
|
297
|
+
self._index = index_
|
298
|
+
super().__init__(name)
|
223
299
|
|
224
300
|
def get_handle_binding(self) -> HandleBinding:
|
225
|
-
if self.
|
226
|
-
return
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
301
|
+
if self._index is not None:
|
302
|
+
return SubscriptHandleBinding(
|
303
|
+
name=self._name,
|
304
|
+
index=Expression(expr=str(self._index)),
|
305
|
+
)
|
306
|
+
|
307
|
+
if self._slice is not None:
|
308
|
+
return SlicedHandleBinding(
|
309
|
+
name=self._name,
|
310
|
+
start=Expression(expr=str(self._slice[0])),
|
311
|
+
end=Expression(expr=str(self._slice[1])),
|
312
|
+
)
|
313
|
+
|
314
|
+
return HandleBinding(name=self._name)
|
232
315
|
|
233
316
|
def __getitem__(self, key: Union[slice, int, QParam]) -> "QArray":
|
234
|
-
|
317
|
+
if self._index is not None:
|
318
|
+
raise ClassiqValueError(QARRAY_ELEMENT_NOT_SUBSCRIPTABLE)
|
319
|
+
|
320
|
+
# TODO [CAD-18620]: improve type hints
|
321
|
+
new_index: Optional[Any] = None
|
322
|
+
|
235
323
|
if isinstance(key, slice):
|
236
324
|
if key.step is not None:
|
237
|
-
raise
|
238
|
-
new_slice = (
|
325
|
+
raise ClassiqValueError(ILLEGAL_SLICING_STEP_MSG)
|
326
|
+
new_slice = self._get_new_slice(key.start, key.stop)
|
327
|
+
|
239
328
|
else:
|
240
329
|
if isinstance(key, QParam) and not isinstance(key, QParamScalar):
|
241
330
|
raise ClassiqValueError("Non-classical parameter for slicing")
|
242
|
-
new_slice = (
|
243
|
-
|
331
|
+
new_slice = self._get_new_slice(key, key + 1)
|
332
|
+
new_index = new_slice[0]
|
333
|
+
|
334
|
+
if (
|
335
|
+
self._slice is not None
|
336
|
+
and not isinstance(new_slice[1], Symbolic)
|
337
|
+
and not isinstance(self._slice[1], Symbolic)
|
338
|
+
and new_slice[1] > self._slice[1]
|
339
|
+
) or (
|
340
|
+
self._length is not None
|
341
|
+
and not isinstance(new_slice[1], Symbolic)
|
342
|
+
and not isinstance(self._length, Symbolic)
|
343
|
+
and new_slice[1] > self._length
|
344
|
+
):
|
244
345
|
raise ClassiqValueError(SLICE_OUT_OF_BOUNDS_MSG)
|
245
346
|
# prevent addition to local handles, since this is used for slicing existing local handles
|
246
347
|
with _no_current_expandable():
|
247
|
-
return QArray(
|
348
|
+
return QArray(
|
349
|
+
self._name, length=self._length, slice_=new_slice, index_=new_index
|
350
|
+
)
|
351
|
+
|
352
|
+
# TODO [CAD-18620]: improve type hints
|
353
|
+
def _get_new_slice(self, start: Any, end: Any) -> Tuple[Any, Any]:
|
354
|
+
if self._slice is not None:
|
355
|
+
return (self._slice[0] + start, self._slice[0] + end)
|
356
|
+
return (start, end)
|
248
357
|
|
249
358
|
def __len__(self) -> int:
|
250
|
-
raise
|
251
|
-
"len(<var>) is not supported for quantum variables - use <var>.len
|
359
|
+
raise ClassiqValueError(
|
360
|
+
"len(<var>) is not supported for quantum variables - use <var>.len instead"
|
252
361
|
)
|
253
362
|
|
254
363
|
if TYPE_CHECKING:
|
255
364
|
|
365
|
+
@property
|
256
366
|
def len(self) -> int: ...
|
257
367
|
|
258
368
|
else:
|
259
369
|
|
370
|
+
@property
|
260
371
|
def len(self) -> QParamScalar:
|
372
|
+
if self._length is not None:
|
373
|
+
return QParamScalar(f"{self._length}")
|
261
374
|
return QParamScalar(f"len({self._name})")
|
262
375
|
|
263
376
|
@classmethod
|
264
377
|
def to_qmod_quantum_type(cls, type_hint: Any) -> QuantumType:
|
265
378
|
length_expr: Optional[Expression] = None
|
266
|
-
if len(
|
267
|
-
length_expr = Expression(
|
379
|
+
if len(version_portable_get_args(type_hint)) == 2:
|
380
|
+
length_expr = Expression(
|
381
|
+
expr=get_type_hint_expr(version_portable_get_args(type_hint)[1])
|
382
|
+
)
|
268
383
|
return QuantumBitvector(length=length_expr)
|
269
384
|
|
270
385
|
def get_qmod_type(self) -> QuantumType:
|
386
|
+
if self._length is not None:
|
387
|
+
return QuantumBitvector(length=Expression(expr=str(self._length)))
|
271
388
|
return QuantumBitvector()
|
272
389
|
|
273
390
|
|
classiq/qmod/quantum_callable.py
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
import inspect
|
1
2
|
from abc import ABC
|
2
3
|
from types import TracebackType
|
3
4
|
from typing import (
|
@@ -25,14 +26,13 @@ from classiq.interface.model.quantum_function_call import (
|
|
25
26
|
ArgValue,
|
26
27
|
OperandIdentifier,
|
27
28
|
QuantumFunctionCall,
|
28
|
-
QuantumLambdaFunction,
|
29
|
-
QuantumOperand,
|
30
29
|
)
|
31
30
|
from classiq.interface.model.quantum_function_declaration import (
|
32
31
|
PositionalArg,
|
33
32
|
QuantumFunctionDeclaration,
|
34
33
|
QuantumOperandDeclaration,
|
35
34
|
)
|
35
|
+
from classiq.interface.model.quantum_lambda_function import QuantumLambdaFunction
|
36
36
|
from classiq.interface.model.quantum_statement import QuantumStatement
|
37
37
|
from classiq.interface.model.quantum_type import QuantumType
|
38
38
|
from classiq.interface.model.variable_declaration_statement import (
|
@@ -41,6 +41,7 @@ from classiq.interface.model.variable_declaration_statement import (
|
|
41
41
|
|
42
42
|
from classiq.exceptions import ClassiqValueError
|
43
43
|
from classiq.qmod.model_state_container import QMODULE, ModelStateContainer
|
44
|
+
from classiq.qmod.qmod_constant import QConstant
|
44
45
|
from classiq.qmod.qmod_parameter import QParam, QParamScalar, create_param
|
45
46
|
from classiq.qmod.qmod_variable import QVar, create_qvar_for_port_decl
|
46
47
|
from classiq.qmod.quantum_callable import QCallable, QExpandableInterface
|
@@ -96,9 +97,7 @@ class QExpandable(QCallable, QExpandableInterface, ABC):
|
|
96
97
|
for arg in self.func_decl.get_positional_arg_decls():
|
97
98
|
if isinstance(arg, ClassicalParameterDeclaration):
|
98
99
|
rename_dict = self.infer_rename_params()
|
99
|
-
actual_name = (
|
100
|
-
rename_dict[arg.name] if arg.name in rename_dict else arg.name
|
101
|
-
)
|
100
|
+
actual_name = rename_dict.get(arg.name, arg.name)
|
102
101
|
result.append(
|
103
102
|
create_param(actual_name, arg.classical_type, self._qmodule)
|
104
103
|
)
|
@@ -126,17 +125,12 @@ class QLambdaFunction(QExpandable):
|
|
126
125
|
return self._decl
|
127
126
|
|
128
127
|
def infer_rename_params(self) -> Dict[str, str]:
|
129
|
-
|
130
|
-
|
128
|
+
py_params = inspect.getfullargspec(self._py_callable)
|
129
|
+
decl_params = self.func_decl.param_decls.keys()
|
131
130
|
return {
|
132
|
-
|
133
|
-
for
|
134
|
-
|
135
|
-
[arg.name for arg in self.func_decl.get_positional_arg_decls()],
|
136
|
-
self._py_callable.__annotations__.keys(),
|
137
|
-
)
|
138
|
-
)
|
139
|
-
if decl_name != actual_name
|
131
|
+
decl_param: py_param
|
132
|
+
for decl_param, py_param in zip(decl_params, py_params.args)
|
133
|
+
if decl_param != py_param
|
140
134
|
}
|
141
135
|
|
142
136
|
|
@@ -163,16 +157,18 @@ class QTerminalCallable(QCallable):
|
|
163
157
|
return QTerminalCallable(self._decl, key)
|
164
158
|
|
165
159
|
def __len__(self) -> int:
|
166
|
-
raise
|
167
|
-
"len(<func>) is not supported for quantum callables - use <func>.len
|
160
|
+
raise ClassiqValueError(
|
161
|
+
"len(<func>) is not supported for quantum callables - use <func>.len instead (Only if it is an operand list)"
|
168
162
|
)
|
169
163
|
|
170
164
|
if TYPE_CHECKING:
|
171
165
|
|
166
|
+
@property
|
172
167
|
def len(self) -> int: ...
|
173
168
|
|
174
169
|
else:
|
175
170
|
|
171
|
+
@property
|
176
172
|
def len(self) -> QParamScalar:
|
177
173
|
if not self.is_list:
|
178
174
|
raise ClassiqValueError("Cannot get length of a non-list operand")
|
@@ -196,8 +192,8 @@ class QTerminalCallable(QCallable):
|
|
196
192
|
|
197
193
|
@overload
|
198
194
|
def prepare_arg(
|
199
|
-
arg_decl: PositionalArg, val: Union[QCallable, Callable[
|
200
|
-
) ->
|
195
|
+
arg_decl: PositionalArg, val: Union[QCallable, Callable[..., None]]
|
196
|
+
) -> QuantumLambdaFunction: ...
|
201
197
|
|
202
198
|
|
203
199
|
@overload
|
@@ -205,6 +201,9 @@ def prepare_arg(arg_decl: PositionalArg, val: Any) -> ArgValue: ...
|
|
205
201
|
|
206
202
|
|
207
203
|
def prepare_arg(arg_decl: PositionalArg, val: Any) -> ArgValue:
|
204
|
+
if isinstance(val, QConstant):
|
205
|
+
val.add_to_model()
|
206
|
+
return Expression(expr=str(val.name))
|
208
207
|
if isinstance(arg_decl, ClassicalParameterDeclaration):
|
209
208
|
return Expression(expr=str(val))
|
210
209
|
elif isinstance(arg_decl, PortDeclaration):
|
classiq/qmod/quantum_function.py
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
import ast
|
1
2
|
import functools
|
2
3
|
from typing import Any, Callable, Dict, List, Optional, Tuple, get_origin
|
3
4
|
|
@@ -13,6 +14,7 @@ from classiq.interface.model.quantum_function_declaration import (
|
|
13
14
|
from classiq.exceptions import ClassiqError
|
14
15
|
from classiq.qmod.classical_function import CFunc
|
15
16
|
from classiq.qmod.declaration_inferrer import infer_func_decl
|
17
|
+
from classiq.qmod.qmod_constant import QConstant
|
16
18
|
from classiq.qmod.qmod_parameter import QParam
|
17
19
|
from classiq.qmod.qmod_variable import QVar
|
18
20
|
from classiq.qmod.quantum_callable import QCallable, QCallableList
|
@@ -32,6 +34,10 @@ def create_model(
|
|
32
34
|
preferences: Optional[Preferences] = None,
|
33
35
|
classical_execution_function: Optional[CFunc] = None,
|
34
36
|
) -> SerializedModel:
|
37
|
+
if entry_point.func_decl.name != "main":
|
38
|
+
raise ClassiqError(
|
39
|
+
f"The entry point function must be named 'main', got '{entry_point.func_decl.name}'"
|
40
|
+
)
|
35
41
|
return entry_point.create_model(
|
36
42
|
constraints, execution_preferences, preferences, classical_execution_function
|
37
43
|
).get_model()
|
@@ -63,21 +69,23 @@ class QFunc(QExpandable):
|
|
63
69
|
) -> Model:
|
64
70
|
self._qmodule.type_decls = dict()
|
65
71
|
self._qmodule.native_defs = dict()
|
72
|
+
self._qmodule.constants = dict()
|
73
|
+
QConstant.set_current_model(self._qmodule)
|
66
74
|
self._add_native_func_def()
|
67
75
|
model_extra_settings: List[Tuple[str, Any]] = [
|
68
76
|
("constraints", constraints),
|
69
77
|
("execution_preferences", execution_preferences),
|
70
78
|
("preferences", preferences),
|
71
79
|
]
|
72
|
-
classical_execution_function
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
80
|
+
if classical_execution_function is not None:
|
81
|
+
self._add_constants_from_classical_code(classical_execution_function)
|
82
|
+
model_extra_settings.append(
|
83
|
+
("classical_execution_code", classical_execution_function.code)
|
84
|
+
)
|
77
85
|
return Model(
|
86
|
+
constants=list(self._qmodule.constants.values()),
|
78
87
|
functions=list(self._qmodule.native_defs.values()),
|
79
88
|
types=list(self._qmodule.type_decls.values()),
|
80
|
-
classical_execution_code=classical_execution_function.code,
|
81
89
|
**{key: value for key, value in model_extra_settings if value},
|
82
90
|
)
|
83
91
|
|
@@ -89,18 +97,43 @@ class QFunc(QExpandable):
|
|
89
97
|
**self.func_decl.__dict__, body=self.body
|
90
98
|
)
|
91
99
|
|
100
|
+
def _add_constants_from_classical_code(
|
101
|
+
self, classical_execution_function: CFunc
|
102
|
+
) -> None:
|
103
|
+
# FIXME: https://classiq.atlassian.net/browse/CAD-18050
|
104
|
+
# We use this visitor to add the constants that were used in the classical
|
105
|
+
# execution code to the model. In the future, if we will have a better notion
|
106
|
+
# of "QModule" and a "QConstant" will be a part of it then we may be able to
|
107
|
+
# remove the handling of the QConstants from this visitor, but I think we will
|
108
|
+
# need similar logic to allow using python variables in the classical execution
|
109
|
+
# code
|
110
|
+
class IdentifierVisitor(ast.NodeVisitor):
|
111
|
+
def visit_Name(self, node: ast.Name) -> None:
|
112
|
+
if (
|
113
|
+
node.id in classical_execution_function._caller_constants
|
114
|
+
and isinstance(
|
115
|
+
classical_execution_function._caller_constants[node.id],
|
116
|
+
QConstant,
|
117
|
+
)
|
118
|
+
):
|
119
|
+
classical_execution_function._caller_constants[
|
120
|
+
node.id
|
121
|
+
].add_to_model()
|
122
|
+
|
123
|
+
IdentifierVisitor().visit(ast.parse(classical_execution_function.code))
|
124
|
+
|
92
125
|
|
93
126
|
class ExternalQFunc(QTerminalCallable):
|
94
127
|
def __init__(self, py_callable: Callable) -> None:
|
95
128
|
decl = _lookup_qfunc(unmangle_keyword(py_callable.__name__))
|
96
129
|
if decl is None:
|
97
|
-
raise
|
130
|
+
raise ClassiqError(f"Definition of {py_callable.__name__!r} not found")
|
98
131
|
|
99
132
|
py_callable.__annotations__.pop("return", None)
|
100
133
|
if py_callable.__annotations__.keys() != {
|
101
134
|
mangle_keyword(arg.name) for arg in decl.get_positional_arg_decls()
|
102
135
|
}:
|
103
|
-
raise
|
136
|
+
raise ClassiqError(
|
104
137
|
f"Parameter type hints for {py_callable.__name__!r} do not match imported declaration"
|
105
138
|
)
|
106
139
|
super().__init__(decl)
|
classiq/qmod/symbolic.py
CHANGED
@@ -1,7 +1,10 @@
|
|
1
1
|
import sys
|
2
|
-
from typing import Tuple
|
2
|
+
from typing import List, Optional, Tuple, Type, get_args, get_origin, overload
|
3
3
|
|
4
|
-
from classiq.
|
4
|
+
from classiq.exceptions import ClassiqValueError
|
5
|
+
from classiq.qmod import model_state_container
|
6
|
+
from classiq.qmod.declaration_inferrer import python_type_to_qmod
|
7
|
+
from classiq.qmod.qmod_parameter import QParam, QParamScalar, create_param
|
5
8
|
from classiq.qmod.symbolic_expr import SymbolicExpr
|
6
9
|
from classiq.qmod.symbolic_type import SymbolicTypes
|
7
10
|
|
@@ -13,9 +16,44 @@ EulerGamma = SymbolicExpr("EulerGamma")
|
|
13
16
|
Catalan = SymbolicExpr("Catalan")
|
14
17
|
|
15
18
|
|
16
|
-
|
19
|
+
@overload
|
20
|
+
def symbolic_function(
|
21
|
+
*args: SymbolicTypes, return_type: None = None
|
22
|
+
) -> QParamScalar: ...
|
23
|
+
|
24
|
+
|
25
|
+
@overload
|
26
|
+
def symbolic_function(*args: SymbolicTypes, return_type: Type[QParam]) -> QParam: ...
|
27
|
+
|
28
|
+
|
29
|
+
def symbolic_function(
|
30
|
+
*args: SymbolicTypes, return_type: Optional[Type[QParam]] = None
|
31
|
+
) -> QParam:
|
32
|
+
qmodule = (
|
33
|
+
model_state_container.QMODULE
|
34
|
+
) # FIXME: https://classiq.atlassian.net/browse/CAD-15126
|
17
35
|
str_args = [str(x) for x in args]
|
18
|
-
|
36
|
+
expr = f"{sys._getframe(1).f_code.co_name}({','.join(str_args)})"
|
37
|
+
|
38
|
+
if return_type is None:
|
39
|
+
return QParamScalar(expr)
|
40
|
+
|
41
|
+
if get_origin(return_type) is not QParam:
|
42
|
+
raise ClassiqValueError(
|
43
|
+
f"Unsupported return type for symbolic function: {return_type}"
|
44
|
+
)
|
45
|
+
|
46
|
+
qmod_type = python_type_to_qmod(get_args(return_type)[0], qmodule=qmodule)
|
47
|
+
if qmod_type is None:
|
48
|
+
raise ClassiqValueError(
|
49
|
+
f"Unsupported return type for symbolic function: {return_type}"
|
50
|
+
)
|
51
|
+
|
52
|
+
return create_param(
|
53
|
+
expr,
|
54
|
+
qmod_type,
|
55
|
+
qmodule,
|
56
|
+
)
|
19
57
|
|
20
58
|
|
21
59
|
def sin(x: SymbolicTypes) -> QParamScalar:
|
@@ -238,6 +276,10 @@ def logical_not(x: SymbolicTypes) -> SymbolicExpr:
|
|
238
276
|
return SymbolicExpr._unary_op(x, "not")
|
239
277
|
|
240
278
|
|
279
|
+
def mod_inverse(a: SymbolicTypes, m: SymbolicTypes) -> QParamScalar:
|
280
|
+
return symbolic_function(a, m)
|
281
|
+
|
282
|
+
|
241
283
|
__all__ = [
|
242
284
|
"pi",
|
243
285
|
"E",
|
@@ -300,8 +342,9 @@ __all__ = [
|
|
300
342
|
"logical_and",
|
301
343
|
"logical_or",
|
302
344
|
"logical_not",
|
345
|
+
"mod_inverse",
|
303
346
|
]
|
304
347
|
|
305
348
|
|
306
|
-
def __dir__():
|
349
|
+
def __dir__() -> List[str]:
|
307
350
|
return __all__
|