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/model/logic_flow.py
DELETED
@@ -1,149 +0,0 @@
|
|
1
|
-
from dataclasses import dataclass
|
2
|
-
from typing import Any, Optional, Tuple
|
3
|
-
|
4
|
-
import networkx as nx
|
5
|
-
|
6
|
-
from classiq.interface.generator.function_params import PortDirection
|
7
|
-
from classiq.interface.generator.quantum_function_call import (
|
8
|
-
ZERO_INDICATOR,
|
9
|
-
SynthesisQuantumFunctionCall,
|
10
|
-
)
|
11
|
-
|
12
|
-
from classiq.exceptions import ClassiqWiringError
|
13
|
-
from classiq.model import logic_flow_change_handler
|
14
|
-
from classiq.quantum_register import QReg, Qubit
|
15
|
-
|
16
|
-
|
17
|
-
# We need the dataclass to be hashable for inserting it into the graph,
|
18
|
-
# hence the dataclass is frozen.
|
19
|
-
@dataclass(frozen=True)
|
20
|
-
class _Pin:
|
21
|
-
pin_name: str
|
22
|
-
index: int
|
23
|
-
func_call: Optional[SynthesisQuantumFunctionCall]
|
24
|
-
io: PortDirection # We need to store PortDirection because a function may have an input and an output pin with the same name
|
25
|
-
|
26
|
-
def __str__(self) -> str:
|
27
|
-
return f"{self.pin_name}[{self.index}]"
|
28
|
-
|
29
|
-
|
30
|
-
@dataclass(frozen=True)
|
31
|
-
class _ZeroPin(_Pin):
|
32
|
-
def __init__(self, io: PortDirection) -> None:
|
33
|
-
super().__init__(pin_name=ZERO_INDICATOR, index=0, func_call=None, io=io)
|
34
|
-
|
35
|
-
def __str__(self) -> str:
|
36
|
-
return ZERO_INDICATOR
|
37
|
-
|
38
|
-
|
39
|
-
class _StrictDiGraph(nx.DiGraph):
|
40
|
-
def add_edge(self, u_of_edge: Any, v_of_edge: Any, **attr: Any) -> None:
|
41
|
-
if u_of_edge in self and v_of_edge in self[u_of_edge]:
|
42
|
-
raise ClassiqWiringError(
|
43
|
-
f"Cannot reconnect an already connected edge: {u_of_edge}, {v_of_edge}"
|
44
|
-
)
|
45
|
-
super().add_edge(u_of_edge, v_of_edge, **attr)
|
46
|
-
|
47
|
-
|
48
|
-
INVALID_QUBITS_ERROR_MESSAGE = (
|
49
|
-
"Cannot use a QReg with consumed or uninitialized qubits:"
|
50
|
-
)
|
51
|
-
|
52
|
-
|
53
|
-
class LogicFlowBuilder:
|
54
|
-
def __init__(self) -> None:
|
55
|
-
self._logic_flow_graph = _StrictDiGraph()
|
56
|
-
self._connect_qubit_func = {
|
57
|
-
PortDirection.Input: self._connect_qubit_to_func_call,
|
58
|
-
PortDirection.Output: self._connect_func_call_to_qubit,
|
59
|
-
}
|
60
|
-
|
61
|
-
def _is_qubit_available(self, qubit: Qubit) -> bool:
|
62
|
-
return qubit in self._logic_flow_graph.nodes
|
63
|
-
|
64
|
-
def _validate_qreg(self, qreg: QReg) -> None:
|
65
|
-
invalid_qubit_indices = [
|
66
|
-
i
|
67
|
-
for i, qubit in enumerate(qreg.qubits)
|
68
|
-
if not self._is_qubit_available(qubit)
|
69
|
-
]
|
70
|
-
if invalid_qubit_indices:
|
71
|
-
raise ClassiqWiringError(
|
72
|
-
f"{INVALID_QUBITS_ERROR_MESSAGE} {invalid_qubit_indices}"
|
73
|
-
)
|
74
|
-
|
75
|
-
def _verify_no_loops(self, dest_node: SynthesisQuantumFunctionCall) -> None:
|
76
|
-
if not nx.is_directed_acyclic_graph(self._logic_flow_graph):
|
77
|
-
raise ClassiqWiringError(f"Cannot wire function {dest_node} to itself")
|
78
|
-
|
79
|
-
def _connect_qubit_to_func_call(
|
80
|
-
self,
|
81
|
-
qubit: Qubit,
|
82
|
-
dest_pin: _Pin,
|
83
|
-
dest_node: Optional[SynthesisQuantumFunctionCall],
|
84
|
-
) -> None:
|
85
|
-
if dest_node is not None:
|
86
|
-
self._logic_flow_graph.add_edge(dest_pin, dest_node)
|
87
|
-
source_node, source_pin = self._get_source_node_and_pin(qubit)
|
88
|
-
# relabel_nodes replaces a node with another (inplace and keeping the edges)
|
89
|
-
nx.relabel_nodes(self._logic_flow_graph, {qubit: dest_pin}, copy=False)
|
90
|
-
logic_flow_change_handler.handle_inner_connection(
|
91
|
-
source_node,
|
92
|
-
str(source_pin),
|
93
|
-
str(dest_pin),
|
94
|
-
dest_node,
|
95
|
-
)
|
96
|
-
|
97
|
-
def _get_source_node_and_pin(
|
98
|
-
self, qubit: Qubit
|
99
|
-
) -> Tuple[SynthesisQuantumFunctionCall, _Pin]:
|
100
|
-
source_pin = next(self._logic_flow_graph.predecessors(qubit))
|
101
|
-
source_node = next(self._logic_flow_graph.predecessors(source_pin))
|
102
|
-
return source_node, source_pin
|
103
|
-
|
104
|
-
def _connect_func_call_to_qubit(
|
105
|
-
self, qubit: Qubit, source_pin: _Pin, source_node: SynthesisQuantumFunctionCall
|
106
|
-
) -> None:
|
107
|
-
self._logic_flow_graph.add_edge(source_node, source_pin)
|
108
|
-
self._logic_flow_graph.add_edge(source_pin, qubit)
|
109
|
-
|
110
|
-
def _connect_io(
|
111
|
-
self,
|
112
|
-
io: PortDirection,
|
113
|
-
func_node: SynthesisQuantumFunctionCall,
|
114
|
-
pin_name: str,
|
115
|
-
qreg: QReg,
|
116
|
-
pin_indices: Optional[range] = None,
|
117
|
-
) -> None:
|
118
|
-
if pin_indices is None:
|
119
|
-
pin_indices = range(len(qreg))
|
120
|
-
pins = [_Pin(pin_name, i, func_node, io) for i in pin_indices]
|
121
|
-
for pin, qubit in zip(pins, qreg.qubits):
|
122
|
-
self._connect_qubit_func[io](qubit, pin, func_node)
|
123
|
-
|
124
|
-
def connect_qreg_to_func_call(
|
125
|
-
self,
|
126
|
-
source: QReg,
|
127
|
-
dest_pin_name: str,
|
128
|
-
dest_func_call: SynthesisQuantumFunctionCall,
|
129
|
-
pin_indices: Optional[range] = None,
|
130
|
-
) -> None:
|
131
|
-
self._validate_qreg(source)
|
132
|
-
self._connect_io(
|
133
|
-
PortDirection.Input, dest_func_call, dest_pin_name, source, pin_indices
|
134
|
-
)
|
135
|
-
self._verify_no_loops(dest_func_call)
|
136
|
-
|
137
|
-
def connect_func_call_to_qreg(
|
138
|
-
self,
|
139
|
-
source_func_call: SynthesisQuantumFunctionCall,
|
140
|
-
source_pin_name: str,
|
141
|
-
dest: QReg,
|
142
|
-
) -> None:
|
143
|
-
self._connect_io(PortDirection.Output, source_func_call, source_pin_name, dest)
|
144
|
-
|
145
|
-
def connect_qreg_to_zero(self, source: QReg) -> None:
|
146
|
-
for qubit in source.qubits:
|
147
|
-
self._connect_qubit_to_func_call(
|
148
|
-
qubit, _ZeroPin(PortDirection.Output), None
|
149
|
-
)
|
@@ -1,71 +0,0 @@
|
|
1
|
-
from typing import Optional
|
2
|
-
|
3
|
-
from classiq.interface.generator.function_params import PortDirection
|
4
|
-
from classiq.interface.generator.quantum_function_call import (
|
5
|
-
ZERO_INDICATOR,
|
6
|
-
SynthesisQuantumFunctionCall,
|
7
|
-
)
|
8
|
-
|
9
|
-
|
10
|
-
def _get_io_wire_name(
|
11
|
-
name: str, call: SynthesisQuantumFunctionCall, io: PortDirection
|
12
|
-
) -> str:
|
13
|
-
if io == PortDirection.Input:
|
14
|
-
return f"{io.name}:{name}->{call.name}:{name}"
|
15
|
-
else:
|
16
|
-
return f"{call.name}:{name}->{io.name}:{name}"
|
17
|
-
|
18
|
-
|
19
|
-
def _get_wire_name(
|
20
|
-
source_call: SynthesisQuantumFunctionCall,
|
21
|
-
source_pin_name: str,
|
22
|
-
dest_pin_name: str,
|
23
|
-
dest_call: Optional[SynthesisQuantumFunctionCall],
|
24
|
-
) -> str:
|
25
|
-
if dest_call is None:
|
26
|
-
assert dest_pin_name == ZERO_INDICATOR
|
27
|
-
return ZERO_INDICATOR
|
28
|
-
return f"{source_call.name}:{source_pin_name}->{dest_call.name}:{dest_pin_name}"
|
29
|
-
|
30
|
-
|
31
|
-
def _set_model_output(
|
32
|
-
call: SynthesisQuantumFunctionCall, pin_name: str, wire_name: str
|
33
|
-
) -> None:
|
34
|
-
call_outputs = dict(call.outputs_dict)
|
35
|
-
call_outputs[pin_name] = wire_name
|
36
|
-
call.outputs = call_outputs
|
37
|
-
if wire_name != ZERO_INDICATOR:
|
38
|
-
call.non_zero_output_wires.append(wire_name)
|
39
|
-
|
40
|
-
|
41
|
-
def _set_model_input(
|
42
|
-
call: Optional[SynthesisQuantumFunctionCall], pin_name: str, wire_name: str
|
43
|
-
) -> None:
|
44
|
-
if call is None:
|
45
|
-
return
|
46
|
-
call_inputs = dict(call.inputs_dict)
|
47
|
-
call_inputs[pin_name] = wire_name
|
48
|
-
call.inputs = call_inputs
|
49
|
-
call.non_zero_input_wires.append(wire_name)
|
50
|
-
|
51
|
-
|
52
|
-
def handle_inner_connection(
|
53
|
-
source_call: SynthesisQuantumFunctionCall,
|
54
|
-
source_pin_name: str,
|
55
|
-
dest_pin_name: str,
|
56
|
-
dest_call: Optional[SynthesisQuantumFunctionCall],
|
57
|
-
) -> None:
|
58
|
-
wire_name = _get_wire_name(source_call, source_pin_name, dest_pin_name, dest_call)
|
59
|
-
_set_model_output(source_call, source_pin_name, wire_name)
|
60
|
-
_set_model_input(dest_call, dest_pin_name, wire_name)
|
61
|
-
|
62
|
-
|
63
|
-
def handle_io_connection(
|
64
|
-
io_dir: PortDirection, call: SynthesisQuantumFunctionCall, io_name: str
|
65
|
-
) -> str:
|
66
|
-
wire_name = _get_io_wire_name(io_name, call, io_dir)
|
67
|
-
if io_dir == PortDirection.Input:
|
68
|
-
_set_model_input(call, io_name, wire_name)
|
69
|
-
else:
|
70
|
-
_set_model_output(call, io_name, wire_name)
|
71
|
-
return wire_name
|
classiq/model/model.py
DELETED
@@ -1,246 +0,0 @@
|
|
1
|
-
"""Model module, implementing facilities for designing models and generating circuits using Classiq platform."""
|
2
|
-
|
3
|
-
from __future__ import annotations
|
4
|
-
|
5
|
-
import logging
|
6
|
-
import tempfile
|
7
|
-
from contextlib import nullcontext
|
8
|
-
from typing import IO, Any, ContextManager, Dict, List, Mapping, Optional, Union, cast
|
9
|
-
|
10
|
-
from classiq.interface.chemistry.operator import PauliOperator
|
11
|
-
from classiq.interface.executor.execution_preferences import (
|
12
|
-
ExecutionPreferences,
|
13
|
-
QaeWithQpeEstimationMethod,
|
14
|
-
)
|
15
|
-
from classiq.interface.generator.expressions.enums import Optimizer
|
16
|
-
from classiq.interface.generator.function_params import IOName
|
17
|
-
from classiq.interface.generator.functions import SynthesisNativeFunctionDefinition
|
18
|
-
from classiq.interface.generator.model import (
|
19
|
-
Constraints,
|
20
|
-
Preferences,
|
21
|
-
SynthesisModel as APIModel,
|
22
|
-
)
|
23
|
-
from classiq.interface.generator.model.model import MAIN_FUNCTION_NAME, SerializedModel
|
24
|
-
from classiq.interface.generator.quantum_function_call import (
|
25
|
-
SynthesisQuantumFunctionCall,
|
26
|
-
)
|
27
|
-
|
28
|
-
from classiq._internals.async_utils import AsyncifyABC
|
29
|
-
from classiq.exceptions import ClassiqError, ClassiqValueError
|
30
|
-
from classiq.model import function_handler
|
31
|
-
from classiq.quantum_functions.function_library import FunctionLibrary
|
32
|
-
from classiq.quantum_register import QReg, QRegGenericAlias
|
33
|
-
|
34
|
-
_logger = logging.getLogger(__name__)
|
35
|
-
|
36
|
-
_SupportedIO = Union[IO, str]
|
37
|
-
|
38
|
-
# TODO: Add docstrings for auto generated methods.
|
39
|
-
|
40
|
-
|
41
|
-
ILLEGAL_SETTING_MSG = "Illegal value type provided"
|
42
|
-
|
43
|
-
|
44
|
-
def _pauli_str_to_enums(pauli_str: str) -> str:
|
45
|
-
return ", ".join(f"Pauli.{pauli_term}" for pauli_term in pauli_str)
|
46
|
-
|
47
|
-
|
48
|
-
def _pauli_operator_to_qmod(hamiltonian: PauliOperator) -> str:
|
49
|
-
if not all(isinstance(summand[1], complex) for summand in hamiltonian.pauli_list):
|
50
|
-
raise ClassiqValueError(
|
51
|
-
"Supporting only Hamiltonian with numeric coefficients."
|
52
|
-
)
|
53
|
-
return ", ".join(
|
54
|
-
f"struct_literal(PauliTerm, pauli=[{_pauli_str_to_enums(pauli)}], coefficient={cast(complex, coeff).real})"
|
55
|
-
for pauli, coeff in hamiltonian.pauli_list
|
56
|
-
)
|
57
|
-
|
58
|
-
|
59
|
-
def _file_handler(fp: Optional[_SupportedIO], mode: str = "r") -> ContextManager[IO]:
|
60
|
-
if fp is None:
|
61
|
-
temp_file = tempfile.NamedTemporaryFile(mode, suffix=".qmod", delete=False)
|
62
|
-
print(f"Using temporary file: {temp_file.name!r}")
|
63
|
-
return temp_file
|
64
|
-
|
65
|
-
if isinstance(fp, str):
|
66
|
-
return open(fp, mode)
|
67
|
-
|
68
|
-
return nullcontext(fp)
|
69
|
-
|
70
|
-
|
71
|
-
DEFAULT_RESULT_NAME = "result"
|
72
|
-
DEFAULT_AMPLITUDE_ESTIMATION_RESULT_NAME = "estimation"
|
73
|
-
|
74
|
-
|
75
|
-
class Model(function_handler.FunctionHandler, metaclass=AsyncifyABC):
|
76
|
-
"""Facility to generate circuits, based on the model."""
|
77
|
-
|
78
|
-
def __init__(self, **kwargs: Any) -> None:
|
79
|
-
"""Init self."""
|
80
|
-
super().__init__()
|
81
|
-
self._model = APIModel(**kwargs)
|
82
|
-
|
83
|
-
@classmethod
|
84
|
-
def from_model(cls, model: APIModel) -> Model:
|
85
|
-
return cls(**dict(model))
|
86
|
-
|
87
|
-
@property
|
88
|
-
def _body(
|
89
|
-
self,
|
90
|
-
) -> List[SynthesisQuantumFunctionCall]:
|
91
|
-
return self._model.body
|
92
|
-
|
93
|
-
@property
|
94
|
-
def constraints(self) -> Constraints:
|
95
|
-
"""Get the constraints aggregated in self.
|
96
|
-
|
97
|
-
Returns:
|
98
|
-
The constraints data.
|
99
|
-
"""
|
100
|
-
return self._model.constraints
|
101
|
-
|
102
|
-
@constraints.setter
|
103
|
-
def constraints(self, value: Any) -> None:
|
104
|
-
if not isinstance(value, Constraints):
|
105
|
-
raise ClassiqError(ILLEGAL_SETTING_MSG)
|
106
|
-
self._model.constraints = value
|
107
|
-
|
108
|
-
@property
|
109
|
-
def preferences(self) -> Preferences:
|
110
|
-
"""Get the preferences aggregated in self.
|
111
|
-
|
112
|
-
Returns:
|
113
|
-
The preferences data.
|
114
|
-
"""
|
115
|
-
return self._model.preferences
|
116
|
-
|
117
|
-
@preferences.setter
|
118
|
-
def preferences(self, value: Any) -> None:
|
119
|
-
if not isinstance(value, Preferences):
|
120
|
-
raise ClassiqError(ILLEGAL_SETTING_MSG)
|
121
|
-
self._model.preferences = value
|
122
|
-
|
123
|
-
@property
|
124
|
-
def execution_preferences(self) -> ExecutionPreferences:
|
125
|
-
return self._model.execution_preferences
|
126
|
-
|
127
|
-
@execution_preferences.setter
|
128
|
-
def execution_preferences(self, value: Any) -> None:
|
129
|
-
if not isinstance(value, ExecutionPreferences):
|
130
|
-
raise ClassiqError(ILLEGAL_SETTING_MSG)
|
131
|
-
self._model.execution_preferences = value
|
132
|
-
|
133
|
-
def create_inputs(
|
134
|
-
self, inputs: Mapping[IOName, QRegGenericAlias]
|
135
|
-
) -> Dict[IOName, QReg]:
|
136
|
-
qregs = super().create_inputs(inputs=inputs)
|
137
|
-
self._model.set_inputs(inputs, self.input_wires)
|
138
|
-
return qregs
|
139
|
-
|
140
|
-
def set_outputs(self, outputs: Mapping[IOName, QReg]) -> None:
|
141
|
-
super().set_outputs(outputs=outputs)
|
142
|
-
self._model.set_outputs(outputs, self.output_wires)
|
143
|
-
|
144
|
-
def include_library(self, library: FunctionLibrary) -> None:
|
145
|
-
"""Includes a user-defined custom function library.
|
146
|
-
|
147
|
-
Args:
|
148
|
-
library (FunctionLibrary): The custom function library.
|
149
|
-
"""
|
150
|
-
super().include_library(library=library)
|
151
|
-
# It is important that the .functions list is shared between the library and
|
152
|
-
# the model, as it is modified in-place
|
153
|
-
self._model.functions = library._data
|
154
|
-
library.remove_function_definition(MAIN_FUNCTION_NAME)
|
155
|
-
self._model.functions.append(
|
156
|
-
SynthesisNativeFunctionDefinition(name=MAIN_FUNCTION_NAME)
|
157
|
-
)
|
158
|
-
|
159
|
-
def get_model(self) -> SerializedModel:
|
160
|
-
return self._model.get_model()
|
161
|
-
|
162
|
-
def create_library(self) -> None:
|
163
|
-
self._function_library = FunctionLibrary(*self._model.functions)
|
164
|
-
self._model.functions = self._function_library._data
|
165
|
-
|
166
|
-
def sample(
|
167
|
-
self,
|
168
|
-
execution_params: Optional[Dict[str, float]] = None,
|
169
|
-
) -> None:
|
170
|
-
execution_params = execution_params or dict()
|
171
|
-
|
172
|
-
self._model.classical_execution_code += classical_sample_function(
|
173
|
-
execution_params=execution_params
|
174
|
-
)
|
175
|
-
|
176
|
-
def vqe(
|
177
|
-
self,
|
178
|
-
hamiltonian: PauliOperator,
|
179
|
-
maximize: bool,
|
180
|
-
optimizer: Optimizer,
|
181
|
-
max_iteration: int,
|
182
|
-
initial_point: Optional[List[int]] = None,
|
183
|
-
tolerance: float = 0,
|
184
|
-
step_size: float = 0,
|
185
|
-
skip_compute_variance: bool = False,
|
186
|
-
alpha_cvar: float = 1,
|
187
|
-
) -> None:
|
188
|
-
initial_point = initial_point or []
|
189
|
-
vqe_classical_code = f"""
|
190
|
-
{DEFAULT_RESULT_NAME} = vqe(
|
191
|
-
hamiltonian=[{_pauli_operator_to_qmod(hamiltonian)}],
|
192
|
-
maximize={maximize},
|
193
|
-
initial_point={initial_point},
|
194
|
-
optimizer=Optimizer.{optimizer.name},
|
195
|
-
max_iteration={max_iteration},
|
196
|
-
tolerance={tolerance},
|
197
|
-
step_size={step_size},
|
198
|
-
skip_compute_variance={skip_compute_variance},
|
199
|
-
alpha_cvar={alpha_cvar}
|
200
|
-
)
|
201
|
-
save({{{DEFAULT_RESULT_NAME!r}: {DEFAULT_RESULT_NAME}}})
|
202
|
-
"""
|
203
|
-
|
204
|
-
self._model.classical_execution_code += vqe_classical_code
|
205
|
-
|
206
|
-
def iqae(
|
207
|
-
self,
|
208
|
-
epsilon: float,
|
209
|
-
alpha: float,
|
210
|
-
execution_params: Optional[Dict[str, float]] = None,
|
211
|
-
) -> None:
|
212
|
-
execution_params = execution_params or {}
|
213
|
-
|
214
|
-
iqae_classical_code = f"""
|
215
|
-
{DEFAULT_RESULT_NAME} = iqae(
|
216
|
-
epsilon={epsilon},
|
217
|
-
alpha={alpha},
|
218
|
-
execution_params={execution_params}
|
219
|
-
)
|
220
|
-
save({{{DEFAULT_RESULT_NAME!r}: {DEFAULT_RESULT_NAME}}})
|
221
|
-
"""
|
222
|
-
|
223
|
-
self._model.classical_execution_code += iqae_classical_code
|
224
|
-
|
225
|
-
def post_process_amplitude_estimation(
|
226
|
-
self,
|
227
|
-
estimation_register_size: int,
|
228
|
-
estimation_method: QaeWithQpeEstimationMethod,
|
229
|
-
) -> None:
|
230
|
-
postprocess_classical_code = f"""
|
231
|
-
{DEFAULT_AMPLITUDE_ESTIMATION_RESULT_NAME} = qae_with_qpe_result_post_processing(
|
232
|
-
{estimation_register_size},
|
233
|
-
{estimation_method},
|
234
|
-
{DEFAULT_RESULT_NAME}
|
235
|
-
)
|
236
|
-
save({{{DEFAULT_AMPLITUDE_ESTIMATION_RESULT_NAME!r}: {DEFAULT_AMPLITUDE_ESTIMATION_RESULT_NAME}}})
|
237
|
-
"""
|
238
|
-
|
239
|
-
self._model.classical_execution_code += postprocess_classical_code
|
240
|
-
|
241
|
-
|
242
|
-
def classical_sample_function(execution_params: Dict[str, float]) -> str:
|
243
|
-
return f"""
|
244
|
-
{DEFAULT_RESULT_NAME} = sample({execution_params})
|
245
|
-
save({{{DEFAULT_RESULT_NAME!r}: {DEFAULT_RESULT_NAME}}})
|
246
|
-
"""
|
@@ -1,17 +0,0 @@
|
|
1
|
-
from classiq.quantum_functions.decorators import quantum_function as qfunc
|
2
|
-
from classiq.quantum_functions.function_library import (
|
3
|
-
QASM3_INTRO,
|
4
|
-
QASM_INTRO,
|
5
|
-
FunctionLibrary,
|
6
|
-
QuantumFunction,
|
7
|
-
QuantumFunctionFactory,
|
8
|
-
)
|
9
|
-
|
10
|
-
__all__ = [
|
11
|
-
"qfunc",
|
12
|
-
"QASM_INTRO",
|
13
|
-
"QASM3_INTRO",
|
14
|
-
"FunctionLibrary",
|
15
|
-
"QuantumFunction",
|
16
|
-
"QuantumFunctionFactory",
|
17
|
-
]
|
@@ -1,207 +0,0 @@
|
|
1
|
-
# type: ignore
|
2
|
-
# We can either ignore each line individually, or ignore the entire file and wait until mypy can ignore
|
3
|
-
# specific errors per-file.
|
4
|
-
import inspect
|
5
|
-
import sys
|
6
|
-
from types import FunctionType
|
7
|
-
from typing import Any, Dict, List, Tuple, Union, _GenericAlias
|
8
|
-
|
9
|
-
from classiq.interface.generator.register_role import RegisterRole as Role
|
10
|
-
|
11
|
-
from classiq.exceptions import ClassiqQFuncError
|
12
|
-
from classiq.quantum_register import AuxQReg, QReg, QRegGenericAlias, QSFixed, ZeroQReg
|
13
|
-
|
14
|
-
if sys.version_info >= (3, 9):
|
15
|
-
from types import GenericAlias
|
16
|
-
else:
|
17
|
-
GenericAlias = _GenericAlias
|
18
|
-
|
19
|
-
GenericAliasUnion = Union[GenericAlias, _GenericAlias]
|
20
|
-
|
21
|
-
|
22
|
-
class AnnotationParser:
|
23
|
-
def __init__(self, func: FunctionType) -> None:
|
24
|
-
self._func = func
|
25
|
-
|
26
|
-
self.output_types: Dict[str, GenericAlias] = {}
|
27
|
-
|
28
|
-
def parse(self) -> None:
|
29
|
-
annotations = self._func.__annotations__.copy()
|
30
|
-
|
31
|
-
# Todo: remove this `if` after introducing `Inplace`
|
32
|
-
if "return" not in annotations:
|
33
|
-
raise ClassiqQFuncError("Return value annotations not found")
|
34
|
-
|
35
|
-
self.output_values = self._unpack_output_values(annotations.pop("return"))
|
36
|
-
self.input_names, self.input_values = self._unpack_input_values(annotations)
|
37
|
-
|
38
|
-
self._validate()
|
39
|
-
|
40
|
-
def _validate(self) -> None:
|
41
|
-
self._validate_type_hints()
|
42
|
-
self._validate_qubit_amount()
|
43
|
-
self._validate_io_length()
|
44
|
-
self._validate_io_correlation()
|
45
|
-
|
46
|
-
def _validate_type_hints(self) -> None:
|
47
|
-
# Validate type of type-hints
|
48
|
-
if not all(
|
49
|
-
map(
|
50
|
-
self.is_valid_generic_alias_of_qreg,
|
51
|
-
self.input_values + self.output_values,
|
52
|
-
)
|
53
|
-
):
|
54
|
-
raise ClassiqQFuncError("Invalid GenericAlias convection")
|
55
|
-
|
56
|
-
def _validate_qubit_amount(self) -> None:
|
57
|
-
# Validate qubit amount
|
58
|
-
if sum(i.size for i in self.output_values) != sum(
|
59
|
-
i.size for i in self.input_values
|
60
|
-
):
|
61
|
-
raise ClassiqQFuncError(
|
62
|
-
"Input and output values have different amounts of qubits"
|
63
|
-
)
|
64
|
-
|
65
|
-
# Todo: Remove this validation by introducing better heuristics
|
66
|
-
# Or after introducing Inplace
|
67
|
-
def _validate_io_length(self) -> None:
|
68
|
-
# Validate amount of inputs and outputs
|
69
|
-
if len(self.input_values) != len(self.output_values):
|
70
|
-
raise ClassiqQFuncError(
|
71
|
-
"Inputs and outputs must have the same number of QRegs"
|
72
|
-
)
|
73
|
-
|
74
|
-
def _validate_io_correlation(self) -> None:
|
75
|
-
# Validate correspondence between inputs and outputs
|
76
|
-
for input_name, input_type, output_type in zip(
|
77
|
-
self.input_names, self.input_values, self.output_values
|
78
|
-
):
|
79
|
-
# Is arithmetic QReg
|
80
|
-
if issubclass(input_type.__origin__, QSFixed):
|
81
|
-
if input_type != output_type:
|
82
|
-
raise ClassiqQFuncError(
|
83
|
-
f"Arithmetic QReg must be of the same type and size in both the input and the output. Got {input_type} and {output_type}"
|
84
|
-
)
|
85
|
-
|
86
|
-
# Is Auxillary QReg
|
87
|
-
if issubclass(input_type.__origin__, AuxQReg):
|
88
|
-
if input_type != output_type:
|
89
|
-
raise ClassiqQFuncError(
|
90
|
-
f"Auxillary QReg must be of the same type and size in both the input and the output. Got {input_type} and {output_type}"
|
91
|
-
)
|
92
|
-
|
93
|
-
# Is Zero QReg
|
94
|
-
if input_type.__origin__ is ZeroQReg:
|
95
|
-
if output_type.__origin__ is QReg or issubclass(
|
96
|
-
output_type.__origin__, QSFixed
|
97
|
-
):
|
98
|
-
self.output_types[input_name] = output_type
|
99
|
-
else:
|
100
|
-
raise ClassiqQFuncError(
|
101
|
-
"Invalid output type. Any ZeroQReg in the input must have a corresponding QReg in the output"
|
102
|
-
)
|
103
|
-
|
104
|
-
@classmethod
|
105
|
-
def _unpack_output_values(
|
106
|
-
cls, output_value_type_hint: Any
|
107
|
-
) -> Tuple[GenericAlias, ...]:
|
108
|
-
# Handle QReg type hints
|
109
|
-
if cls.is_subclass_qreg(output_value_type_hint):
|
110
|
-
return (cls.to_generic_alias(output_value_type_hint),)
|
111
|
-
|
112
|
-
# Supporting both `typing._GenericAlias` and `types.GenericAlias`
|
113
|
-
if not cls.is_instance_generic_alias(output_value_type_hint):
|
114
|
-
raise ClassiqQFuncError(
|
115
|
-
"Output value type hint must be either a single QReg, `typing.Tuple[QReg, ...]` or, for python>=3.9, `tuple[QReg, ...]`"
|
116
|
-
)
|
117
|
-
|
118
|
-
# Allowing only a tuple of outputs:
|
119
|
-
if not cls.is_tuple_generic_alias(output_value_type_hint):
|
120
|
-
raise ClassiqQFuncError(
|
121
|
-
"Output value type hint must be either Tuple[QReg, ...] or tuple[QReg, ...]"
|
122
|
-
)
|
123
|
-
|
124
|
-
# This line may raise ClassiqQFuncError
|
125
|
-
return tuple(map(cls.to_generic_alias, output_value_type_hint.__args__))
|
126
|
-
|
127
|
-
def _unpack_input_values(
|
128
|
-
self, annotations: Dict[str, Any]
|
129
|
-
) -> Tuple[List[str], Tuple[GenericAlias, ...]]:
|
130
|
-
input_names = list(annotations.keys())
|
131
|
-
input_values = tuple(map(self.to_generic_alias, annotations.values()))
|
132
|
-
return input_names, input_values
|
133
|
-
|
134
|
-
@staticmethod
|
135
|
-
def to_generic_alias(obj: Any) -> GenericAlias:
|
136
|
-
# Handle GenericAlias
|
137
|
-
if isinstance(obj, (QRegGenericAlias, GenericAlias)):
|
138
|
-
return obj
|
139
|
-
# Handle _GenericAlias, for python>3.9, i.e. when GenericAlias != _GenericAlias
|
140
|
-
if isinstance(obj, _GenericAlias):
|
141
|
-
return GenericAlias(obj.__origin__, obj.__args__)
|
142
|
-
# Handle a single QReg (not GenericAlias of QReg)
|
143
|
-
elif inspect.isclass(obj) and issubclass(obj, QReg):
|
144
|
-
return GenericAlias(obj, tuple())
|
145
|
-
|
146
|
-
raise ClassiqQFuncError(f"Invalid type hint object: {obj.__class__.__name__}")
|
147
|
-
|
148
|
-
@staticmethod
|
149
|
-
def is_instance_generic_alias(obj: Any) -> bool:
|
150
|
-
return isinstance(obj, (GenericAlias, _GenericAlias))
|
151
|
-
|
152
|
-
@classmethod
|
153
|
-
def is_subclass_qreg(cls, obj: Any) -> bool:
|
154
|
-
if inspect.isclass(obj):
|
155
|
-
return issubclass(obj, QReg)
|
156
|
-
elif cls.is_instance_generic_alias(obj):
|
157
|
-
return issubclass(obj.__origin__, QReg)
|
158
|
-
return False
|
159
|
-
|
160
|
-
@staticmethod
|
161
|
-
def is_tuple_generic_alias(obj: GenericAliasUnion) -> bool:
|
162
|
-
return obj.__origin__.__name__.lower() == "tuple"
|
163
|
-
|
164
|
-
@staticmethod
|
165
|
-
def is_valid_generic_alias_of_qreg(obj: GenericAlias) -> bool:
|
166
|
-
return isinstance(obj, QRegGenericAlias)
|
167
|
-
|
168
|
-
|
169
|
-
def get_annotation_role(annotation: GenericAlias, default_role: Role) -> Role:
|
170
|
-
"""
|
171
|
-
Note: this function cannot distinguish between inputs and outputs.
|
172
|
-
Thus, for inputs, all 3 options are valid
|
173
|
-
However, for outputs:
|
174
|
-
a) we don't expect to get ZERO
|
175
|
-
b) We treat INPUT as OUTPUT
|
176
|
-
"""
|
177
|
-
ret = None
|
178
|
-
|
179
|
-
if getattr(annotation, "role", None) is not None:
|
180
|
-
ret = annotation.role
|
181
|
-
if getattr(annotation.__origin__, "role", None) is not None:
|
182
|
-
ret = annotation.role
|
183
|
-
|
184
|
-
if issubclass(annotation.__origin__, QReg) and not issubclass(
|
185
|
-
annotation.__origin__, ZeroQReg
|
186
|
-
):
|
187
|
-
ret = default_role
|
188
|
-
|
189
|
-
if issubclass(annotation.__origin__, ZeroQReg) and not issubclass(
|
190
|
-
annotation.__origin__, AuxQReg
|
191
|
-
):
|
192
|
-
ret = Role.ZERO_INPUT
|
193
|
-
|
194
|
-
if issubclass(annotation.__origin__, AuxQReg):
|
195
|
-
ret = Role.AUXILIARY
|
196
|
-
|
197
|
-
# Didn't match anything so far
|
198
|
-
if ret is None:
|
199
|
-
raise ClassiqQFuncError("Invalid annotation role")
|
200
|
-
|
201
|
-
if default_role == Role.INPUT and ret == Role.OUTPUT:
|
202
|
-
raise ClassiqQFuncError("input should not have Role.OUTPUT")
|
203
|
-
|
204
|
-
if default_role == Role.OUTPUT and ret in (Role.ZERO_INPUT, Role.INPUT):
|
205
|
-
raise ClassiqQFuncError("output should not have Role.ZERO / Role.INPUT")
|
206
|
-
|
207
|
-
return ret
|