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
@@ -0,0 +1,107 @@
|
|
1
|
+
import itertools
|
2
|
+
from dataclasses import dataclass, field
|
3
|
+
from typing import Dict, List, Optional, Union
|
4
|
+
|
5
|
+
import pyomo.environ as pyo
|
6
|
+
from pyomo.core.base import _GeneralVarData
|
7
|
+
from pyomo.core.expr.visitor import clone_expression, identify_variables
|
8
|
+
|
9
|
+
from classiq.interface.combinatorial_optimization.encoding_types import EncodingType
|
10
|
+
|
11
|
+
from classiq.applications_model_constructors.combinatorial_helpers import pyomo_utils
|
12
|
+
from classiq.exceptions import ClassiqCombOptError
|
13
|
+
|
14
|
+
|
15
|
+
@dataclass
|
16
|
+
class VarExpressionMapping:
|
17
|
+
var: _GeneralVarData
|
18
|
+
expr: pyo.Expression
|
19
|
+
encodings_vars: List[_GeneralVarData] = field(default_factory=list)
|
20
|
+
|
21
|
+
|
22
|
+
class EncodingMapping:
|
23
|
+
def __init__(self, encoding_type: EncodingType) -> None:
|
24
|
+
self._data: List[VarExpressionMapping] = []
|
25
|
+
self.encoding_type = encoding_type
|
26
|
+
|
27
|
+
@property
|
28
|
+
def original_vars(self) -> List[_GeneralVarData]:
|
29
|
+
return [pair.var for pair in self._data]
|
30
|
+
|
31
|
+
@property
|
32
|
+
def encodings_vars(self) -> List[_GeneralVarData]:
|
33
|
+
return list(
|
34
|
+
itertools.chain.from_iterable(
|
35
|
+
var_mapping.encodings_vars for var_mapping in self._data
|
36
|
+
)
|
37
|
+
)
|
38
|
+
|
39
|
+
@property
|
40
|
+
def substitution_dict(self) -> Dict[int, pyo.Expression]:
|
41
|
+
return {id(mapping.var): mapping.expr for mapping in self._data}
|
42
|
+
|
43
|
+
def __len__(self) -> int:
|
44
|
+
return len(self._data)
|
45
|
+
|
46
|
+
def add(
|
47
|
+
self,
|
48
|
+
original_var: _GeneralVarData,
|
49
|
+
encoding_expr: pyo.Expression,
|
50
|
+
encodings_vars: Union[List[_GeneralVarData], None] = None,
|
51
|
+
) -> None:
|
52
|
+
if encodings_vars is None:
|
53
|
+
encodings_vars = list(identify_variables(encoding_expr))
|
54
|
+
|
55
|
+
self._check_unique_encoding_vars(encodings_vars)
|
56
|
+
self._data.append(
|
57
|
+
VarExpressionMapping(
|
58
|
+
var=original_var, expr=encoding_expr, encodings_vars=encodings_vars
|
59
|
+
)
|
60
|
+
)
|
61
|
+
|
62
|
+
def _check_unique_encoding_vars(self, variables: List[_GeneralVarData]) -> None:
|
63
|
+
assert all(
|
64
|
+
not pyomo_utils.contains(var, self.encodings_vars) for var in variables
|
65
|
+
)
|
66
|
+
|
67
|
+
def get_var_expr_mapping(
|
68
|
+
self, original_var: _GeneralVarData
|
69
|
+
) -> VarExpressionMapping:
|
70
|
+
for var_expr_mapping in self._data:
|
71
|
+
if var_expr_mapping.var is original_var:
|
72
|
+
return var_expr_mapping
|
73
|
+
raise ClassiqCombOptError("No variable expression mapping found.")
|
74
|
+
|
75
|
+
def get_encoding_vars(self, original_var: _GeneralVarData) -> List[_GeneralVarData]:
|
76
|
+
return self.get_var_expr_mapping(original_var).encodings_vars
|
77
|
+
|
78
|
+
def get_original_var(
|
79
|
+
self, encoding_var: _GeneralVarData
|
80
|
+
) -> Optional[_GeneralVarData]:
|
81
|
+
for original_var in self.original_vars:
|
82
|
+
if pyomo_utils.contains(encoding_var, self.get_encoding_vars(original_var)):
|
83
|
+
return original_var
|
84
|
+
return None
|
85
|
+
|
86
|
+
def decode(self, solution: List[int]) -> List[int]:
|
87
|
+
idx = 0
|
88
|
+
decoded_solution = []
|
89
|
+
for var_mapping in self._data:
|
90
|
+
num_encoding_vars = len(var_mapping.encodings_vars)
|
91
|
+
encoding_vars_solution = solution[idx : idx + num_encoding_vars]
|
92
|
+
idx += num_encoding_vars
|
93
|
+
|
94
|
+
substitution_map = {
|
95
|
+
id(var): num
|
96
|
+
for var, num in zip(var_mapping.encodings_vars, encoding_vars_solution)
|
97
|
+
}
|
98
|
+
substituted_expr = clone_expression(
|
99
|
+
var_mapping.expr, substitute=substitution_map
|
100
|
+
)
|
101
|
+
|
102
|
+
if callable(substituted_expr):
|
103
|
+
substituted_expr = substituted_expr()
|
104
|
+
|
105
|
+
decoded_solution.append(substituted_expr)
|
106
|
+
|
107
|
+
return decoded_solution
|
@@ -0,0 +1,122 @@
|
|
1
|
+
import math
|
2
|
+
from itertools import filterfalse
|
3
|
+
from typing import Any, Dict, Union, cast
|
4
|
+
|
5
|
+
import numpy as np
|
6
|
+
import pyomo.environ as pyo
|
7
|
+
from pyomo.core.base import _GeneralVarData
|
8
|
+
from pyomo.core.base.component import ComponentData, _ComponentBase
|
9
|
+
from pyomo.core.base.constraint import _GeneralConstraintData
|
10
|
+
from pyomo.core.expr.numeric_expr import (
|
11
|
+
MonomialTermExpression,
|
12
|
+
ProductExpression,
|
13
|
+
clone_expression,
|
14
|
+
)
|
15
|
+
from sympy import Expr
|
16
|
+
|
17
|
+
from classiq.applications_model_constructors.combinatorial_helpers import pyomo_utils
|
18
|
+
from classiq.exceptions import ClassiqCombOptNoSolutionError
|
19
|
+
|
20
|
+
_INTEGER_TYPES = [pyo.NonNegativeIntegers, pyo.Integers, pyo.PositiveIntegers]
|
21
|
+
|
22
|
+
|
23
|
+
def is_model_encodable(model: pyo.ConcreteModel) -> bool:
|
24
|
+
variables = pyomo_utils.extract(model, pyo.Var)
|
25
|
+
return not all(is_var_binary(var) for var in variables)
|
26
|
+
|
27
|
+
|
28
|
+
def is_var_binary(var: _GeneralVarData) -> bool:
|
29
|
+
return var.domain == pyo.Binary or (
|
30
|
+
var.domain in _INTEGER_TYPES and var.lb == 0 and var.ub == 1
|
31
|
+
)
|
32
|
+
|
33
|
+
|
34
|
+
def is_var_span_power_of_2(var: _GeneralVarData) -> bool:
|
35
|
+
var_span = get_var_span(var)
|
36
|
+
return math.log2(var_span + 1).is_integer()
|
37
|
+
|
38
|
+
|
39
|
+
ENCODED_SUFFIX = "_encoded"
|
40
|
+
ONE_HOT_SUFFIX = "_one_hot"
|
41
|
+
|
42
|
+
|
43
|
+
def is_obj_encoded(var: _ComponentBase) -> bool:
|
44
|
+
return ENCODED_SUFFIX in var.name
|
45
|
+
|
46
|
+
|
47
|
+
def get_var_span(var: _GeneralVarData) -> int:
|
48
|
+
return var.ub - var.lb
|
49
|
+
|
50
|
+
|
51
|
+
def encoded_obj_name(name: str) -> str:
|
52
|
+
return name + ENCODED_SUFFIX
|
53
|
+
|
54
|
+
|
55
|
+
def get_encoded_var_index(var: _GeneralVarData) -> int:
|
56
|
+
indexed_var = var.parent_component()
|
57
|
+
index = [
|
58
|
+
index_temp for index_temp, var_temp in indexed_var.items() if var_temp is var
|
59
|
+
][0]
|
60
|
+
return index[1]
|
61
|
+
|
62
|
+
|
63
|
+
def recursively_remove_monomial_expr(obj: Any) -> None:
|
64
|
+
# Due to pyomo bug. see: https://github.com/Pyomo/pyomo/issues/2174
|
65
|
+
for arg in getattr(obj, "args", []):
|
66
|
+
if isinstance(arg, MonomialTermExpression):
|
67
|
+
arg.__class__ = ProductExpression
|
68
|
+
recursively_remove_monomial_expr(arg)
|
69
|
+
|
70
|
+
|
71
|
+
def encode_expr(
|
72
|
+
expr: pyo.Expression, substitution_dict: Dict[int, pyo.Expression]
|
73
|
+
) -> pyo.Expression:
|
74
|
+
encoded_expr = clone_expression(expr=expr, substitute=substitution_dict)
|
75
|
+
recursively_remove_monomial_expr(encoded_expr)
|
76
|
+
return encoded_expr
|
77
|
+
|
78
|
+
|
79
|
+
def encode_constraints(
|
80
|
+
model: pyo.ConcreteModel, substitution_dict: Dict[int, pyo.Expression]
|
81
|
+
) -> None:
|
82
|
+
all_constraints = pyomo_utils.extract(model, _GeneralConstraintData)
|
83
|
+
constraints = filterfalse(is_obj_encoded, all_constraints)
|
84
|
+
|
85
|
+
for constraint in constraints:
|
86
|
+
constraint_expression = encode_expr(constraint.expr, substitution_dict)
|
87
|
+
if not isinstance(constraint_expression, bool) and not isinstance(
|
88
|
+
constraint_expression, np.bool_
|
89
|
+
):
|
90
|
+
constraint.set_value(expr=constraint_expression)
|
91
|
+
continue
|
92
|
+
|
93
|
+
deal_with_trivial_boolean_constraint(constraint, constraint_expression, model)
|
94
|
+
|
95
|
+
|
96
|
+
def deal_with_trivial_boolean_constraint(
|
97
|
+
constraint: _ComponentBase,
|
98
|
+
constraint_expression: Union[bool, Expr],
|
99
|
+
model: pyo.ConcreteModel,
|
100
|
+
) -> None:
|
101
|
+
# using '==' on purpose since comparing against sympy's True
|
102
|
+
if constraint_expression == True: # noqa: E712
|
103
|
+
pyomo_utils.delete_component(model, cast(ComponentData, constraint))
|
104
|
+
if constraint_expression == False: # noqa: E712
|
105
|
+
raise ClassiqCombOptNoSolutionError
|
106
|
+
|
107
|
+
|
108
|
+
def encode_objective(
|
109
|
+
model: pyo.ConcreteModel, substitution_dict: Dict[int, pyo.Expression]
|
110
|
+
) -> None:
|
111
|
+
objective = next(model.component_objects(pyo.Objective))
|
112
|
+
|
113
|
+
encoded_objective = pyo.Objective(
|
114
|
+
expr=encode_expr(objective.expr, substitution_dict),
|
115
|
+
sense=objective.sense,
|
116
|
+
)
|
117
|
+
model.del_component(objective.name)
|
118
|
+
setattr(
|
119
|
+
model,
|
120
|
+
objective.name,
|
121
|
+
encoded_objective,
|
122
|
+
)
|
@@ -0,0 +1,79 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from typing import Dict, List, Tuple
|
4
|
+
|
5
|
+
from pyomo.core.base import _GeneralVarData
|
6
|
+
|
7
|
+
from classiq.interface.generator.function_params import IOName
|
8
|
+
|
9
|
+
from classiq.applications_model_constructors.combinatorial_helpers import encoding_utils
|
10
|
+
from classiq.applications_model_constructors.combinatorial_helpers.encoding_mapping import (
|
11
|
+
EncodingMapping,
|
12
|
+
)
|
13
|
+
|
14
|
+
AUXILIARY_NAME = "auxiliary"
|
15
|
+
|
16
|
+
|
17
|
+
class InternalQuantumReg:
|
18
|
+
def __init__(self, size: int, name: str) -> None:
|
19
|
+
self.name = name
|
20
|
+
self.size = size
|
21
|
+
|
22
|
+
|
23
|
+
class MemoryMapping:
|
24
|
+
def __init__(
|
25
|
+
self,
|
26
|
+
variables: List[_GeneralVarData],
|
27
|
+
vars_encoding_mapping: EncodingMapping | None = None,
|
28
|
+
) -> None:
|
29
|
+
self.substitution_dict: Dict[int, InternalQuantumReg] = dict()
|
30
|
+
self.qubit_allocation: Dict[IOName, Tuple[int, int]] = dict()
|
31
|
+
self.vars_encoding_mapping: EncodingMapping | None = vars_encoding_mapping
|
32
|
+
self.vars: List[_GeneralVarData] = variables
|
33
|
+
self._allocate_memory()
|
34
|
+
|
35
|
+
def __len__(self) -> int:
|
36
|
+
return len(self.substitution_dict)
|
37
|
+
|
38
|
+
def _allocate_memory(self) -> None:
|
39
|
+
for var_data in self.vars:
|
40
|
+
if (
|
41
|
+
not self.vars_encoding_mapping
|
42
|
+
or encoding_utils.get_var_span(var_data) == 1
|
43
|
+
):
|
44
|
+
num_qubits_var = 1
|
45
|
+
else:
|
46
|
+
num_qubits_var = len(
|
47
|
+
self.vars_encoding_mapping.get_encoding_vars(var_data)
|
48
|
+
)
|
49
|
+
|
50
|
+
self.substitution_dict[id(var_data)] = InternalQuantumReg(
|
51
|
+
num_qubits_var, "q"
|
52
|
+
)
|
53
|
+
var_name = get_var_name(var_data)
|
54
|
+
self.qubit_allocation[var_name] = (
|
55
|
+
self._variable_qubits_allocated,
|
56
|
+
num_qubits_var,
|
57
|
+
)
|
58
|
+
|
59
|
+
@property
|
60
|
+
def qregs(self) -> List[InternalQuantumReg]:
|
61
|
+
return list(self.substitution_dict.values())
|
62
|
+
|
63
|
+
@property
|
64
|
+
def _variable_qubits_allocated(self) -> int:
|
65
|
+
return sum(
|
66
|
+
num_qubits
|
67
|
+
for name, (_, num_qubits) in self.qubit_allocation.items()
|
68
|
+
if name != AUXILIARY_NAME
|
69
|
+
)
|
70
|
+
|
71
|
+
|
72
|
+
def get_var_name(var: _GeneralVarData) -> str:
|
73
|
+
return (
|
74
|
+
var.name.replace("[", "")
|
75
|
+
.replace("]", "")
|
76
|
+
.replace(",", "")
|
77
|
+
.replace("'", "")
|
78
|
+
.replace('"', "")
|
79
|
+
)
|
@@ -0,0 +1,34 @@
|
|
1
|
+
from classiq.interface.generator.function_params import DEFAULT_OUTPUT_NAME
|
2
|
+
from classiq.interface.generator.functions import SynthesisNativeFunctionDefinition
|
3
|
+
from classiq.interface.generator.state_preparation import (
|
4
|
+
ComputationalBasisStatePreparation,
|
5
|
+
)
|
6
|
+
|
7
|
+
from classiq import FunctionGenerator, QReg
|
8
|
+
|
9
|
+
BinaryStr = str
|
10
|
+
Name = str
|
11
|
+
|
12
|
+
|
13
|
+
def multiple_comp_basis_sp(
|
14
|
+
function_name: str, states: dict[Name, BinaryStr], call_name_frmt: str = "{}"
|
15
|
+
) -> SynthesisNativeFunctionDefinition:
|
16
|
+
generator = FunctionGenerator(function_name=function_name)
|
17
|
+
qregs_dict = {
|
18
|
+
var_name: QReg[len(state)] # type:ignore[misc]
|
19
|
+
for var_name, state in states.items()
|
20
|
+
}
|
21
|
+
qregs = generator.create_inputs(qregs_dict)
|
22
|
+
|
23
|
+
for var_name, state in states.items():
|
24
|
+
params = ComputationalBasisStatePreparation(computational_state=state)
|
25
|
+
qregs[var_name] = generator.ComputationalBasisStatePreparation(
|
26
|
+
params,
|
27
|
+
in_wires=qregs[var_name],
|
28
|
+
strict_zero_ios=False,
|
29
|
+
call_name=call_name_frmt.format(var_name),
|
30
|
+
)[DEFAULT_OUTPUT_NAME]
|
31
|
+
|
32
|
+
generator.set_outputs(qregs)
|
33
|
+
|
34
|
+
return generator.to_function_definition()
|
@@ -0,0 +1,166 @@
|
|
1
|
+
import copy
|
2
|
+
from itertools import filterfalse
|
3
|
+
from typing import List, Optional, Union
|
4
|
+
|
5
|
+
import pyomo.environ as pyo
|
6
|
+
from pyomo.core import ConcreteModel
|
7
|
+
from pyomo.core.base import _GeneralVarData
|
8
|
+
from pyomo.core.base.constraint import _GeneralConstraintData
|
9
|
+
from pyomo.environ import Expression
|
10
|
+
|
11
|
+
from classiq.interface.chemistry.operator import PauliOperator
|
12
|
+
from classiq.interface.combinatorial_optimization import sense
|
13
|
+
from classiq.interface.combinatorial_optimization.encoding_types import EncodingType
|
14
|
+
from classiq.interface.combinatorial_optimization.solver_types import QSolver
|
15
|
+
|
16
|
+
from classiq.applications_model_constructors.combinatorial_helpers import (
|
17
|
+
encoding_utils,
|
18
|
+
memory,
|
19
|
+
pyomo_utils,
|
20
|
+
)
|
21
|
+
from classiq.applications_model_constructors.combinatorial_helpers.encoding_mapping import (
|
22
|
+
EncodingMapping,
|
23
|
+
)
|
24
|
+
from classiq.applications_model_constructors.combinatorial_helpers.memory import (
|
25
|
+
InternalQuantumReg,
|
26
|
+
)
|
27
|
+
from classiq.applications_model_constructors.combinatorial_helpers.transformations import (
|
28
|
+
encoding,
|
29
|
+
ising_converter,
|
30
|
+
penalty,
|
31
|
+
slack_variables,
|
32
|
+
)
|
33
|
+
from classiq.applications_model_constructors.combinatorial_helpers.transformations.fixed_variables import (
|
34
|
+
add_fixed_variables_to_solution,
|
35
|
+
remove_fixed_variables,
|
36
|
+
)
|
37
|
+
from classiq.applications_model_constructors.combinatorial_helpers.transformations.penalty_support import (
|
38
|
+
is_model_penalty_supported,
|
39
|
+
)
|
40
|
+
from classiq.exceptions import ClassiqCombOptError
|
41
|
+
|
42
|
+
|
43
|
+
class OptimizationModel:
|
44
|
+
def __init__(
|
45
|
+
self,
|
46
|
+
model: ConcreteModel,
|
47
|
+
qsolver: QSolver,
|
48
|
+
penalty_energy: Optional[float],
|
49
|
+
encoding_type: Optional[EncodingType] = None,
|
50
|
+
) -> None:
|
51
|
+
assert model.nobjectives() == 1, "model must have a single objective"
|
52
|
+
self._model_original = copy.deepcopy(model)
|
53
|
+
self._assigned_model = remove_fixed_variables(model)
|
54
|
+
self.qsolver = qsolver
|
55
|
+
self._encoding_type = encoding_type
|
56
|
+
|
57
|
+
self.is_encoded = encoding_utils.is_model_encodable(model)
|
58
|
+
if self.is_encoded:
|
59
|
+
if self._encoding_type is None:
|
60
|
+
self._encoding_type = EncodingType.BINARY
|
61
|
+
self._model_encoder = encoding.ModelEncoder(
|
62
|
+
model, qsolver, self._encoding_type
|
63
|
+
)
|
64
|
+
self._model = self._model_encoder.encoded_model
|
65
|
+
self._vars_encoding_mapping = self._model_encoder.vars_encoding_mapping
|
66
|
+
else:
|
67
|
+
self._model = model
|
68
|
+
# TODO How to handle encoding_type == None
|
69
|
+
self._vars_encoding_mapping = EncodingMapping(self._encoding_type) # type: ignore[arg-type]
|
70
|
+
|
71
|
+
self._slack_vars_convert()
|
72
|
+
|
73
|
+
self.memory_mapping = memory.MemoryMapping(
|
74
|
+
self.vars_not_encoded, self._vars_encoding_mapping
|
75
|
+
)
|
76
|
+
|
77
|
+
self.is_maximization = sense.is_maximization(model)
|
78
|
+
self.objective = next(self._model.component_objects(pyo.Objective))
|
79
|
+
|
80
|
+
self.penalty_energy = penalty_energy
|
81
|
+
self._add_penalty_term()
|
82
|
+
|
83
|
+
self.ising: PauliOperator = self._to_ising()
|
84
|
+
|
85
|
+
@property
|
86
|
+
def vars(self) -> List[_GeneralVarData]:
|
87
|
+
return pyomo_utils.extract(self._model, _GeneralVarData)
|
88
|
+
|
89
|
+
@property
|
90
|
+
def vars_not_encoded(self) -> List[_GeneralVarData]:
|
91
|
+
return list(filterfalse(encoding_utils.is_obj_encoded, self.vars))
|
92
|
+
|
93
|
+
@property
|
94
|
+
def _ising_vars(self) -> List[_GeneralVarData]:
|
95
|
+
if self.is_encoded:
|
96
|
+
return [
|
97
|
+
var
|
98
|
+
for var in self.vars
|
99
|
+
if encoding_utils.is_obj_encoded(var)
|
100
|
+
or slack_variables.is_obj_slacked(var)
|
101
|
+
]
|
102
|
+
else:
|
103
|
+
return self.vars
|
104
|
+
|
105
|
+
@property
|
106
|
+
def constraints(self) -> List[_GeneralConstraintData]:
|
107
|
+
all_constraints = pyomo_utils.extract(self._model, _GeneralConstraintData)
|
108
|
+
return list(filterfalse(encoding_utils.is_obj_encoded, all_constraints))
|
109
|
+
|
110
|
+
@property
|
111
|
+
def qregs(self) -> List[InternalQuantumReg]:
|
112
|
+
return self.memory_mapping.qregs
|
113
|
+
|
114
|
+
@property
|
115
|
+
def num_qubits(self) -> int:
|
116
|
+
return sum(qreg.size for qreg in self.qregs)
|
117
|
+
|
118
|
+
@property
|
119
|
+
def sign(self) -> int:
|
120
|
+
return -1 if self.is_maximization else 1
|
121
|
+
|
122
|
+
def _add_penalty_term(self) -> None:
|
123
|
+
if self.qsolver == QSolver.QAOAPenalty:
|
124
|
+
self.objective.expr += self._get_penalty_term()
|
125
|
+
|
126
|
+
def _get_penalty_term(self) -> Union[int, Expression]:
|
127
|
+
normalized_penalty_term = penalty.get_penalty_expression(self.constraints)
|
128
|
+
penalty_term = self.penalty_energy * normalized_penalty_term * self.sign
|
129
|
+
|
130
|
+
if self.is_encoded:
|
131
|
+
penalty_term = self._model_encoder.encode_expr(penalty_term)
|
132
|
+
return penalty_term
|
133
|
+
|
134
|
+
def _to_ising(self) -> PauliOperator:
|
135
|
+
return (
|
136
|
+
ising_converter.convert_pyomo_to_hamiltonian(
|
137
|
+
self.objective.expr, self._ising_vars, self.qregs
|
138
|
+
)
|
139
|
+
* self.sign
|
140
|
+
)
|
141
|
+
|
142
|
+
def _remove_slack_variables_from_solution(self, solution: List[int]) -> List[int]:
|
143
|
+
variables = pyomo_utils.extract(self._model_original, pyo.Var)
|
144
|
+
return solution[: len(variables)]
|
145
|
+
|
146
|
+
def decode(self, solution: List[int]) -> List[int]:
|
147
|
+
if self.is_encoded:
|
148
|
+
solution = self._vars_encoding_mapping.decode(solution)
|
149
|
+
|
150
|
+
solution = add_fixed_variables_to_solution(self._model_original, solution)
|
151
|
+
|
152
|
+
return self._remove_slack_variables_from_solution(solution)
|
153
|
+
|
154
|
+
def get_operator(self) -> PauliOperator:
|
155
|
+
try:
|
156
|
+
return self.ising
|
157
|
+
except Exception as exc:
|
158
|
+
raise ClassiqCombOptError(
|
159
|
+
f"Could not convert optimization model to operator: {exc}"
|
160
|
+
) from exc
|
161
|
+
|
162
|
+
def _slack_vars_convert(self) -> ConcreteModel:
|
163
|
+
if self.qsolver == QSolver.QAOAPenalty and not is_model_penalty_supported(
|
164
|
+
self._model
|
165
|
+
):
|
166
|
+
return slack_variables.slack_vars_convert(self._model)
|
File without changes
|
@@ -0,0 +1,31 @@
|
|
1
|
+
from typing import Any, Union
|
2
|
+
|
3
|
+
import numpy as np
|
4
|
+
|
5
|
+
|
6
|
+
class SparsePauliOp:
|
7
|
+
def __init__(self, paulis: Any, coeffs: Any) -> None:
|
8
|
+
assert len(paulis) == len(
|
9
|
+
coeffs
|
10
|
+
), "Paulis and coefficients lists must have the same length."
|
11
|
+
self.paulis = np.array(paulis)
|
12
|
+
self.coeffs = np.array(coeffs, dtype=complex)
|
13
|
+
|
14
|
+
def __str__(self) -> str:
|
15
|
+
terms = [f"{coef}*{pauli}" for coef, pauli in zip(self.coeffs, self.paulis)]
|
16
|
+
return " + ".join(terms)
|
17
|
+
|
18
|
+
def __add__(self, other: "SparsePauliOp") -> "SparsePauliOp":
|
19
|
+
"""Add two SparsePauliOp objects."""
|
20
|
+
if not isinstance(other, SparsePauliOp):
|
21
|
+
raise ValueError("Can only add SparsePauliOp objects.")
|
22
|
+
new_paulis = np.concatenate([self.paulis, other.paulis])
|
23
|
+
new_coeffs = np.concatenate([self.coeffs, other.coeffs])
|
24
|
+
return SparsePauliOp(new_paulis, new_coeffs)
|
25
|
+
|
26
|
+
def __mul__(self, other: Union[int, float, complex]) -> "SparsePauliOp":
|
27
|
+
"""Scalar multiplication of a SparsePauliOp."""
|
28
|
+
if not isinstance(other, (int, float, complex)):
|
29
|
+
raise ValueError("Can only multiply by scalar values.")
|
30
|
+
new_coeffs = self.coeffs * other
|
31
|
+
return SparsePauliOp(self.paulis, new_coeffs)
|
@@ -0,0 +1,65 @@
|
|
1
|
+
from typing import List, cast
|
2
|
+
|
3
|
+
from classiq.interface.chemistry.operator import PauliOperator
|
4
|
+
from classiq.interface.generator.expressions.enums.pauli import Pauli
|
5
|
+
from classiq.interface.generator.functions.qmod_python_interface import QmodPyStruct
|
6
|
+
from classiq.interface.helpers.custom_pydantic_types import PydanticPauliList
|
7
|
+
|
8
|
+
from classiq.exceptions import (
|
9
|
+
ClassiqExecutorInvalidHamiltonianError,
|
10
|
+
ClassiqNonNumericCoefficientInPauliError,
|
11
|
+
ClassiqValueError,
|
12
|
+
)
|
13
|
+
|
14
|
+
|
15
|
+
def _pauli_str_to_enums(pauli_str: str) -> str:
|
16
|
+
return ", ".join(f"Pauli.{pauli_term}" for pauli_term in pauli_str)
|
17
|
+
|
18
|
+
|
19
|
+
def pauli_integers_to_str(paulis: List[Pauli]) -> str:
|
20
|
+
return "".join([Pauli(pauli).name for pauli in paulis])
|
21
|
+
|
22
|
+
|
23
|
+
def pauli_operator_to_hamiltonian(pauli_list: PydanticPauliList) -> List[QmodPyStruct]:
|
24
|
+
pauli_struct_list: List[QmodPyStruct] = []
|
25
|
+
for pauli_term in pauli_list:
|
26
|
+
if not isinstance(pauli_term[1], complex) or pauli_term[1].imag != 0:
|
27
|
+
raise ClassiqNonNumericCoefficientInPauliError(
|
28
|
+
"Coefficient is not a number."
|
29
|
+
)
|
30
|
+
|
31
|
+
pauli_struct_list.append(
|
32
|
+
{
|
33
|
+
"pauli": [Pauli[p] for p in pauli_term[0]],
|
34
|
+
"coefficient": pauli_term[1].real,
|
35
|
+
}
|
36
|
+
)
|
37
|
+
return pauli_struct_list
|
38
|
+
|
39
|
+
|
40
|
+
def get_pauli_operator(pauli_operator: List[QmodPyStruct]) -> PauliOperator:
|
41
|
+
pauli_list = [
|
42
|
+
(
|
43
|
+
pauli_integers_to_str(elem["pauli"]),
|
44
|
+
elem["coefficient"],
|
45
|
+
)
|
46
|
+
for elem in pauli_operator
|
47
|
+
]
|
48
|
+
|
49
|
+
try:
|
50
|
+
pauli = PauliOperator(pauli_list=pauli_list)
|
51
|
+
except ValueError:
|
52
|
+
raise ClassiqExecutorInvalidHamiltonianError() from None
|
53
|
+
|
54
|
+
return pauli
|
55
|
+
|
56
|
+
|
57
|
+
def _pauli_operator_to_qmod(hamiltonian: PauliOperator) -> str:
|
58
|
+
if not all(isinstance(summand[1], complex) for summand in hamiltonian.pauli_list):
|
59
|
+
raise ClassiqValueError(
|
60
|
+
"Supporting only Hamiltonian with numeric coefficients."
|
61
|
+
)
|
62
|
+
return ", ".join(
|
63
|
+
f"struct_literal(PauliTerm, pauli=[{_pauli_str_to_enums(pauli)}], coefficient={cast(complex, coeff).real})"
|
64
|
+
for pauli, coeff in hamiltonian.pauli_list
|
65
|
+
)
|
File without changes
|