classiq 0.37.0__py3-none-any.whl → 0.38.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 +2 -2
- classiq/_analyzer_extras/_ipywidgets_async_extension.py +1 -1
- classiq/_analyzer_extras/interactive_hardware.py +3 -3
- classiq/_internals/api_wrapper.py +24 -16
- 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 +13 -5
- classiq/_internals/jobs.py +10 -7
- classiq/analyzer/analyzer.py +26 -28
- classiq/analyzer/analyzer_utilities.py +5 -5
- classiq/analyzer/rb.py +4 -5
- classiq/analyzer/show_interactive_hack.py +6 -6
- classiq/applications/benchmarking/mirror_benchmarking.py +9 -6
- classiq/applications/combinatorial_optimization/__init__.py +5 -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/qsvm.py +4 -7
- classiq/applications/qsvm/qsvm_data_generation.py +2 -5
- classiq/applications_model_constructors/__init__.py +9 -1
- classiq/applications_model_constructors/chemistry_model_constructor.py +9 -16
- classiq/applications_model_constructors/combinatorial_helpers/__init__.py +0 -0
- classiq/applications_model_constructors/combinatorial_helpers/allowed_constraints.py +20 -0
- classiq/applications_model_constructors/combinatorial_helpers/arithmetic/__init__.py +0 -0
- classiq/applications_model_constructors/combinatorial_helpers/arithmetic/arithmetic_expression.py +35 -0
- classiq/applications_model_constructors/combinatorial_helpers/arithmetic/isolation.py +42 -0
- classiq/applications_model_constructors/combinatorial_helpers/combinatorial_problem_utils.py +130 -0
- classiq/applications_model_constructors/combinatorial_helpers/encoding_mapping.py +107 -0
- classiq/applications_model_constructors/combinatorial_helpers/encoding_utils.py +122 -0
- classiq/applications_model_constructors/combinatorial_helpers/memory.py +79 -0
- classiq/applications_model_constructors/combinatorial_helpers/multiple_comp_basis_sp.py +34 -0
- classiq/applications_model_constructors/combinatorial_helpers/optimization_model.py +166 -0
- classiq/applications_model_constructors/combinatorial_helpers/pauli_helpers/__init__.py +0 -0
- classiq/applications_model_constructors/combinatorial_helpers/pauli_helpers/pauli_sparsing.py +31 -0
- classiq/applications_model_constructors/combinatorial_helpers/pauli_helpers/pauli_utils.py +65 -0
- classiq/applications_model_constructors/combinatorial_helpers/py.typed +0 -0
- classiq/applications_model_constructors/combinatorial_helpers/pyomo_utils.py +243 -0
- classiq/applications_model_constructors/combinatorial_helpers/sympy_utils.py +22 -0
- classiq/applications_model_constructors/combinatorial_helpers/transformations/__init__.py +0 -0
- classiq/applications_model_constructors/combinatorial_helpers/transformations/encoding.py +194 -0
- classiq/applications_model_constructors/combinatorial_helpers/transformations/fixed_variables.py +144 -0
- classiq/applications_model_constructors/combinatorial_helpers/transformations/ising_converter.py +124 -0
- classiq/applications_model_constructors/combinatorial_helpers/transformations/penalty.py +32 -0
- classiq/applications_model_constructors/combinatorial_helpers/transformations/penalty_support.py +41 -0
- classiq/applications_model_constructors/combinatorial_helpers/transformations/sign_seperation.py +75 -0
- classiq/applications_model_constructors/combinatorial_helpers/transformations/slack_variables.py +90 -0
- classiq/applications_model_constructors/combinatorial_optimization_model_constructor.py +48 -91
- classiq/applications_model_constructors/finance_model_constructor.py +4 -17
- classiq/applications_model_constructors/grover_model_constructor.py +20 -91
- classiq/applications_model_constructors/libraries/qmci_library.py +17 -19
- classiq/builtin_functions/standard_gates.py +1 -1
- classiq/exceptions.py +43 -1
- classiq/executor.py +10 -9
- classiq/interface/_version.py +1 -1
- classiq/interface/analyzer/analysis_params.py +6 -3
- classiq/interface/analyzer/result.py +12 -4
- classiq/interface/applications/qsvm.py +13 -1
- 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 +3 -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 +19 -17
- 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 +1 -1
- 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/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/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 +1 -1
- 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 +1 -1
- classiq/interface/generator/functions/core_lib_declarations/quantum_functions/std_lib_functions.py +505 -138
- classiq/interface/generator/functions/core_lib_declarations/quantum_operators.py +25 -99
- classiq/interface/generator/functions/foreign_function_definition.py +12 -4
- 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 +3 -1
- 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/constraints.py +2 -1
- classiq/interface/generator/model/model.py +11 -19
- 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 +8 -3
- 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/inplace_binary_operation.py +2 -2
- classiq/interface/model/model.py +27 -21
- classiq/interface/model/native_function_definition.py +3 -5
- 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 +25 -139
- classiq/interface/model/quantum_function_declaration.py +8 -0
- classiq/interface/model/quantum_if_operation.py +2 -3
- classiq/interface/model/quantum_lambda_function.py +64 -0
- classiq/interface/model/quantum_type.py +57 -56
- classiq/interface/model/quantum_variable_declaration.py +1 -1
- classiq/interface/model/statement_block.py +32 -0
- classiq/interface/model/validations/handles_validator.py +14 -12
- 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 -0
- classiq/model/function_handler.py +5 -9
- classiq/model/model.py +2 -19
- classiq/qmod/__init__.py +13 -6
- classiq/qmod/builtins/classical_execution_primitives.py +27 -36
- classiq/qmod/builtins/classical_functions.py +24 -14
- classiq/qmod/builtins/functions.py +162 -145
- classiq/qmod/builtins/operations.py +24 -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 +311 -0
- classiq/qmod/qfunc.py +27 -0
- classiq/qmod/qmod_constant.py +76 -0
- classiq/qmod/qmod_parameter.py +34 -12
- classiq/qmod/qmod_struct.py +3 -3
- classiq/qmod/qmod_variable.py +102 -18
- classiq/qmod/quantum_expandable.py +16 -16
- classiq/qmod/quantum_function.py +37 -8
- classiq/qmod/symbolic.py +47 -4
- classiq/qmod/symbolic_expr.py +9 -0
- classiq/qmod/utilities.py +13 -0
- classiq/qmod/write_qmod.py +39 -0
- classiq/quantum_functions/__init__.py +2 -2
- classiq/quantum_functions/annotation_parser.py +9 -11
- classiq/quantum_functions/function_parser.py +1 -1
- classiq/quantum_functions/quantum_function.py +3 -3
- classiq/quantum_register.py +17 -9
- {classiq-0.37.0.dist-info → classiq-0.38.0.dist-info}/METADATA +2 -1
- {classiq-0.37.0.dist-info → classiq-0.38.0.dist-info}/RECORD +222 -186
- {classiq-0.37.0.dist-info → classiq-0.38.0.dist-info}/WHEEL +1 -1
- classiq/interface/generator/expressions/qmod_qnum_proxy.py +0 -22
- 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/function_handler.pyi +0 -152
@@ -1,40 +1,37 @@
|
|
1
1
|
from typing import Callable, List, Union
|
2
2
|
|
3
3
|
from classiq.interface.generator.expressions.expression import Expression
|
4
|
-
from classiq.interface.generator.functions.core_lib_declarations.quantum_operators import (
|
5
|
-
OPERAND_FIELD_NAME,
|
6
|
-
)
|
7
4
|
from classiq.interface.model.bind_operation import BindOperation
|
8
5
|
from classiq.interface.model.inplace_binary_operation import (
|
9
6
|
BinaryOperation,
|
10
7
|
InplaceBinaryOperation,
|
11
8
|
)
|
12
|
-
from classiq.interface.model.
|
13
|
-
NumericReinterpretationOperation,
|
14
|
-
)
|
9
|
+
from classiq.interface.model.quantum_function_call import ArgValue
|
15
10
|
from classiq.interface.model.quantum_function_declaration import (
|
16
11
|
QuantumOperandDeclaration,
|
17
12
|
)
|
18
13
|
from classiq.interface.model.quantum_if_operation import QuantumIfOperation
|
14
|
+
from classiq.interface.model.within_apply_operation import WithinApplyOperation
|
19
15
|
|
20
|
-
from classiq.qmod.builtins.functions import (
|
21
|
-
apply,
|
22
|
-
compute as compute_operator,
|
23
|
-
uncompute,
|
24
|
-
)
|
25
|
-
from classiq.qmod.qmod_parameter import QParam
|
26
16
|
from classiq.qmod.qmod_variable import Input, Output, QNum, QVar
|
27
17
|
from classiq.qmod.quantum_callable import QCallable
|
28
18
|
from classiq.qmod.quantum_expandable import prepare_arg
|
29
19
|
from classiq.qmod.symbolic_expr import SymbolicExpr
|
30
20
|
|
31
21
|
|
32
|
-
def bind(
|
22
|
+
def bind(
|
23
|
+
source: Union[Input[QVar], List[Input[QVar]]],
|
24
|
+
destination: Union[Output[QVar], List[Output[QVar]]],
|
25
|
+
) -> None:
|
33
26
|
assert QCallable.CURRENT_EXPANDABLE is not None
|
27
|
+
if not isinstance(source, list):
|
28
|
+
source = [source]
|
29
|
+
if not isinstance(destination, list):
|
30
|
+
destination = [destination]
|
34
31
|
QCallable.CURRENT_EXPANDABLE.append_statement_to_body(
|
35
32
|
BindOperation(
|
36
|
-
|
37
|
-
|
33
|
+
in_handles=[src_var.get_handle_binding() for src_var in source],
|
34
|
+
out_handles=[dst_var.get_handle_binding() for dst_var in destination],
|
38
35
|
)
|
39
36
|
)
|
40
37
|
|
@@ -46,22 +43,7 @@ def quantum_if(
|
|
46
43
|
QCallable.CURRENT_EXPANDABLE.append_statement_to_body(
|
47
44
|
QuantumIfOperation(
|
48
45
|
expression=Expression(expr=str(condition)),
|
49
|
-
then=
|
50
|
-
)
|
51
|
-
)
|
52
|
-
|
53
|
-
|
54
|
-
def reinterpret_num(
|
55
|
-
is_signed: Union[QParam[bool], bool],
|
56
|
-
fraction_digits: Union[QParam[int], int],
|
57
|
-
target: QNum,
|
58
|
-
) -> None:
|
59
|
-
assert QCallable.CURRENT_EXPANDABLE is not None
|
60
|
-
QCallable.CURRENT_EXPANDABLE.append_statement_to_body(
|
61
|
-
NumericReinterpretationOperation(
|
62
|
-
target=target.get_handle_binding(),
|
63
|
-
is_signed=Expression(expr=str(is_signed)),
|
64
|
-
fraction_digits=Expression(expr=str(fraction_digits)),
|
46
|
+
then=_to_operand(then),
|
65
47
|
)
|
66
48
|
)
|
67
49
|
|
@@ -98,15 +80,22 @@ def within_apply(
|
|
98
80
|
compute: Callable[[], None],
|
99
81
|
action: Callable[[], None],
|
100
82
|
) -> None:
|
101
|
-
|
102
|
-
|
103
|
-
|
83
|
+
assert QCallable.CURRENT_EXPANDABLE is not None
|
84
|
+
QCallable.CURRENT_EXPANDABLE.append_statement_to_body(
|
85
|
+
WithinApplyOperation(
|
86
|
+
compute=_to_operand(compute),
|
87
|
+
action=_to_operand(action),
|
88
|
+
)
|
89
|
+
)
|
90
|
+
|
91
|
+
|
92
|
+
def _to_operand(callable_: Union[QCallable, Callable[[], None]]) -> ArgValue:
|
93
|
+
return prepare_arg(QuantumOperandDeclaration(name=""), callable_)
|
104
94
|
|
105
95
|
|
106
96
|
__all__ = [
|
107
97
|
"bind",
|
108
98
|
"quantum_if",
|
109
|
-
"reinterpret_num",
|
110
99
|
"inplace_add",
|
111
100
|
"inplace_xor",
|
112
101
|
"within_apply",
|
classiq/qmod/builtins/structs.py
CHANGED
@@ -2,16 +2,16 @@
|
|
2
2
|
|
3
3
|
from typing import List
|
4
4
|
|
5
|
-
from classiq.qmod.qmod_struct import
|
5
|
+
from classiq.qmod.qmod_struct import struct
|
6
6
|
|
7
7
|
|
8
|
-
@
|
8
|
+
@struct
|
9
9
|
class PauliTerm:
|
10
10
|
pauli: List[int]
|
11
11
|
coefficient: float
|
12
12
|
|
13
13
|
|
14
|
-
@
|
14
|
+
@struct
|
15
15
|
class MoleculeProblem:
|
16
16
|
mapping: int
|
17
17
|
z2_symmetries: bool
|
@@ -20,27 +20,27 @@ class MoleculeProblem:
|
|
20
20
|
remove_orbitals: List[int]
|
21
21
|
|
22
22
|
|
23
|
-
@
|
23
|
+
@struct
|
24
24
|
class Molecule:
|
25
25
|
atoms: List["ChemistryAtom"]
|
26
26
|
spin: int
|
27
27
|
charge: int
|
28
28
|
|
29
29
|
|
30
|
-
@
|
30
|
+
@struct
|
31
31
|
class ChemistryAtom:
|
32
32
|
element: int
|
33
33
|
position: "Position"
|
34
34
|
|
35
35
|
|
36
|
-
@
|
36
|
+
@struct
|
37
37
|
class Position:
|
38
38
|
x: float
|
39
39
|
y: float
|
40
40
|
z: float
|
41
41
|
|
42
42
|
|
43
|
-
@
|
43
|
+
@struct
|
44
44
|
class FockHamiltonianProblem:
|
45
45
|
mapping: int
|
46
46
|
z2_symmetries: bool
|
@@ -48,19 +48,19 @@ class FockHamiltonianProblem:
|
|
48
48
|
num_particles: List[int]
|
49
49
|
|
50
50
|
|
51
|
-
@
|
51
|
+
@struct
|
52
52
|
class LadderTerm:
|
53
53
|
coefficient: float
|
54
54
|
ops: List["LadderOp"]
|
55
55
|
|
56
56
|
|
57
|
-
@
|
57
|
+
@struct
|
58
58
|
class LadderOp:
|
59
59
|
op: int
|
60
60
|
index: int
|
61
61
|
|
62
62
|
|
63
|
-
@
|
63
|
+
@struct
|
64
64
|
class CombinatorialOptimizationSolution:
|
65
65
|
probability: float
|
66
66
|
cost: float
|
@@ -68,7 +68,7 @@ class CombinatorialOptimizationSolution:
|
|
68
68
|
count: int
|
69
69
|
|
70
70
|
|
71
|
-
@
|
71
|
+
@struct
|
72
72
|
class GaussianModel:
|
73
73
|
num_qubits: int
|
74
74
|
normal_max_value: float
|
@@ -78,14 +78,14 @@ class GaussianModel:
|
|
78
78
|
min_loss: int
|
79
79
|
|
80
80
|
|
81
|
-
@
|
81
|
+
@struct
|
82
82
|
class LogNormalModel:
|
83
83
|
num_qubits: int
|
84
84
|
mu: float
|
85
85
|
sigma: float
|
86
86
|
|
87
87
|
|
88
|
-
@
|
88
|
+
@struct
|
89
89
|
class FinanceFunction:
|
90
90
|
f: int
|
91
91
|
threshold: float
|
@@ -95,13 +95,13 @@ class FinanceFunction:
|
|
95
95
|
tail_probability: float
|
96
96
|
|
97
97
|
|
98
|
-
@
|
98
|
+
@struct
|
99
99
|
class QsvmResult:
|
100
100
|
test_score: float
|
101
101
|
predicted_labels: List[float]
|
102
102
|
|
103
103
|
|
104
|
-
@
|
104
|
+
@struct
|
105
105
|
class QSVMFeatureMapPauli:
|
106
106
|
feature_dimension: int
|
107
107
|
reps: int
|
classiq/qmod/cfunc.py
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
from typing import Any, Callable, Dict, Optional, Union, overload
|
2
|
+
|
3
|
+
from classiq.qmod.classical_function import CFunc
|
4
|
+
|
5
|
+
|
6
|
+
def get_caller_locals() -> Dict[str, Any]:
|
7
|
+
"""Print the local variables in the caller's frame."""
|
8
|
+
import inspect
|
9
|
+
|
10
|
+
frame = inspect.currentframe()
|
11
|
+
try:
|
12
|
+
assert frame is not None
|
13
|
+
cfunc_frame = frame.f_back
|
14
|
+
assert cfunc_frame is not None
|
15
|
+
caller_frame = cfunc_frame.f_back
|
16
|
+
assert caller_frame is not None
|
17
|
+
|
18
|
+
return caller_frame.f_locals
|
19
|
+
finally:
|
20
|
+
# See here for information about the `del`
|
21
|
+
# https://docs.python.org/3/library/inspect.html#the-interpreter-stack
|
22
|
+
del frame
|
23
|
+
|
24
|
+
|
25
|
+
@overload
|
26
|
+
def cfunc(func: Callable) -> CFunc: ...
|
27
|
+
|
28
|
+
|
29
|
+
@overload
|
30
|
+
def cfunc(func: None = None) -> Callable[[Callable], CFunc]: ...
|
31
|
+
|
32
|
+
|
33
|
+
def cfunc(func: Optional[Callable] = None) -> Union[Callable[[Callable], CFunc], CFunc]:
|
34
|
+
caller_locals = get_caller_locals()
|
35
|
+
|
36
|
+
def wrapper(func: Callable) -> CFunc:
|
37
|
+
return CFunc(func, caller_locals)
|
38
|
+
|
39
|
+
if func is not None:
|
40
|
+
return wrapper(func)
|
41
|
+
|
42
|
+
return wrapper
|
@@ -2,10 +2,9 @@ import ast
|
|
2
2
|
import inspect
|
3
3
|
import sys
|
4
4
|
from textwrap import dedent
|
5
|
-
from typing import Callable
|
5
|
+
from typing import Any, Callable, Dict
|
6
6
|
|
7
7
|
from classiq.exceptions import ClassiqValueError
|
8
|
-
from classiq.qmod.builtins.classical_execution_primitives import sample, save
|
9
8
|
|
10
9
|
|
11
10
|
def _unparse_function_body(code: str, func: ast.FunctionDef) -> str:
|
@@ -18,23 +17,16 @@ def _unparse_function_body(code: str, func: ast.FunctionDef) -> str:
|
|
18
17
|
|
19
18
|
|
20
19
|
class CFunc:
|
21
|
-
|
22
|
-
def default_cmain() -> "CFunc":
|
23
|
-
@CFunc
|
24
|
-
def cmain() -> None:
|
25
|
-
result = sample()
|
26
|
-
save({"result": result})
|
27
|
-
|
28
|
-
return cmain
|
29
|
-
|
30
|
-
def __init__(self, py_callable: Callable[[], None]):
|
20
|
+
def __init__(self, py_callable: Callable[[], None], caller_locals: Dict[str, Any]):
|
31
21
|
code = dedent(inspect.getsource(py_callable))
|
32
22
|
func = ast.parse(code).body[0]
|
33
23
|
if not isinstance(func, ast.FunctionDef):
|
34
|
-
raise ClassiqValueError(
|
24
|
+
raise ClassiqValueError("Use @cfunc to decorate a function")
|
35
25
|
if len(func.args.args) > 0:
|
36
|
-
raise ClassiqValueError(
|
26
|
+
raise ClassiqValueError("A @cfunc must receive no arguments")
|
37
27
|
if sys.version_info >= (3, 9):
|
38
28
|
self.code = "\n".join([ast.unparse(statement) for statement in func.body])
|
39
29
|
else:
|
40
30
|
self.code = _unparse_function_body(code, func)
|
31
|
+
|
32
|
+
self._caller_constants = caller_locals
|
@@ -1,6 +1,5 @@
|
|
1
1
|
import dataclasses
|
2
2
|
import inspect
|
3
|
-
import sys
|
4
3
|
from typing import Any, Callable, Dict, List, Optional, Type, get_args, get_origin
|
5
4
|
|
6
5
|
from typing_extensions import _AnnotatedAlias
|
@@ -26,25 +25,17 @@ from classiq.interface.model.quantum_function_declaration import (
|
|
26
25
|
)
|
27
26
|
|
28
27
|
from classiq import StructDeclaration
|
28
|
+
from classiq.exceptions import ClassiqValueError
|
29
29
|
from classiq.qmod.model_state_container import ModelStateContainer
|
30
30
|
from classiq.qmod.qmod_parameter import Array, QParam
|
31
31
|
from classiq.qmod.qmod_variable import QVar, get_type_hint_expr
|
32
32
|
from classiq.qmod.quantum_callable import QCallable, QCallableList
|
33
|
-
from classiq.qmod.utilities import unmangle_keyword
|
33
|
+
from classiq.qmod.utilities import unmangle_keyword, version_portable_get_args
|
34
34
|
|
35
35
|
OPERAND_ARG_NAME = "arg{i}"
|
36
36
|
|
37
37
|
|
38
|
-
def
|
39
|
-
if get_origin(py_type) is None:
|
40
|
-
return tuple()
|
41
|
-
if sys.version_info[0:2] < (3, 10):
|
42
|
-
return get_args(py_type) # The result of __class_getitem__
|
43
|
-
else:
|
44
|
-
return get_args(py_type)[0]
|
45
|
-
|
46
|
-
|
47
|
-
def _python_type_to_qmod(
|
38
|
+
def python_type_to_qmod(
|
48
39
|
py_type: type, *, qmodule: ModelStateContainer
|
49
40
|
) -> Optional[ConcreteClassicalType]:
|
50
41
|
if py_type == int:
|
@@ -55,16 +46,16 @@ def _python_type_to_qmod(
|
|
55
46
|
return Bool()
|
56
47
|
elif get_origin(py_type) == list:
|
57
48
|
return ClassicalList(
|
58
|
-
element_type=
|
49
|
+
element_type=python_type_to_qmod(get_args(py_type)[0], qmodule=qmodule)
|
59
50
|
)
|
60
51
|
elif get_origin(py_type) == Array:
|
61
|
-
array_args =
|
52
|
+
array_args = version_portable_get_args(py_type)
|
62
53
|
if len(array_args) != 2:
|
63
|
-
raise
|
54
|
+
raise ClassiqValueError(
|
64
55
|
"Array accepts two generic parameters in the form 'Array[<element-type>, <size>]'"
|
65
56
|
)
|
66
57
|
return ClassicalArray(
|
67
|
-
element_type=
|
58
|
+
element_type=python_type_to_qmod(array_args[0], qmodule=qmodule),
|
68
59
|
size=get_type_hint_expr(array_args[1]),
|
69
60
|
)
|
70
61
|
elif inspect.isclass(py_type) and issubclass(py_type, QStructBase):
|
@@ -78,14 +69,14 @@ def _add_qmod_struct(
|
|
78
69
|
) -> None:
|
79
70
|
if (
|
80
71
|
py_type.__name__ in StructDeclaration.BUILTIN_STRUCT_DECLARATIONS
|
81
|
-
or py_type.__name__ in qmodule.type_decls
|
72
|
+
or py_type.__name__ in qmodule.type_decls
|
82
73
|
):
|
83
74
|
return
|
84
75
|
|
85
76
|
qmodule.type_decls[py_type.__name__] = StructDeclaration(
|
86
77
|
name=py_type.__name__,
|
87
78
|
variables={
|
88
|
-
f.name:
|
79
|
+
f.name: python_type_to_qmod(f.type, qmodule=qmodule)
|
89
80
|
for f in dataclasses.fields(py_type)
|
90
81
|
},
|
91
82
|
)
|
@@ -95,10 +86,10 @@ def _extract_param_decl(
|
|
95
86
|
name: str, py_type: Any, *, qmodule: ModelStateContainer
|
96
87
|
) -> ClassicalParameterDeclaration:
|
97
88
|
if len(get_args(py_type)) != 1:
|
98
|
-
raise
|
89
|
+
raise ClassiqValueError("QParam takes exactly one generic argument")
|
99
90
|
py_type = get_args(py_type)[0]
|
100
91
|
return ClassicalParameterDeclaration(
|
101
|
-
name=name, classical_type=
|
92
|
+
name=name, classical_type=python_type_to_qmod(py_type, qmodule=qmodule)
|
102
93
|
)
|
103
94
|
|
104
95
|
|
@@ -118,7 +109,7 @@ def _extract_port_decl(name: str, py_type: Any) -> PortDeclaration:
|
|
118
109
|
def _extract_operand_decl(
|
119
110
|
name: str, py_type: Any, qmodule: ModelStateContainer
|
120
111
|
) -> QuantumOperandDeclaration:
|
121
|
-
qc_args =
|
112
|
+
qc_args = version_portable_get_args(py_type)
|
122
113
|
arg_dict = {
|
123
114
|
OPERAND_ARG_NAME.format(i=i): arg_type for i, arg_type in enumerate(qc_args)
|
124
115
|
}
|
@@ -0,0 +1,23 @@
|
|
1
|
+
from typing import TYPE_CHECKING, List, Tuple
|
2
|
+
|
3
|
+
from classiq.interface.generator.arith.arithmetic import compute_arithmetic_result_type
|
4
|
+
from classiq.interface.generator.arith.number_utils import MAXIMAL_MACHINE_PRECISION
|
5
|
+
from classiq.interface.model.quantum_type import QuantumNumeric
|
6
|
+
|
7
|
+
from classiq.qmod.qmod_variable import QNum
|
8
|
+
from classiq.qmod.symbolic_type import SymbolicTypes
|
9
|
+
|
10
|
+
|
11
|
+
def get_expression_numeric_attributes(
|
12
|
+
vars: List[QNum],
|
13
|
+
expr: SymbolicTypes,
|
14
|
+
machine_precision: int = MAXIMAL_MACHINE_PRECISION,
|
15
|
+
) -> Tuple[int, bool, int]:
|
16
|
+
res_type = compute_arithmetic_result_type(
|
17
|
+
expr_str=str(expr),
|
18
|
+
var_types={var._name: var.get_qmod_type() for var in vars},
|
19
|
+
machine_precision=machine_precision,
|
20
|
+
)
|
21
|
+
if TYPE_CHECKING:
|
22
|
+
assert isinstance(res_type, QuantumNumeric)
|
23
|
+
return res_type.size_in_bits, res_type.sign_value, res_type.fraction_digits_value
|
@@ -1,5 +1,6 @@
|
|
1
1
|
from typing import Dict
|
2
2
|
|
3
|
+
from classiq.interface.generator.constant import Constant
|
3
4
|
from classiq.interface.model.native_function_definition import NativeFunctionDefinition
|
4
5
|
|
5
6
|
from classiq import StructDeclaration
|
@@ -8,6 +9,7 @@ from classiq import StructDeclaration
|
|
8
9
|
class ModelStateContainer:
|
9
10
|
type_decls: Dict[str, StructDeclaration]
|
10
11
|
native_defs: Dict[str, NativeFunctionDefinition]
|
12
|
+
constants: Dict[str, Constant]
|
11
13
|
|
12
14
|
|
13
15
|
QMODULE = ModelStateContainer()
|
File without changes
|
@@ -0,0 +1,189 @@
|
|
1
|
+
import ast
|
2
|
+
import re
|
3
|
+
from dataclasses import dataclass
|
4
|
+
from typing import Callable, Dict, List, Mapping, Type
|
5
|
+
|
6
|
+
import numpy as np
|
7
|
+
|
8
|
+
from classiq.qmod.utilities import DEFAULT_DECIMAL_PRECISION
|
9
|
+
|
10
|
+
IDENTIFIER = re.compile(r"[a-zA-Z_]\w*")
|
11
|
+
BINARY_OPS: Mapping[Type[ast.operator], str] = {
|
12
|
+
ast.Add: "+",
|
13
|
+
ast.Sub: "-",
|
14
|
+
ast.Mult: "*",
|
15
|
+
ast.Div: "/",
|
16
|
+
ast.Mod: "%",
|
17
|
+
ast.Pow: "**",
|
18
|
+
ast.BitAnd: "&",
|
19
|
+
ast.BitOr: "|",
|
20
|
+
ast.BitXor: "^",
|
21
|
+
ast.LShift: "<<",
|
22
|
+
ast.RShift: ">>",
|
23
|
+
}
|
24
|
+
BOOL_OPS: Mapping[Type[ast.boolop], str] = {ast.And: "and", ast.Or: "or"}
|
25
|
+
UNARY_OPS: Mapping[Type[ast.unaryop], str] = {
|
26
|
+
ast.UAdd: "+",
|
27
|
+
ast.USub: "-",
|
28
|
+
ast.Invert: "~",
|
29
|
+
ast.Not: "not",
|
30
|
+
}
|
31
|
+
COMPARE_OPS: Mapping[Type[ast.cmpop], str] = {
|
32
|
+
ast.Eq: "==",
|
33
|
+
ast.NotEq: "!=",
|
34
|
+
ast.Lt: "<",
|
35
|
+
ast.LtE: "<=",
|
36
|
+
ast.Gt: ">",
|
37
|
+
ast.GtE: ">=",
|
38
|
+
}
|
39
|
+
LIST_FORMAT_CHAR_LIMIT = 20
|
40
|
+
|
41
|
+
|
42
|
+
@dataclass
|
43
|
+
class ASTToQMODCode:
|
44
|
+
level: int
|
45
|
+
decimal_precision: int
|
46
|
+
indent_seq: str = " "
|
47
|
+
|
48
|
+
@property
|
49
|
+
def indent(self) -> str:
|
50
|
+
return self.level * self.indent_seq
|
51
|
+
|
52
|
+
def visit(self, node: ast.AST) -> str:
|
53
|
+
return self.ast_to_code(node)
|
54
|
+
|
55
|
+
def ast_to_code(self, node: ast.AST) -> str:
|
56
|
+
if isinstance(node, ast.Module):
|
57
|
+
return self.indent.join(self.ast_to_code(child) for child in node.body)
|
58
|
+
elif isinstance(node, ast.Attribute):
|
59
|
+
# Enum attribute access
|
60
|
+
if not isinstance(node.value, ast.Name) or not isinstance(node.attr, str):
|
61
|
+
raise AssertionError("Error parsing enum attribute access")
|
62
|
+
if not (IDENTIFIER.match(node.value.id) and IDENTIFIER.match(node.attr)):
|
63
|
+
raise AssertionError("Error parsing enum attribute access")
|
64
|
+
return f"{node.value.id!s}::{node.attr!s}"
|
65
|
+
elif isinstance(node, ast.Name):
|
66
|
+
return node.id
|
67
|
+
elif isinstance(node, ast.Num):
|
68
|
+
return str(np.round(node.n, self.decimal_precision))
|
69
|
+
elif isinstance(node, ast.Str):
|
70
|
+
return repr(node.s)
|
71
|
+
elif isinstance(node, ast.Constant):
|
72
|
+
return repr(node.value)
|
73
|
+
elif isinstance(node, ast.BinOp):
|
74
|
+
return "({} {} {})".format(
|
75
|
+
self.ast_to_code(node.left),
|
76
|
+
BINARY_OPS[type(node.op)],
|
77
|
+
self.ast_to_code(node.right),
|
78
|
+
)
|
79
|
+
elif isinstance(node, ast.UnaryOp):
|
80
|
+
unary_op = UNARY_OPS[type(node.op)]
|
81
|
+
space = " " if unary_op == "not" else ""
|
82
|
+
return f"({unary_op}{space}{self.ast_to_code(node.operand)})"
|
83
|
+
elif isinstance(node, ast.BoolOp):
|
84
|
+
return "({})".format(
|
85
|
+
(" " + BOOL_OPS[type(node.op)] + " ").join(
|
86
|
+
self.ast_to_code(value) for value in node.values
|
87
|
+
)
|
88
|
+
)
|
89
|
+
elif isinstance(node, ast.Compare):
|
90
|
+
if len(node.ops) != 1 or len(node.comparators) != 1:
|
91
|
+
raise AssertionError("Error parsing comparison expression.")
|
92
|
+
return "({} {} {})".format(
|
93
|
+
self.ast_to_code(node.left),
|
94
|
+
COMPARE_OPS[type(node.ops[0])],
|
95
|
+
self.ast_to_code(node.comparators[0]),
|
96
|
+
)
|
97
|
+
elif isinstance(node, ast.List):
|
98
|
+
elts = node.elts
|
99
|
+
elements = self.indent_items(
|
100
|
+
lambda: [self.ast_to_code(element) for element in elts]
|
101
|
+
)
|
102
|
+
return f"[{elements}]"
|
103
|
+
elif isinstance(node, ast.Subscript):
|
104
|
+
return f"{self.ast_to_code(node.value)}[{_remove_redundant_parentheses(self.ast_to_code(node.slice))}]"
|
105
|
+
elif isinstance(node, ast.Slice):
|
106
|
+
# A QMOD expression does not support slice step
|
107
|
+
if node.lower is None or node.upper is None or node.step is not None:
|
108
|
+
raise AssertionError("Error parsing slice expression.")
|
109
|
+
return f"{self.ast_to_code(node.lower)}:{self.ast_to_code(node.upper)}"
|
110
|
+
elif isinstance(node, ast.Call):
|
111
|
+
func = self.ast_to_code(node.func)
|
112
|
+
if func == "get_field":
|
113
|
+
if len(node.args) != 2:
|
114
|
+
raise AssertionError("Error parsing struct field access.")
|
115
|
+
field = str(self.ast_to_code(node.args[1])).replace("'", "")
|
116
|
+
if not IDENTIFIER.match(field):
|
117
|
+
raise AssertionError("Error parsing struct field access.")
|
118
|
+
return f"{self.ast_to_code(node.args[0])}.{field}"
|
119
|
+
elif func == "struct_literal":
|
120
|
+
if len(node.args) != 1 or not isinstance(node.args[0], ast.Name):
|
121
|
+
raise AssertionError("Error parsing struct literal.")
|
122
|
+
keywords = node.keywords
|
123
|
+
initializer_list = self.indent_items(
|
124
|
+
lambda: [
|
125
|
+
f"{keyword.arg} = {self._cleaned_ast_to_code(keyword.value)}"
|
126
|
+
for keyword in keywords
|
127
|
+
if keyword.arg is not None
|
128
|
+
]
|
129
|
+
)
|
130
|
+
return f"{self.ast_to_code(node.args[0])} {{{initializer_list}}}"
|
131
|
+
else:
|
132
|
+
return "{}({})".format(
|
133
|
+
func, ", ".join(self._cleaned_ast_to_code(arg) for arg in node.args)
|
134
|
+
)
|
135
|
+
elif isinstance(node, ast.Expr):
|
136
|
+
return self._cleaned_ast_to_code(node.value)
|
137
|
+
else:
|
138
|
+
raise AssertionError("Error parsing expression: unsupported AST node.")
|
139
|
+
|
140
|
+
def indent_items(self, items: Callable[[], List[str]]) -> str:
|
141
|
+
should_indent = (
|
142
|
+
len("".join([i.strip() for i in items()])) >= LIST_FORMAT_CHAR_LIMIT
|
143
|
+
)
|
144
|
+
if should_indent:
|
145
|
+
self.level += 1
|
146
|
+
left_ws = "\n" + self.indent
|
147
|
+
inner_ws = ",\n" + self.indent
|
148
|
+
else:
|
149
|
+
left_ws = ""
|
150
|
+
inner_ws = ", "
|
151
|
+
items_ = items()
|
152
|
+
if should_indent:
|
153
|
+
self.level -= 1
|
154
|
+
right_ws = "\n" + self.indent
|
155
|
+
else:
|
156
|
+
right_ws = ""
|
157
|
+
return f"{left_ws}{inner_ws.join(items_)}{right_ws}"
|
158
|
+
|
159
|
+
def _cleaned_ast_to_code(self, node: ast.AST) -> str:
|
160
|
+
return _remove_redundant_parentheses(self.ast_to_code(node))
|
161
|
+
|
162
|
+
|
163
|
+
def _remove_redundant_parentheses(expr: str) -> str:
|
164
|
+
if not (expr.startswith("(") and expr.endswith(")")):
|
165
|
+
return expr
|
166
|
+
parentheses_map: Dict[int, int] = dict()
|
167
|
+
stack: List[int] = []
|
168
|
+
for index, char in enumerate(expr):
|
169
|
+
if char == "(":
|
170
|
+
stack.append(index)
|
171
|
+
elif char == ")":
|
172
|
+
parentheses_map[stack.pop()] = index
|
173
|
+
index = 0
|
174
|
+
original_length = len(expr)
|
175
|
+
while (
|
176
|
+
index in parentheses_map
|
177
|
+
and parentheses_map[index] == original_length - index - 1
|
178
|
+
):
|
179
|
+
expr = expr[1:-1]
|
180
|
+
index += 1
|
181
|
+
return expr
|
182
|
+
|
183
|
+
|
184
|
+
def transform_expression(
|
185
|
+
expr: str, level: int = 0, decimal_precision: int = DEFAULT_DECIMAL_PRECISION
|
186
|
+
) -> str:
|
187
|
+
return ASTToQMODCode(level=level, decimal_precision=decimal_precision).visit(
|
188
|
+
ast.parse(expr)
|
189
|
+
)
|