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
classiq/applications_model_constructors/combinatorial_helpers/transformations/ising_converter.py
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
from typing import List
|
2
|
+
|
3
|
+
import pyomo
|
4
|
+
import sympy as sp
|
5
|
+
from pyomo.core.base import _GeneralVarData
|
6
|
+
|
7
|
+
from classiq.interface.chemistry.operator import PauliOperator
|
8
|
+
from classiq.interface.helpers import custom_pydantic_types
|
9
|
+
|
10
|
+
from classiq.applications_model_constructors.combinatorial_helpers import memory
|
11
|
+
from classiq.applications_model_constructors.combinatorial_helpers.memory import (
|
12
|
+
InternalQuantumReg,
|
13
|
+
)
|
14
|
+
from classiq.applications_model_constructors.combinatorial_helpers.sympy_utils import (
|
15
|
+
sympyify_expression,
|
16
|
+
sympyify_vars,
|
17
|
+
)
|
18
|
+
from classiq.exceptions import ClassiqCombOptNotSupportedProblemError
|
19
|
+
|
20
|
+
PYOMO_PARSING_ERROR_MESAGE = "Parsing of this pyomo model is not supported."
|
21
|
+
|
22
|
+
|
23
|
+
def convert_pyomo_to_hamiltonian(
|
24
|
+
pyomo_expr: pyomo.core.Expression,
|
25
|
+
ordered_pyomo_vars: List[_GeneralVarData],
|
26
|
+
qregs: List[InternalQuantumReg],
|
27
|
+
) -> PauliOperator:
|
28
|
+
symbols_map = sympyify_vars(ordered_pyomo_vars)
|
29
|
+
sympy_expr = sympyify_expression(pyomo_expr, symbols_map)
|
30
|
+
if not sympy_expr.is_polynomial():
|
31
|
+
raise ClassiqCombOptNotSupportedProblemError(PYOMO_PARSING_ERROR_MESAGE)
|
32
|
+
|
33
|
+
ordered_sympy_vars = [
|
34
|
+
symbols_map.pyomo2sympy[pyomo_var] for pyomo_var in ordered_pyomo_vars
|
35
|
+
]
|
36
|
+
ising_expr = _to_ising_symbolic_objective_function(sympy_expr)
|
37
|
+
ising_expr = _refine_ising_expr(ising_expr)
|
38
|
+
|
39
|
+
operator = _convert_ising_sympy_to_operator(ising_expr, ordered_sympy_vars)
|
40
|
+
operator = _add_auxiliary_qubits_to_operator(operator, qregs)
|
41
|
+
|
42
|
+
return PauliOperator(pauli_list=operator)
|
43
|
+
|
44
|
+
|
45
|
+
def _convert_ising_sympy_to_operator(
|
46
|
+
ising_expr: sp.Expr, ordered_sympy_vars: List[sp.Symbol]
|
47
|
+
) -> custom_pydantic_types.PydanticPauliList:
|
48
|
+
pauli_op_list: custom_pydantic_types.PydanticPauliList = []
|
49
|
+
for expr_term in ising_expr.args:
|
50
|
+
expr_vars = _get_vars(expr_term)
|
51
|
+
z_vec = _find_sub_list_items(ordered_sympy_vars, expr_vars)
|
52
|
+
pauli_string_list = ["I"] * len(z_vec)
|
53
|
+
for index, is_z_op in enumerate(z_vec):
|
54
|
+
if is_z_op:
|
55
|
+
pauli_string_list[len(z_vec) - index - 1] = (
|
56
|
+
"Z" # reminder: Pauli reverses the order!
|
57
|
+
)
|
58
|
+
pauli_string = "".join(pauli_string_list)
|
59
|
+
coeff = _get_coeff_from_expr(expr_term)
|
60
|
+
pauli_op_list.append((pauli_string, complex(coeff)))
|
61
|
+
return pauli_op_list
|
62
|
+
|
63
|
+
|
64
|
+
def _refine_ising_expr(ising_expr: sp.Expr) -> sp.Expr:
|
65
|
+
# The variables here are assumed to be either 1 or -1 (ising variables).
|
66
|
+
# Therefore x^a can replaced with 1 if a is even, and with x is a is odd.
|
67
|
+
# Change the expression recursively.
|
68
|
+
def update_expr(expr: sp.Expr) -> sp.Expr:
|
69
|
+
if isinstance(expr, sp.Pow):
|
70
|
+
if expr.args[1] % 2: # odd power: x**a -> x
|
71
|
+
expr = expr.args[0]
|
72
|
+
else: # even power: x**a -> 1
|
73
|
+
expr = sp.Float(1)
|
74
|
+
if hasattr(expr, "args") and expr.args:
|
75
|
+
new_args = [update_expr(arg) for arg in expr.args]
|
76
|
+
expr_class = type(expr)
|
77
|
+
return expr_class(*new_args)
|
78
|
+
else:
|
79
|
+
return expr
|
80
|
+
|
81
|
+
return update_expr(ising_expr)
|
82
|
+
|
83
|
+
|
84
|
+
def _to_ising_symbolic_objective_function(objective: sp.Expr) -> sp.Expr:
|
85
|
+
# cost-function to Hamiltonian conversion explanation:
|
86
|
+
# https://qiskit.org/textbook/ch-applications/qaoa.html#1.1-Diagonal-Hamiltonians
|
87
|
+
subs_vars_dict = {var: (1 - var) / 2 for var in objective.free_symbols}
|
88
|
+
objective_ising = objective.subs(subs_vars_dict)
|
89
|
+
return sp.expand(objective_ising)
|
90
|
+
|
91
|
+
|
92
|
+
def _get_vars(expr_term: sp.AtomicExpr) -> List[sp.Symbol]:
|
93
|
+
if isinstance(expr_term, sp.Symbol):
|
94
|
+
return [expr_term]
|
95
|
+
else:
|
96
|
+
return expr_term.args
|
97
|
+
|
98
|
+
|
99
|
+
def _find_sub_list_items(
|
100
|
+
long_list: List[sp.Symbol], sub_list: List[sp.Symbol]
|
101
|
+
) -> List[bool]:
|
102
|
+
return [x in sub_list for x in long_list]
|
103
|
+
|
104
|
+
|
105
|
+
def _get_coeff_from_expr(expr: sp.Expr) -> float:
|
106
|
+
if isinstance(expr, sp.Number):
|
107
|
+
return float(expr)
|
108
|
+
if isinstance(expr, sp.Symbol):
|
109
|
+
return 1
|
110
|
+
if all(isinstance(arg, sp.Symbol) for arg in expr.args):
|
111
|
+
return 1
|
112
|
+
return float(expr.args[0])
|
113
|
+
|
114
|
+
|
115
|
+
def _add_auxiliary_qubits_to_operator(
|
116
|
+
operator: custom_pydantic_types.PydanticPauliList, qregs: List[InternalQuantumReg]
|
117
|
+
) -> custom_pydantic_types.PydanticPauliList:
|
118
|
+
# TODO: handle the case when the auxiliary are in the middle of the circuit
|
119
|
+
for qreg in qregs:
|
120
|
+
if qreg.name == memory.AUXILIARY_NAME:
|
121
|
+
operator = [
|
122
|
+
("I" * qreg.size + monomial[0], monomial[1]) for monomial in operator
|
123
|
+
]
|
124
|
+
return operator
|
@@ -0,0 +1,32 @@
|
|
1
|
+
import itertools
|
2
|
+
from typing import List
|
3
|
+
|
4
|
+
from pyomo.core.base.constraint import _GeneralConstraintData
|
5
|
+
from pyomo.core.expr.relational_expr import EqualityExpression
|
6
|
+
from pyomo.environ import Expression
|
7
|
+
|
8
|
+
|
9
|
+
def get_penalty_expression(
|
10
|
+
flat_constraints: List[_GeneralConstraintData],
|
11
|
+
) -> Expression:
|
12
|
+
return sum(
|
13
|
+
_convert_constraint_to_penalty_term(constraint)
|
14
|
+
for constraint in flat_constraints
|
15
|
+
)
|
16
|
+
|
17
|
+
|
18
|
+
def _convert_constraint_to_penalty_term(
|
19
|
+
constraint: _GeneralConstraintData,
|
20
|
+
) -> Expression:
|
21
|
+
if isinstance(constraint.expr, EqualityExpression):
|
22
|
+
return (constraint.expr.args[0] - constraint.expr.args[1]) ** 2
|
23
|
+
|
24
|
+
# we can assume that isinstance(constraint.expr, InequalityExpression) and constraint.expr.args[1] == 1
|
25
|
+
# due to _is_constraint_penalty_supported method
|
26
|
+
else:
|
27
|
+
index = 0
|
28
|
+
if isinstance(constraint.expr.args[0], int):
|
29
|
+
index = 1
|
30
|
+
constraint_variables = constraint.expr.args[index].args
|
31
|
+
var_pairs = list(itertools.combinations(constraint_variables, 2))
|
32
|
+
return sum(var1 * var2 for var1, var2 in var_pairs)
|
classiq/applications_model_constructors/combinatorial_helpers/transformations/penalty_support.py
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
from pyomo.core import ConcreteModel
|
2
|
+
from pyomo.core.base.constraint import _GeneralConstraintData
|
3
|
+
from pyomo.core.expr.relational_expr import EqualityExpression
|
4
|
+
from pyomo.repn.standard_repn import _GeneralVarData
|
5
|
+
|
6
|
+
from classiq.applications_model_constructors.combinatorial_helpers import (
|
7
|
+
allowed_constraints,
|
8
|
+
encoding_utils,
|
9
|
+
)
|
10
|
+
from classiq.applications_model_constructors.combinatorial_helpers.pyomo_utils import (
|
11
|
+
extract,
|
12
|
+
)
|
13
|
+
from classiq.applications_model_constructors.combinatorial_helpers.sympy_utils import (
|
14
|
+
sympyify_expression,
|
15
|
+
)
|
16
|
+
|
17
|
+
|
18
|
+
def is_model_penalty_supported(model: ConcreteModel) -> bool:
|
19
|
+
variables = extract(model, _GeneralVarData)
|
20
|
+
is_vars_supported = all(is_var_penalty_supported(var) for var in variables)
|
21
|
+
|
22
|
+
constraints = extract(model, _GeneralConstraintData)
|
23
|
+
is_constraints_supported = all(
|
24
|
+
is_constraint_penalty_supported(constraint) for constraint in constraints
|
25
|
+
)
|
26
|
+
return is_vars_supported and is_constraints_supported
|
27
|
+
|
28
|
+
|
29
|
+
def is_var_penalty_supported(var: _GeneralVarData) -> bool:
|
30
|
+
return encoding_utils.is_var_binary(var) or encoding_utils.is_var_span_power_of_2(
|
31
|
+
var
|
32
|
+
)
|
33
|
+
|
34
|
+
|
35
|
+
def is_constraint_penalty_supported(constraint: _GeneralConstraintData) -> bool:
|
36
|
+
if isinstance(constraint.expr, EqualityExpression):
|
37
|
+
return True
|
38
|
+
|
39
|
+
sympy_expr = sympyify_expression(constraint.expr)
|
40
|
+
|
41
|
+
return allowed_constraints.is_constraint_sum_less_than_one(sympy_expr)
|
classiq/applications_model_constructors/combinatorial_helpers/transformations/sign_seperation.py
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
from itertools import filterfalse
|
2
|
+
from typing import List, Tuple
|
3
|
+
|
4
|
+
from sympy import (
|
5
|
+
Add,
|
6
|
+
Expr,
|
7
|
+
GreaterThan,
|
8
|
+
LessThan,
|
9
|
+
Mul,
|
10
|
+
Number,
|
11
|
+
Symbol,
|
12
|
+
expand,
|
13
|
+
simplify,
|
14
|
+
)
|
15
|
+
|
16
|
+
from classiq.exceptions import ClassiqCombOptError
|
17
|
+
|
18
|
+
|
19
|
+
def sign_separation(expr: Expr) -> LessThan:
|
20
|
+
expr = simplify(expr)
|
21
|
+
expr = expand(expr)
|
22
|
+
|
23
|
+
if not isinstance(expr, (LessThan, GreaterThan)):
|
24
|
+
raise ClassiqCombOptError("sign separation didn't worked out")
|
25
|
+
|
26
|
+
if isinstance(expr, GreaterThan):
|
27
|
+
expr = LessThan(expr.args[1], expr.args[0])
|
28
|
+
|
29
|
+
expr_body = expr.args[0]
|
30
|
+
expr_bound = expr.args[1]
|
31
|
+
|
32
|
+
positive_body_args, negative_body_args = _get_positive_and_negative_args(expr_body)
|
33
|
+
|
34
|
+
positive_bound_args, negative_bound_args = _get_positive_and_negative_args(
|
35
|
+
expr_bound
|
36
|
+
)
|
37
|
+
|
38
|
+
modified_expr = LessThan(
|
39
|
+
Add(*positive_body_args) - Add(*negative_bound_args),
|
40
|
+
Add(*positive_bound_args) - Add(*negative_body_args),
|
41
|
+
)
|
42
|
+
|
43
|
+
return modified_expr
|
44
|
+
|
45
|
+
|
46
|
+
def _get_positive_and_negative_args(expr: Expr) -> Tuple[List[Expr], List[Expr]]:
|
47
|
+
positive_args = []
|
48
|
+
negative_args = []
|
49
|
+
|
50
|
+
if isinstance(expr, Add):
|
51
|
+
positive_args += list(filter(_is_positive_expr, expr.args))
|
52
|
+
negative_args += list(filterfalse(_is_positive_expr, expr.args))
|
53
|
+
|
54
|
+
elif _is_positive_expr(expr):
|
55
|
+
positive_args.append(expr)
|
56
|
+
else:
|
57
|
+
negative_args.append(expr)
|
58
|
+
|
59
|
+
if not positive_args and not negative_args:
|
60
|
+
raise ClassiqCombOptError("sign separation didn't worked out")
|
61
|
+
|
62
|
+
return positive_args, negative_args
|
63
|
+
|
64
|
+
|
65
|
+
def _is_positive_expr(expr: Expr) -> bool:
|
66
|
+
return (
|
67
|
+
(isinstance(expr, Number) and expr > 0)
|
68
|
+
or isinstance(expr, Symbol)
|
69
|
+
or (
|
70
|
+
isinstance(expr, Mul)
|
71
|
+
and isinstance(expr.args[0], Number)
|
72
|
+
and expr.args[0] > 0
|
73
|
+
and isinstance(expr.args[1], Symbol)
|
74
|
+
)
|
75
|
+
)
|
classiq/applications_model_constructors/combinatorial_helpers/transformations/slack_variables.py
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
import math
|
2
|
+
from functools import cached_property
|
3
|
+
from itertools import filterfalse
|
4
|
+
from typing import List
|
5
|
+
|
6
|
+
import pyomo.core as pyo
|
7
|
+
from pyomo.core.base.component import _ComponentBase
|
8
|
+
from pyomo.core.expr.sympy_tools import sympy2pyomo_expression, sympyify_expression
|
9
|
+
|
10
|
+
from classiq.applications_model_constructors.combinatorial_helpers import pyomo_utils
|
11
|
+
from classiq.applications_model_constructors.combinatorial_helpers.arithmetic.arithmetic_expression import (
|
12
|
+
multivariate_extremum,
|
13
|
+
)
|
14
|
+
from classiq.applications_model_constructors.combinatorial_helpers.transformations import (
|
15
|
+
penalty_support,
|
16
|
+
)
|
17
|
+
from classiq.applications_model_constructors.combinatorial_helpers.transformations.sign_seperation import (
|
18
|
+
sign_separation,
|
19
|
+
)
|
20
|
+
|
21
|
+
|
22
|
+
def slack_vars_convert(model: pyo.ConcreteModel) -> pyo.ConcreteModel:
|
23
|
+
constraints = pyomo_utils.extract(model, pyo.Constraint)
|
24
|
+
converted_constraints = list(
|
25
|
+
filterfalse(penalty_support.is_constraint_penalty_supported, constraints)
|
26
|
+
)
|
27
|
+
|
28
|
+
for constraint in converted_constraints:
|
29
|
+
convertor = ConstraintConvertor(constraint)
|
30
|
+
setattr(model, convertor.slack_var_name, convertor.slack_var)
|
31
|
+
setattr(model, convertor.slack_constraint_name, convertor.slack_constraint)
|
32
|
+
|
33
|
+
pyomo_utils.delete_component(model, constraint)
|
34
|
+
|
35
|
+
return model
|
36
|
+
|
37
|
+
|
38
|
+
_SLACK_VAR_SUFFIX = "_slack_var"
|
39
|
+
_SLACK_SUFFIX = "_slack"
|
40
|
+
|
41
|
+
|
42
|
+
def is_obj_slacked(var: _ComponentBase) -> bool:
|
43
|
+
return _SLACK_SUFFIX in var.name
|
44
|
+
|
45
|
+
|
46
|
+
class ConstraintConvertor:
|
47
|
+
def __init__(self, constraint: pyo.Constraint) -> None:
|
48
|
+
self._symbols_map, self._expr = sympyify_expression(constraint.expr)
|
49
|
+
self._expr = sign_separation(self._expr)
|
50
|
+
self._expr_lower, self._expr_upper = self._expr.args
|
51
|
+
|
52
|
+
self._name = pyomo_utils.get_name(constraint)
|
53
|
+
|
54
|
+
self.slack_var_name = self._name + _SLACK_VAR_SUFFIX
|
55
|
+
self.slack_var_idxs = range(self._bound_int.bit_length())
|
56
|
+
self.slack_var = pyo.Var(self.slack_var_idxs, domain=pyo.Binary)
|
57
|
+
self.slack_var.construct()
|
58
|
+
|
59
|
+
self.slack_constraint_name = self._name + _SLACK_SUFFIX
|
60
|
+
|
61
|
+
@cached_property
|
62
|
+
def _bound_int(self) -> int:
|
63
|
+
max_upper = math.ceil(
|
64
|
+
multivariate_extremum(self._expr_upper, self._symbols_map, is_min=False)
|
65
|
+
)
|
66
|
+
min_lower = math.floor(
|
67
|
+
multivariate_extremum(self._expr_lower, self._symbols_map, is_min=True)
|
68
|
+
)
|
69
|
+
return max_upper - min_lower
|
70
|
+
|
71
|
+
@cached_property
|
72
|
+
def _slack_coeffs(self) -> List[int]:
|
73
|
+
coeffs = [2**idx for idx in self.slack_var_idxs[:-1]]
|
74
|
+
coeffs += [self._bound_int - sum(coeffs)]
|
75
|
+
return coeffs
|
76
|
+
|
77
|
+
@cached_property
|
78
|
+
def _slack_expr(self) -> pyo.Expression:
|
79
|
+
return sum(
|
80
|
+
coeff * self.slack_var[num] for num, coeff in enumerate(self._slack_coeffs)
|
81
|
+
)
|
82
|
+
|
83
|
+
@cached_property
|
84
|
+
def slack_constraint(self) -> pyo.Constraint:
|
85
|
+
expr_lower_pyomo = sympy2pyomo_expression(self._expr_lower, self._symbols_map)
|
86
|
+
expr_upper_pyomo = sympy2pyomo_expression(self._expr_upper, self._symbols_map)
|
87
|
+
|
88
|
+
return pyo.Constraint(
|
89
|
+
expr=expr_lower_pyomo + self._slack_expr == expr_upper_pyomo
|
90
|
+
)
|
@@ -1,20 +1,19 @@
|
|
1
|
-
import
|
2
|
-
from typing import List, Optional
|
1
|
+
from typing import Optional
|
3
2
|
|
4
|
-
import sympy
|
5
3
|
from pyomo import environ as pyo
|
6
|
-
from pyomo.core import
|
7
|
-
from pyomo.core.base.objective import ScalarObjective
|
8
|
-
from pyomo.core.expr.sympy_tools import Pyomo2SympyVisitor, PyomoSympyBimap
|
4
|
+
from pyomo.core import Objective, maximize
|
9
5
|
|
6
|
+
from classiq.interface.generator.constant import Constant
|
10
7
|
from classiq.interface.generator.expressions.expression import Expression
|
11
|
-
from classiq.interface.generator.functions.classical_type import
|
8
|
+
from classiq.interface.generator.functions.classical_type import (
|
9
|
+
ClassicalArray,
|
10
|
+
ClassicalList,
|
11
|
+
Real,
|
12
|
+
Struct,
|
13
|
+
)
|
12
14
|
from classiq.interface.generator.functions.port_declaration import (
|
13
15
|
PortDeclarationDirection,
|
14
16
|
)
|
15
|
-
from classiq.interface.generator.types.combinatorial_problem import (
|
16
|
-
CombinatorialOptimizationStructDeclaration,
|
17
|
-
)
|
18
17
|
from classiq.interface.model.handle_binding import HandleBinding
|
19
18
|
from classiq.interface.model.model import Model, SerializedModel
|
20
19
|
from classiq.interface.model.native_function_definition import NativeFunctionDefinition
|
@@ -25,62 +24,15 @@ from classiq.applications.combinatorial_optimization.combinatorial_optimization_
|
|
25
24
|
OptimizerConfig,
|
26
25
|
QAOAConfig,
|
27
26
|
)
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
bounds_set = False
|
38
|
-
lower_bound = None
|
39
|
-
upper_bound = None
|
40
|
-
|
41
|
-
for var_dict in pyo_model.component_objects(Var):
|
42
|
-
for key in var_dict:
|
43
|
-
var = Pyomo2SympyVisitor(symbols_map).walk_expression(var_dict[key])
|
44
|
-
var.name = var.name.replace(",", "_")
|
45
|
-
variables.append(var)
|
46
|
-
if bounds_set:
|
47
|
-
if lower_bound != var_dict[key].lb:
|
48
|
-
raise ValueError("All problem variables must agree on lower bound")
|
49
|
-
if upper_bound != var_dict[key].ub:
|
50
|
-
raise ValueError("All problem variables must agree on upper bound")
|
51
|
-
else:
|
52
|
-
lower_bound = var_dict[key].lb
|
53
|
-
upper_bound = var_dict[key].ub
|
54
|
-
bounds_set = True
|
55
|
-
|
56
|
-
constraint_exprs: List[sympy.Expr] = []
|
57
|
-
|
58
|
-
for constraint_dict in pyo_model.component_objects(Constraint):
|
59
|
-
for key in constraint_dict:
|
60
|
-
constraint_exprs.append(
|
61
|
-
Pyomo2SympyVisitor(symbols_map).walk_expression(
|
62
|
-
constraint_dict[key].expr
|
63
|
-
)
|
64
|
-
)
|
65
|
-
|
66
|
-
pyo_objective: ScalarObjective = next(pyo_model.component_objects(Objective))
|
67
|
-
objective_type_str = "Max" if pyo_objective.sense == maximize else "Min"
|
68
|
-
objective_expr: sympy.Expr = Pyomo2SympyVisitor(symbols_map).walk_expression(
|
69
|
-
pyo_objective
|
70
|
-
)
|
71
|
-
|
72
|
-
combi_struct_decl = {
|
73
|
-
"name": struct_name,
|
74
|
-
"variables": {str(variable): {"kind": "int"} for variable in variables},
|
75
|
-
"variable_lower_bound": lower_bound,
|
76
|
-
"variable_upper_bound": upper_bound,
|
77
|
-
"constraints": [
|
78
|
-
{"expr": str(constraint_expr)} for constraint_expr in constraint_exprs
|
79
|
-
],
|
80
|
-
"objective_type": objective_type_str,
|
81
|
-
"objective_function": {"expr": str(objective_expr)},
|
82
|
-
}
|
83
|
-
return json.dumps(combi_struct_decl, indent=2)
|
27
|
+
from classiq.applications_model_constructors.combinatorial_helpers.combinatorial_problem_utils import (
|
28
|
+
compute_qaoa_initial_point,
|
29
|
+
convert_pyomo_to_global_presentation,
|
30
|
+
pyo_model_to_hamiltonian,
|
31
|
+
)
|
32
|
+
from classiq.applications_model_constructors.combinatorial_helpers.pauli_helpers.pauli_utils import (
|
33
|
+
_pauli_operator_to_qmod,
|
34
|
+
get_pauli_operator,
|
35
|
+
)
|
84
36
|
|
85
37
|
|
86
38
|
def construct_combi_opt_py_model(
|
@@ -98,16 +50,24 @@ def construct_combi_opt_py_model(
|
|
98
50
|
if optimizer_config.max_iteration is not None:
|
99
51
|
max_iteration = optimizer_config.max_iteration
|
100
52
|
|
53
|
+
hamiltonian = pyo_model_to_hamiltonian(pyo_model, qaoa_config.penalty_energy)
|
54
|
+
qaoa_initial_point = compute_qaoa_initial_point(hamiltonian, qaoa_config.num_layers)
|
55
|
+
len_hamiltonian = len(hamiltonian[0]["pauli"])
|
56
|
+
pauli_oper = get_pauli_operator(hamiltonian)
|
57
|
+
pauli_qmod = _pauli_operator_to_qmod(pauli_oper)
|
58
|
+
|
101
59
|
initial_point_expression = (
|
102
60
|
f"{optimizer_config.initial_point}"
|
103
61
|
if optimizer_config.initial_point is not None
|
104
|
-
else f"
|
62
|
+
else f"{qaoa_initial_point}"
|
105
63
|
)
|
106
64
|
|
107
65
|
return Model(
|
108
|
-
|
109
|
-
|
110
|
-
|
66
|
+
constants=[
|
67
|
+
Constant(
|
68
|
+
name="hamiltonian",
|
69
|
+
const_type=ClassicalList(element_type=Struct(name="PauliTerm")),
|
70
|
+
value=Expression(expr=f"[{pauli_qmod}]"),
|
111
71
|
)
|
112
72
|
],
|
113
73
|
functions=[
|
@@ -121,9 +81,7 @@ def construct_combi_opt_py_model(
|
|
121
81
|
port_declarations={
|
122
82
|
"target": PortDeclaration(
|
123
83
|
name="target",
|
124
|
-
size=Expression(
|
125
|
-
expr=f"len(get_field(optimization_problem_to_hamiltonian(get_type(MyCombiProblem), {qaoa_config.penalty_energy})[0], 'pauli'))"
|
126
|
-
),
|
84
|
+
size=Expression(expr=f"{len_hamiltonian}"),
|
127
85
|
direction=PortDeclarationDirection.Output,
|
128
86
|
),
|
129
87
|
},
|
@@ -138,12 +96,9 @@ def construct_combi_opt_py_model(
|
|
138
96
|
QuantumFunctionCall(
|
139
97
|
function="qaoa_penalty",
|
140
98
|
params={
|
141
|
-
"hamiltonian": Expression(
|
142
|
-
expr=f"optimization_problem_to_hamiltonian(get_type(MyCombiProblem), {qaoa_config.penalty_energy})"
|
143
|
-
),
|
144
|
-
"params_list": Expression(expr="params_list"),
|
145
99
|
"num_qubits": Expression(expr="len(target)"),
|
146
|
-
"
|
100
|
+
"params_list": Expression(expr="params_list"),
|
101
|
+
"hamiltonian": Expression(expr="hamiltonian"),
|
147
102
|
},
|
148
103
|
inouts={"target": HandleBinding(name="target")},
|
149
104
|
),
|
@@ -152,19 +107,18 @@ def construct_combi_opt_py_model(
|
|
152
107
|
],
|
153
108
|
classical_execution_code=f"""
|
154
109
|
vqe_result = vqe(
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
110
|
+
hamiltonian=hamiltonian,
|
111
|
+
maximize={next(pyo_model.component_objects(Objective)).sense == maximize},
|
112
|
+
initial_point={initial_point_expression},
|
113
|
+
optimizer=Optimizer.{optimizer_config.opt_type},
|
114
|
+
max_iteration={max_iteration},
|
115
|
+
tolerance={optimizer_config.tolerance},
|
116
|
+
step_size={optimizer_config.step_size},
|
117
|
+
skip_compute_variance={optimizer_config.skip_compute_variance},
|
118
|
+
alpha_cvar={optimizer_config.alpha_cvar}
|
164
119
|
)
|
165
|
-
|
166
|
-
|
167
|
-
save({{{_OUTPUT_VARIABLE_NAME!r}: {_OUTPUT_VARIABLE_NAME}, "vqe_result": vqe_result, "hamiltonian": hamiltonian}})
|
120
|
+
|
121
|
+
save({{"vqe_result": vqe_result, "hamiltonian": hamiltonian}})
|
168
122
|
""",
|
169
123
|
)
|
170
124
|
|
@@ -174,5 +128,8 @@ def construct_combinatorial_optimization_model(
|
|
174
128
|
qaoa_config: Optional[QAOAConfig] = None,
|
175
129
|
optimizer_config: Optional[OptimizerConfig] = None,
|
176
130
|
) -> SerializedModel:
|
177
|
-
|
131
|
+
converted_pyo_model = convert_pyomo_to_global_presentation(pyo_model)
|
132
|
+
model = construct_combi_opt_py_model(
|
133
|
+
converted_pyo_model, qaoa_config, optimizer_config
|
134
|
+
)
|
178
135
|
return model.get_model()
|
@@ -4,10 +4,6 @@ from typing import Union
|
|
4
4
|
from classiq.interface.finance.function_input import FinanceFunctionInput
|
5
5
|
from classiq.interface.finance.gaussian_model_input import GaussianModelInput
|
6
6
|
from classiq.interface.finance.log_normal_model_input import LogNormalModelInput
|
7
|
-
from classiq.interface.generator.expressions.enums.finance_functions import (
|
8
|
-
FINANCE_FUNCTION_STRING,
|
9
|
-
FinanceFunctionType,
|
10
|
-
)
|
11
7
|
from classiq.interface.generator.expressions.expression import Expression
|
12
8
|
from classiq.interface.generator.functions.port_declaration import (
|
13
9
|
PortDeclarationDirection,
|
@@ -16,10 +12,8 @@ from classiq.interface.model.handle_binding import HandleBinding
|
|
16
12
|
from classiq.interface.model.model import Model, SerializedModel
|
17
13
|
from classiq.interface.model.native_function_definition import NativeFunctionDefinition
|
18
14
|
from classiq.interface.model.port_declaration import PortDeclaration
|
19
|
-
from classiq.interface.model.quantum_function_call import
|
20
|
-
|
21
|
-
QuantumLambdaFunction,
|
22
|
-
)
|
15
|
+
from classiq.interface.model.quantum_function_call import QuantumFunctionCall
|
16
|
+
from classiq.interface.model.quantum_lambda_function import QuantumLambdaFunction
|
23
17
|
from classiq.interface.model.variable_declaration_statement import (
|
24
18
|
VariableDeclarationStatement,
|
25
19
|
)
|
@@ -56,13 +50,6 @@ def construct_finance_model(
|
|
56
50
|
else:
|
57
51
|
raise ClassiqError(f"Invalid model input: {finance_model_input}")
|
58
52
|
|
59
|
-
if isinstance(finance_function_input.f, FinanceFunctionType):
|
60
|
-
finance_function_f = finance_function_input.f
|
61
|
-
elif isinstance(finance_function_input.f, str):
|
62
|
-
finance_function_f = FINANCE_FUNCTION_STRING[finance_function_input.f]
|
63
|
-
else:
|
64
|
-
finance_function_f = FinanceFunctionType(finance_function_input.f)
|
65
|
-
|
66
53
|
polynomial_degree = 0
|
67
54
|
if finance_function_input.polynomial_degree is not None:
|
68
55
|
polynomial_degree = finance_function_input.polynomial_degree
|
@@ -71,7 +58,7 @@ def construct_finance_model(
|
|
71
58
|
if finance_function_input.tail_probability is not None:
|
72
59
|
tail_probability = finance_function_input.tail_probability
|
73
60
|
|
74
|
-
finance_function_object = f"struct_literal(FinanceFunction, f={
|
61
|
+
finance_function_object = f"struct_literal(FinanceFunction, f={finance_function_input.f}, threshold={finance_function_input.condition.threshold}, larger={finance_function_input.condition.larger}, polynomial_degree={polynomial_degree}, use_chebyshev_polynomial_approximation={finance_function_input.use_chebyshev_polynomial_approximation}, tail_probability={tail_probability})"
|
75
62
|
num_unitary_qubits = total_num_qubits + 1
|
76
63
|
|
77
64
|
model = Model(
|
@@ -91,10 +78,10 @@ def construct_finance_model(
|
|
91
78
|
QuantumFunctionCall(
|
92
79
|
function="qmci",
|
93
80
|
params={
|
81
|
+
"num_phase_qubits": Expression(expr=f"{phase_port_size}"),
|
94
82
|
"num_unitary_qubits": Expression(
|
95
83
|
expr=f"{num_unitary_qubits}"
|
96
84
|
),
|
97
|
-
"num_phase_qubits": Expression(expr=f"{phase_port_size}"),
|
98
85
|
},
|
99
86
|
operands={
|
100
87
|
"sp_op": QuantumLambdaFunction(
|