classiq 0.86.1__py3-none-any.whl → 0.87.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 -0
- classiq/applications/chemistry/hartree_fock.py +5 -1
- classiq/applications/chemistry/op_utils.py +2 -2
- classiq/applications/combinatorial_helpers/encoding_mapping.py +11 -15
- classiq/applications/combinatorial_helpers/encoding_utils.py +6 -6
- classiq/applications/combinatorial_helpers/memory.py +4 -4
- classiq/applications/combinatorial_helpers/optimization_model.py +5 -5
- classiq/applications/combinatorial_helpers/pauli_helpers/pauli_utils.py +6 -10
- classiq/applications/combinatorial_helpers/pyomo_utils.py +27 -26
- classiq/applications/combinatorial_helpers/sympy_utils.py +2 -2
- classiq/applications/combinatorial_helpers/transformations/encoding.py +4 -6
- classiq/applications/combinatorial_helpers/transformations/fixed_variables.py +4 -4
- classiq/applications/combinatorial_helpers/transformations/ising_converter.py +2 -2
- classiq/applications/combinatorial_helpers/transformations/penalty_support.py +3 -3
- classiq/applications/combinatorial_optimization/combinatorial_problem.py +4 -0
- classiq/applications/hamiltonian/pauli_decomposition.py +33 -1
- classiq/evaluators/argument_types.py +15 -6
- classiq/evaluators/parameter_types.py +43 -39
- classiq/evaluators/qmod_annotated_expression.py +88 -11
- classiq/evaluators/qmod_expression_visitors/out_of_place_node_transformer.py +19 -0
- classiq/evaluators/qmod_expression_visitors/qmod_expression_evaluator.py +54 -11
- classiq/evaluators/qmod_expression_visitors/qmod_expression_renamer.py +40 -25
- classiq/evaluators/qmod_expression_visitors/qmod_expression_simplifier.py +29 -59
- classiq/evaluators/qmod_node_evaluators/attribute_evaluation.py +12 -5
- classiq/evaluators/qmod_node_evaluators/binary_op_evaluation.py +175 -28
- classiq/evaluators/qmod_node_evaluators/classical_function_evaluation.py +21 -14
- classiq/evaluators/qmod_node_evaluators/compare_evaluation.py +9 -5
- classiq/evaluators/qmod_node_evaluators/constant_evaluation.py +20 -1
- classiq/evaluators/qmod_node_evaluators/min_max_evaluation.py +97 -0
- classiq/evaluators/qmod_node_evaluators/name_evaluation.py +11 -26
- classiq/evaluators/qmod_node_evaluators/numeric_attrs_utils.py +56 -0
- classiq/evaluators/qmod_node_evaluators/piecewise_evaluation.py +40 -0
- classiq/evaluators/qmod_node_evaluators/struct_instantiation_evaluation.py +2 -3
- classiq/evaluators/qmod_node_evaluators/subscript_evaluation.py +48 -21
- classiq/evaluators/qmod_node_evaluators/unary_op_evaluation.py +53 -9
- classiq/evaluators/qmod_node_evaluators/utils.py +27 -5
- classiq/evaluators/qmod_type_inference/classical_type_inference.py +188 -0
- classiq/evaluators/qmod_type_inference/quantum_type_inference.py +292 -0
- classiq/evaluators/quantum_type_utils.py +0 -131
- classiq/execution/execution_session.py +1 -1
- classiq/execution/qnn.py +4 -1
- classiq/execution/user_budgets.py +1 -1
- classiq/interface/_version.py +1 -1
- classiq/interface/backend/backend_preferences.py +10 -30
- classiq/interface/backend/quantum_backend_providers.py +63 -52
- classiq/interface/generator/arith/binary_ops.py +107 -115
- classiq/interface/generator/arith/extremum_operations.py +33 -45
- classiq/interface/generator/arith/number_utils.py +4 -1
- classiq/interface/generator/circuit_code/types_and_constants.py +0 -9
- classiq/interface/generator/compiler_keywords.py +2 -0
- classiq/interface/generator/function_param_list.py +133 -5
- classiq/interface/generator/functions/classical_type.py +59 -2
- classiq/interface/generator/functions/qmod_python_interface.py +15 -0
- classiq/interface/generator/functions/type_name.py +6 -0
- classiq/interface/generator/model/preferences/preferences.py +1 -17
- classiq/interface/generator/quantum_program.py +1 -13
- classiq/interface/helpers/model_normalizer.py +2 -2
- classiq/interface/helpers/text_utils.py +7 -2
- classiq/interface/interface_version.py +1 -1
- classiq/interface/model/classical_if.py +40 -0
- classiq/interface/model/handle_binding.py +28 -16
- classiq/interface/model/quantum_type.py +61 -2
- classiq/interface/pretty_print/expression_to_qmod.py +24 -11
- classiq/interface/pyomo_extension/__init__.py +0 -4
- classiq/interface/pyomo_extension/pyomo_sympy_bimap.py +2 -2
- classiq/model_expansions/arithmetic.py +43 -1
- classiq/model_expansions/arithmetic_compute_result_attrs.py +255 -0
- classiq/model_expansions/capturing/captured_vars.py +2 -5
- classiq/model_expansions/quantum_operations/allocate.py +22 -15
- classiq/model_expansions/quantum_operations/arithmetic/explicit_boolean_expressions.py +1 -3
- classiq/model_expansions/quantum_operations/assignment_result_processor.py +52 -71
- classiq/model_expansions/quantum_operations/bind.py +15 -7
- classiq/model_expansions/quantum_operations/call_emitter.py +2 -10
- classiq/model_expansions/quantum_operations/classical_var_emitter.py +6 -0
- classiq/open_library/functions/__init__.py +3 -0
- classiq/open_library/functions/lcu.py +117 -0
- classiq/qmod/builtins/enums.py +2 -2
- classiq/qmod/builtins/structs.py +33 -18
- classiq/qmod/pretty_print/expression_to_python.py +7 -9
- {classiq-0.86.1.dist-info → classiq-0.87.0.dist-info}/METADATA +3 -3
- {classiq-0.86.1.dist-info → classiq-0.87.0.dist-info}/RECORD +83 -89
- classiq/interface/generator/amplitude_estimation.py +0 -34
- classiq/interface/generator/function_param_list_without_self_reference.py +0 -160
- classiq/interface/generator/grover_diffuser.py +0 -93
- classiq/interface/generator/grover_operator.py +0 -106
- classiq/interface/generator/oracles/__init__.py +0 -3
- classiq/interface/generator/oracles/arithmetic_oracle.py +0 -82
- classiq/interface/generator/oracles/custom_oracle.py +0 -65
- classiq/interface/generator/oracles/oracle_abc.py +0 -76
- classiq/interface/generator/oracles/oracle_function_param_list.py +0 -6
- classiq/interface/generator/piecewise_linear_amplitude_loading.py +0 -165
- classiq/interface/generator/qpe.py +0 -169
- classiq/interface/grover/grover_modelling_params.py +0 -13
- classiq/model_expansions/transformers/var_splitter.py +0 -224
- /classiq/{interface/grover → evaluators/qmod_type_inference}/__init__.py +0 -0
- {classiq-0.86.1.dist-info → classiq-0.87.0.dist-info}/WHEEL +0 -0
classiq/__init__.py
CHANGED
@@ -37,6 +37,7 @@ from classiq.applications.combinatorial_optimization import (
|
|
37
37
|
from classiq.applications.hamiltonian.pauli_decomposition import (
|
38
38
|
hamiltonian_to_matrix,
|
39
39
|
matrix_to_hamiltonian,
|
40
|
+
matrix_to_pauli_operator,
|
40
41
|
)
|
41
42
|
from classiq.execution import * # noqa: F403
|
42
43
|
from classiq.execution import __all__ as _execution_all
|
@@ -109,6 +110,7 @@ __all__ = (
|
|
109
110
|
"show",
|
110
111
|
"hamiltonian_to_matrix",
|
111
112
|
"matrix_to_hamiltonian",
|
113
|
+
"matrix_to_pauli_operator",
|
112
114
|
"quantum_program_from_qasm",
|
113
115
|
"quantum_program_from_qasm_async",
|
114
116
|
]
|
@@ -55,7 +55,11 @@ def _get_hf_qubit_op(
|
|
55
55
|
problem: FermionHamiltonianProblem, mapper: FermionToQubitMapper
|
56
56
|
) -> QubitOperator:
|
57
57
|
hf_fermion_op = get_hf_fermion_op(problem)
|
58
|
-
|
58
|
+
# In case of tapering: We need to taper the state, not the operator. This can be done by passing is_invariant=True
|
59
|
+
# (even though the operator is not necessarily invariant).
|
60
|
+
# Then, taper_off_qubits eliminates the qubits to taper off (up to a sign)
|
61
|
+
# for each term in the Hamiltonian.
|
62
|
+
return mapper.map(hf_fermion_op, is_invariant=True)
|
59
63
|
|
60
64
|
|
61
65
|
def _apply_term_on_zero_state(
|
@@ -27,7 +27,7 @@ def qubit_op_to_pauli_terms(
|
|
27
27
|
) -> SparsePauliOp:
|
28
28
|
n_qubits = _get_n_qubits(qubit_op, n_qubits)
|
29
29
|
return SparsePauliOp(
|
30
|
-
terms=[
|
30
|
+
terms=[
|
31
31
|
SparsePauliTerm(
|
32
32
|
paulis=[ # type:ignore[arg-type]
|
33
33
|
IndexedPauli(
|
@@ -40,7 +40,7 @@ def qubit_op_to_pauli_terms(
|
|
40
40
|
)
|
41
41
|
for term, coeff in qubit_op.terms.items()
|
42
42
|
],
|
43
|
-
num_qubits=n_qubits,
|
43
|
+
num_qubits=n_qubits,
|
44
44
|
)
|
45
45
|
|
46
46
|
|
@@ -3,7 +3,7 @@ from dataclasses import dataclass, field
|
|
3
3
|
from typing import Optional, Union
|
4
4
|
|
5
5
|
import pyomo.environ as pyo
|
6
|
-
from pyomo.core.base import
|
6
|
+
from pyomo.core.base.var import VarData
|
7
7
|
from pyomo.core.expr.visitor import clone_expression, identify_variables
|
8
8
|
|
9
9
|
from classiq.interface.combinatorial_optimization.encoding_types import EncodingType
|
@@ -14,9 +14,9 @@ from classiq.applications.combinatorial_helpers import pyomo_utils
|
|
14
14
|
|
15
15
|
@dataclass
|
16
16
|
class VarExpressionMapping:
|
17
|
-
var:
|
17
|
+
var: VarData
|
18
18
|
expr: pyo.Expression
|
19
|
-
encodings_vars: list[
|
19
|
+
encodings_vars: list[VarData] = field(default_factory=list)
|
20
20
|
|
21
21
|
|
22
22
|
class EncodingMapping:
|
@@ -25,11 +25,11 @@ class EncodingMapping:
|
|
25
25
|
self.encoding_type = encoding_type
|
26
26
|
|
27
27
|
@property
|
28
|
-
def original_vars(self) -> list[
|
28
|
+
def original_vars(self) -> list[VarData]:
|
29
29
|
return [pair.var for pair in self._data]
|
30
30
|
|
31
31
|
@property
|
32
|
-
def encodings_vars(self) -> list[
|
32
|
+
def encodings_vars(self) -> list[VarData]:
|
33
33
|
return list(
|
34
34
|
itertools.chain.from_iterable(
|
35
35
|
var_mapping.encodings_vars for var_mapping in self._data
|
@@ -45,9 +45,9 @@ class EncodingMapping:
|
|
45
45
|
|
46
46
|
def add(
|
47
47
|
self,
|
48
|
-
original_var:
|
48
|
+
original_var: VarData,
|
49
49
|
encoding_expr: pyo.Expression,
|
50
|
-
encodings_vars: Union[list[
|
50
|
+
encodings_vars: Union[list[VarData], None] = None,
|
51
51
|
) -> None:
|
52
52
|
if encodings_vars is None:
|
53
53
|
encodings_vars = list(identify_variables(encoding_expr))
|
@@ -59,25 +59,21 @@ class EncodingMapping:
|
|
59
59
|
)
|
60
60
|
)
|
61
61
|
|
62
|
-
def _check_unique_encoding_vars(self, variables: list[
|
62
|
+
def _check_unique_encoding_vars(self, variables: list[VarData]) -> None:
|
63
63
|
assert all(
|
64
64
|
not pyomo_utils.contains(var, self.encodings_vars) for var in variables
|
65
65
|
)
|
66
66
|
|
67
|
-
def get_var_expr_mapping(
|
68
|
-
self, original_var: _GeneralVarData
|
69
|
-
) -> VarExpressionMapping:
|
67
|
+
def get_var_expr_mapping(self, original_var: VarData) -> VarExpressionMapping:
|
70
68
|
for var_expr_mapping in self._data:
|
71
69
|
if var_expr_mapping.var is original_var:
|
72
70
|
return var_expr_mapping
|
73
71
|
raise ClassiqCombOptError("No variable expression mapping found.")
|
74
72
|
|
75
|
-
def get_encoding_vars(self, original_var:
|
73
|
+
def get_encoding_vars(self, original_var: VarData) -> list[VarData]:
|
76
74
|
return self.get_var_expr_mapping(original_var).encodings_vars
|
77
75
|
|
78
|
-
def get_original_var(
|
79
|
-
self, encoding_var: _GeneralVarData
|
80
|
-
) -> Optional[_GeneralVarData]:
|
76
|
+
def get_original_var(self, encoding_var: VarData) -> Optional[VarData]:
|
81
77
|
for original_var in self.original_vars:
|
82
78
|
if pyomo_utils.contains(encoding_var, self.get_encoding_vars(original_var)):
|
83
79
|
return original_var
|
@@ -4,14 +4,14 @@ from typing import Any, Union, cast
|
|
4
4
|
|
5
5
|
import numpy as np
|
6
6
|
import pyomo.environ as pyo
|
7
|
-
from pyomo.core.base import _GeneralVarData
|
8
7
|
from pyomo.core.base.component import ComponentData, _ComponentBase
|
9
8
|
from pyomo.core.base.constraint import _GeneralConstraintData
|
9
|
+
from pyomo.core.base.var import VarData
|
10
10
|
from pyomo.core.expr.numeric_expr import (
|
11
11
|
MonomialTermExpression,
|
12
12
|
ProductExpression,
|
13
|
-
clone_expression,
|
14
13
|
)
|
14
|
+
from pyomo.core.expr.visitor import clone_expression
|
15
15
|
from sympy import Expr
|
16
16
|
|
17
17
|
from classiq.interface.exceptions import ClassiqCombOptNoSolutionError
|
@@ -26,13 +26,13 @@ def is_model_encodable(model: pyo.ConcreteModel) -> bool:
|
|
26
26
|
return not all(is_var_binary(var) for var in variables)
|
27
27
|
|
28
28
|
|
29
|
-
def is_var_binary(var:
|
29
|
+
def is_var_binary(var: VarData) -> bool:
|
30
30
|
return var.domain == pyo.Binary or (
|
31
31
|
var.domain in _INTEGER_TYPES and var.lb == 0 and var.ub == 1
|
32
32
|
)
|
33
33
|
|
34
34
|
|
35
|
-
def is_var_span_power_of_2(var:
|
35
|
+
def is_var_span_power_of_2(var: VarData) -> bool:
|
36
36
|
var_span = get_var_span(var)
|
37
37
|
return math.log2(var_span + 1).is_integer()
|
38
38
|
|
@@ -46,7 +46,7 @@ def is_obj_encoded(var: _ComponentBase) -> bool:
|
|
46
46
|
return ENCODED_SUFFIX in var.name
|
47
47
|
|
48
48
|
|
49
|
-
def get_var_span(var:
|
49
|
+
def get_var_span(var: VarData) -> int:
|
50
50
|
return var.ub - var.lb
|
51
51
|
|
52
52
|
|
@@ -54,7 +54,7 @@ def encoded_obj_name(name: str) -> str:
|
|
54
54
|
return name + ENCODED_SUFFIX
|
55
55
|
|
56
56
|
|
57
|
-
def get_encoded_var_index(var:
|
57
|
+
def get_encoded_var_index(var: VarData) -> int:
|
58
58
|
indexed_var = var.parent_component()
|
59
59
|
index = [
|
60
60
|
index_temp for index_temp, var_temp in indexed_var.items() if var_temp is var
|
@@ -1,6 +1,6 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
-
from pyomo.core.base import
|
3
|
+
from pyomo.core.base.var import VarData
|
4
4
|
|
5
5
|
from classiq.interface.generator.function_params import IOName
|
6
6
|
|
@@ -19,13 +19,13 @@ class InternalQuantumReg:
|
|
19
19
|
class MemoryMapping:
|
20
20
|
def __init__(
|
21
21
|
self,
|
22
|
-
variables: list[
|
22
|
+
variables: list[VarData],
|
23
23
|
vars_encoding_mapping: EncodingMapping | None = None,
|
24
24
|
) -> None:
|
25
25
|
self.substitution_dict: dict[int, InternalQuantumReg] = dict()
|
26
26
|
self.qubit_allocation: dict[IOName, tuple[int, int]] = dict()
|
27
27
|
self.vars_encoding_mapping: EncodingMapping | None = vars_encoding_mapping
|
28
|
-
self.vars: list[
|
28
|
+
self.vars: list[VarData] = variables
|
29
29
|
self._allocate_memory()
|
30
30
|
|
31
31
|
def __len__(self) -> int:
|
@@ -65,7 +65,7 @@ class MemoryMapping:
|
|
65
65
|
)
|
66
66
|
|
67
67
|
|
68
|
-
def get_var_name(var:
|
68
|
+
def get_var_name(var: VarData) -> str:
|
69
69
|
return (
|
70
70
|
var.name.replace("[", "")
|
71
71
|
.replace("]", "")
|
@@ -6,8 +6,8 @@ from typing import Optional, Union
|
|
6
6
|
import pyomo.environ as pyo
|
7
7
|
import sympy
|
8
8
|
from pyomo.core import ConcreteModel
|
9
|
-
from pyomo.core.base import _GeneralVarData
|
10
9
|
from pyomo.core.base.constraint import _GeneralConstraintData
|
10
|
+
from pyomo.core.base.var import VarData
|
11
11
|
from pyomo.core.expr.sympy_tools import sympyify_expression
|
12
12
|
from pyomo.environ import Expression
|
13
13
|
|
@@ -92,15 +92,15 @@ class OptimizationModel:
|
|
92
92
|
self._initialize_objective_not_encoded(model_copy)
|
93
93
|
|
94
94
|
@property
|
95
|
-
def vars(self) -> list[
|
96
|
-
return pyomo_utils.extract(self._model,
|
95
|
+
def vars(self) -> list[VarData]:
|
96
|
+
return pyomo_utils.extract(self._model, VarData)
|
97
97
|
|
98
98
|
@property
|
99
|
-
def vars_not_encoded(self) -> list[
|
99
|
+
def vars_not_encoded(self) -> list[VarData]:
|
100
100
|
return list(filterfalse(encoding_utils.is_obj_encoded, self.vars))
|
101
101
|
|
102
102
|
@property
|
103
|
-
def _ising_vars(self) -> list[
|
103
|
+
def _ising_vars(self) -> list[VarData]:
|
104
104
|
if self.is_encoded:
|
105
105
|
return [
|
106
106
|
var
|
@@ -28,26 +28,22 @@ def pauli_operator_to_hamiltonian(pauli_list: PydanticPauliList) -> list[PauliTe
|
|
28
28
|
|
29
29
|
|
30
30
|
def pauli_operator_to_sparse_hamiltonian(
|
31
|
-
pauli_list:
|
31
|
+
pauli_list: list[PauliTerm],
|
32
32
|
) -> SparsePauliOp:
|
33
33
|
pauli_terms: list[SparsePauliTerm] = []
|
34
34
|
for pauli_term in pauli_list:
|
35
|
-
if not isinstance(pauli_term[1], complex) or pauli_term[1].imag != 0:
|
36
|
-
raise ClassiqNonNumericCoefficientInPauliError(
|
37
|
-
"Coefficient is not a number."
|
38
|
-
)
|
39
35
|
term = SparsePauliTerm(
|
40
36
|
paulis=[ # type:ignore[arg-type]
|
41
|
-
IndexedPauli(pauli=
|
42
|
-
for i, p in enumerate(pauli_term[
|
37
|
+
IndexedPauli(pauli=p, index=i) # type:ignore[arg-type]
|
38
|
+
for i, p in enumerate(pauli_term.pauli[::-1])
|
43
39
|
],
|
44
|
-
coefficient=pauli_term
|
40
|
+
coefficient=pauli_term.coefficient,
|
45
41
|
)
|
46
42
|
pauli_terms.append(term)
|
47
43
|
|
48
44
|
return SparsePauliOp(
|
49
|
-
terms=pauli_terms,
|
50
|
-
num_qubits=len(pauli_list[0]
|
45
|
+
terms=pauli_terms,
|
46
|
+
num_qubits=len(pauli_list[0].pauli), # type: ignore[arg-type]
|
51
47
|
)
|
52
48
|
|
53
49
|
|
@@ -15,11 +15,11 @@ import pyomo.core.expr.numeric_expr as pyo_expr
|
|
15
15
|
import pyomo.environ as pyo
|
16
16
|
import sympy
|
17
17
|
from pyomo.core import ConcreteModel, Constraint, Objective, Var, maximize
|
18
|
-
from pyomo.core.base import _GeneralVarData
|
19
18
|
from pyomo.core.base.component import ComponentData
|
20
19
|
from pyomo.core.base.constraint import _GeneralConstraintData
|
21
20
|
from pyomo.core.base.indexed_component import IndexedComponent
|
22
21
|
from pyomo.core.base.objective import ScalarObjective
|
22
|
+
from pyomo.core.base.var import VarData
|
23
23
|
from pyomo.core.expr.base import ExpressionBase
|
24
24
|
from pyomo.core.expr.sympy_tools import (
|
25
25
|
Pyomo2SympyVisitor,
|
@@ -35,7 +35,7 @@ from classiq.interface.generator.types.struct_declaration import StructDeclarati
|
|
35
35
|
from classiq.qmod.qmod_variable import QArray, QBit, QNum, QStruct, QVar
|
36
36
|
from classiq.qmod.symbolic_expr import SymbolicExpr
|
37
37
|
|
38
|
-
ListVars = list[
|
38
|
+
ListVars = list[VarData]
|
39
39
|
SUPPORTED_TYPES = [
|
40
40
|
pyo.Binary,
|
41
41
|
pyo.Integers,
|
@@ -65,12 +65,12 @@ class CombinatorialOptimizationStructDeclaration(StructDeclaration):
|
|
65
65
|
)
|
66
66
|
|
67
67
|
|
68
|
-
def contains(var_data:
|
68
|
+
def contains(var_data: VarData, vars_data: ListVars) -> bool:
|
69
69
|
# HACK: standard "__containts__ (in)" method doesn't work, because pyomo overrode the __eq__ method (IMO)
|
70
70
|
return any(var_data is var_data_temp for var_data_temp in vars_data)
|
71
71
|
|
72
72
|
|
73
|
-
def remove(var_data:
|
73
|
+
def remove(var_data: VarData, vars_data: ListVars) -> ListVars:
|
74
74
|
# HACK: standard "list method remove" method doesn't work, because pyomo overrode the __eq__ method (IMO)
|
75
75
|
assert contains(var_data, vars_data), "var not in list"
|
76
76
|
vars_data = vars_data.copy()
|
@@ -81,7 +81,7 @@ def remove(var_data: _GeneralVarData, vars_data: ListVars) -> ListVars:
|
|
81
81
|
return vars_data
|
82
82
|
|
83
83
|
|
84
|
-
def index(var_data:
|
84
|
+
def index(var_data: VarData, vars_data: ListVars) -> int:
|
85
85
|
# HACK: standard "index method" doesn't work.
|
86
86
|
assert contains(var_data, vars_data), "var not in list"
|
87
87
|
idxs = [
|
@@ -94,7 +94,7 @@ T = TypeVar("T")
|
|
94
94
|
|
95
95
|
|
96
96
|
def extract(model: ConcreteModel, type_: type[T]) -> list[T]:
|
97
|
-
if type_ ==
|
97
|
+
if type_ == VarData:
|
98
98
|
type_ = Var
|
99
99
|
|
100
100
|
elif type_ == _GeneralConstraintData:
|
@@ -267,15 +267,13 @@ def pyomo2qmod(
|
|
267
267
|
)
|
268
268
|
|
269
269
|
|
270
|
-
def pyomo_to_qmod_qstruct(
|
271
|
-
struct_name: str, vars: list[_GeneralVarData]
|
272
|
-
) -> type[QStruct]:
|
270
|
+
def pyomo_to_qmod_qstruct(struct_name: str, vars: list[VarData]) -> type[QStruct]:
|
273
271
|
qmod_struct = type(struct_name, (QStruct,), {})
|
274
272
|
qmod_struct.__annotations__ = _get_qstruct_fields(vars)
|
275
273
|
return qmod_struct
|
276
274
|
|
277
275
|
|
278
|
-
def _get_qstruct_fields(vars: list[
|
276
|
+
def _get_qstruct_fields(vars: list[VarData]) -> dict[str, type[QVar]]:
|
279
277
|
array_type_sizes = _get_array_sizes(vars)
|
280
278
|
fields: dict[str, type[QVar]] = {}
|
281
279
|
for var in vars:
|
@@ -283,7 +281,7 @@ def _get_qstruct_fields(vars: list[_GeneralVarData]) -> dict[str, type[QVar]]:
|
|
283
281
|
return fields
|
284
282
|
|
285
283
|
|
286
|
-
def _get_array_sizes(vars: list[
|
284
|
+
def _get_array_sizes(vars: list[VarData]) -> dict[str, tuple[int, ...]]:
|
287
285
|
array_types: dict[str, set[tuple]] = defaultdict(set)
|
288
286
|
for var in vars:
|
289
287
|
if is_index_var(var):
|
@@ -293,11 +291,13 @@ def _get_array_sizes(vars: list[_GeneralVarData]) -> dict[str, tuple[int, ...]]:
|
|
293
291
|
return {
|
294
292
|
name: dimensions
|
295
293
|
for name, indices in array_types.items()
|
296
|
-
if (dimensions := _get_indices_dimensions(indices)) is not None
|
294
|
+
if (dimensions := _get_indices_dimensions(indices, strict=False)) is not None
|
297
295
|
}
|
298
296
|
|
299
297
|
|
300
|
-
def _get_indices_dimensions(
|
298
|
+
def _get_indices_dimensions(
|
299
|
+
indices: set[tuple[int, ...]], *, strict: bool
|
300
|
+
) -> Optional[tuple[int, ...]]:
|
301
301
|
indices_list = list(indices)
|
302
302
|
if len(indices) == 0:
|
303
303
|
return None
|
@@ -313,16 +313,16 @@ def _get_indices_dimensions(indices: set[tuple[int, ...]]) -> Optional[tuple[int
|
|
313
313
|
min(dimension_bounds[dim_idx][0], idx),
|
314
314
|
max(dimension_bounds[dim_idx][1], idx),
|
315
315
|
)
|
316
|
-
if any(lb != 0 for lb, ub in dimension_bounds):
|
316
|
+
if strict and any(lb != 0 for lb, ub in dimension_bounds):
|
317
317
|
return None
|
318
318
|
dimensions = tuple(ub + 1 for _, ub in dimension_bounds)
|
319
|
-
if reduce(mul, dimensions) != len(indices_list):
|
319
|
+
if strict and reduce(mul, dimensions) != len(indices_list):
|
320
320
|
return None
|
321
321
|
return dimensions
|
322
322
|
|
323
323
|
|
324
324
|
def _add_qmod_field(
|
325
|
-
var:
|
325
|
+
var: VarData,
|
326
326
|
array_type_sizes: dict[str, tuple[int, ...]],
|
327
327
|
fields: dict[str, type[QVar]],
|
328
328
|
) -> None:
|
@@ -331,15 +331,16 @@ def _add_qmod_field(
|
|
331
331
|
var_name = get_field_name(var)
|
332
332
|
fields[var_name] = _get_qmod_field_type(var_name, var)
|
333
333
|
return
|
334
|
+
if parent_name in fields:
|
335
|
+
return
|
334
336
|
dimensions = array_type_sizes[parent_name]
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
fields[parent_name] = qmod_type
|
337
|
+
qmod_type: type[QVar] = _get_qmod_field_type(parent_name, var)
|
338
|
+
for dim in reversed(dimensions):
|
339
|
+
qmod_type = QArray[qmod_type, dim] # type:ignore[valid-type]
|
340
|
+
fields[parent_name] = qmod_type
|
340
341
|
|
341
342
|
|
342
|
-
def _get_qmod_field_type(var_name: str, var_data:
|
343
|
+
def _get_qmod_field_type(var_name: str, var_data: VarData) -> type[QVar]:
|
343
344
|
if var_data.domain not in SUPPORTED_TYPES:
|
344
345
|
raise ClassiqValueError(
|
345
346
|
f"Type {str(var_data.domain)!r} of variable {var_name!r} is not supported"
|
@@ -396,11 +397,11 @@ def _get_item(obj: Any, multi_index: tuple[int, ...]) -> Any:
|
|
396
397
|
return obj
|
397
398
|
|
398
399
|
|
399
|
-
def get_field_name(var:
|
400
|
+
def get_field_name(var: VarData) -> str:
|
400
401
|
return var.local_name.replace("[", "_").replace("]", "").replace(",", "_")
|
401
402
|
|
402
403
|
|
403
|
-
def is_index_var(var:
|
404
|
+
def is_index_var(var: VarData) -> bool:
|
404
405
|
index = var.index()
|
405
406
|
return isinstance(index, int) or (
|
406
407
|
isinstance(index, tuple) and all(isinstance(idx, int) for idx in index)
|
@@ -415,7 +416,7 @@ def index_as_tuple(index: Union[int, tuple[int, ...]]) -> tuple[int, ...]:
|
|
415
416
|
|
416
417
|
@contextmanager
|
417
418
|
def add_var_domain_constraints(model: ConcreteModel) -> Iterator[None]:
|
418
|
-
vars = extract(model,
|
419
|
+
vars = extract(model, VarData)
|
419
420
|
constraints = [
|
420
421
|
constraint
|
421
422
|
for var in vars
|
@@ -431,7 +432,7 @@ def add_var_domain_constraints(model: ConcreteModel) -> Iterator[None]:
|
|
431
432
|
model.del_component("var_domain_constraints")
|
432
433
|
|
433
434
|
|
434
|
-
def _get_var_domain_constraint(var:
|
435
|
+
def _get_var_domain_constraint(var: VarData) -> Optional[ExpressionBase]:
|
435
436
|
bounds = var.bounds
|
436
437
|
if (
|
437
438
|
type(bounds) is not tuple
|
@@ -1,12 +1,12 @@
|
|
1
1
|
from typing import Optional
|
2
2
|
|
3
3
|
import pyomo.core as pyo
|
4
|
-
from pyomo.core.base import
|
4
|
+
from pyomo.core.base.var import VarData
|
5
5
|
from pyomo.core.expr.sympy_tools import Pyomo2SympyVisitor, PyomoSympyBimap
|
6
6
|
from sympy import Expr
|
7
7
|
|
8
8
|
|
9
|
-
def sympyify_vars(variables: list[
|
9
|
+
def sympyify_vars(variables: list[VarData]) -> PyomoSympyBimap:
|
10
10
|
symbols_map = PyomoSympyBimap()
|
11
11
|
for var in variables:
|
12
12
|
Pyomo2SympyVisitor(symbols_map).walk_expression(var)
|
@@ -3,7 +3,7 @@ from itertools import chain, product
|
|
3
3
|
from typing import Callable, Union
|
4
4
|
|
5
5
|
import pyomo.environ as pyo
|
6
|
-
from pyomo.core.base import
|
6
|
+
from pyomo.core.base.var import VarData
|
7
7
|
from pyomo.core.expr.numeric_expr import ExpressionBase
|
8
8
|
from pyomo.core.expr.relational_expr import EqualityExpression
|
9
9
|
|
@@ -25,9 +25,7 @@ def _make_invalid_encoding_type_error(
|
|
25
25
|
)
|
26
26
|
|
27
27
|
|
28
|
-
def encoding_length(
|
29
|
-
var: _GeneralVarData, encoding_type: Union[EncodingType, None]
|
30
|
-
) -> int:
|
28
|
+
def encoding_length(var: VarData, encoding_type: Union[EncodingType, None]) -> int:
|
31
29
|
if encoding_type is None:
|
32
30
|
return 1
|
33
31
|
|
@@ -99,7 +97,7 @@ class ModelEncoder:
|
|
99
97
|
return vars_encoding_mapping
|
100
98
|
|
101
99
|
def _get_encoding_expr(
|
102
|
-
self, var_data:
|
100
|
+
self, var_data: VarData, encoding_vars: list[VarData]
|
103
101
|
) -> pyo.Expression:
|
104
102
|
if self.encoding_type == EncodingType.BINARY:
|
105
103
|
var_span = encoding_utils.get_var_span(var_data)
|
@@ -119,7 +117,7 @@ class ModelEncoder:
|
|
119
117
|
return encoding_expr
|
120
118
|
|
121
119
|
def _get_binary_coeffs(
|
122
|
-
self, encoding_vars: list[
|
120
|
+
self, encoding_vars: list[VarData], var_span: int
|
123
121
|
) -> list[int]:
|
124
122
|
num_vars = len(encoding_vars)
|
125
123
|
if self.qsolver == QSolver.QAOAMixer:
|
@@ -3,9 +3,9 @@ from typing import Union
|
|
3
3
|
|
4
4
|
from pyomo.core import ConcreteModel, Var
|
5
5
|
from pyomo.core.base.constraint import _GeneralConstraintData
|
6
|
+
from pyomo.core.base.var import VarData
|
6
7
|
from pyomo.core.expr.relational_expr import EqualityExpression
|
7
8
|
from pyomo.core.expr.visitor import identify_variables
|
8
|
-
from pyomo.repn.standard_repn import _GeneralVarData
|
9
9
|
|
10
10
|
from classiq.interface.exceptions import (
|
11
11
|
ClassiqCombOptNoSolutionError,
|
@@ -81,10 +81,10 @@ def _get_fixing_constraints(model: ConcreteModel) -> list[_GeneralConstraintData
|
|
81
81
|
|
82
82
|
def _get_var_and_value_from_fixing_constraint(
|
83
83
|
constraint: _GeneralConstraintData,
|
84
|
-
) -> tuple[
|
84
|
+
) -> tuple[VarData, float]:
|
85
85
|
var = next(identify_variables(constraint.body))
|
86
86
|
|
87
|
-
if isinstance(constraint.body,
|
87
|
+
if isinstance(constraint.body, VarData):
|
88
88
|
return var, constraint.upper.value
|
89
89
|
|
90
90
|
symbols_map = sympyify_vars([var])
|
@@ -125,7 +125,7 @@ def add_fixed_variables_to_solution(
|
|
125
125
|
return solution_with_fixed
|
126
126
|
|
127
127
|
|
128
|
-
def _get_value_if_exists(var:
|
128
|
+
def _get_value_if_exists(var: VarData) -> Union[int, VarData]:
|
129
129
|
return var.value if var.value is not None else var
|
130
130
|
|
131
131
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import pyomo
|
2
2
|
import sympy as sp
|
3
|
-
from pyomo.core.base import
|
3
|
+
from pyomo.core.base.var import VarData
|
4
4
|
|
5
5
|
from classiq.interface.chemistry.operator import PauliOperator
|
6
6
|
from classiq.interface.exceptions import ClassiqCombOptNotSupportedProblemError
|
@@ -18,7 +18,7 @@ PYOMO_PARSING_ERROR_MESAGE = "Parsing of this pyomo model is not supported."
|
|
18
18
|
|
19
19
|
def convert_pyomo_to_hamiltonian(
|
20
20
|
pyomo_expr: pyomo.core.Expression,
|
21
|
-
ordered_pyomo_vars: list[
|
21
|
+
ordered_pyomo_vars: list[VarData],
|
22
22
|
qregs: list[InternalQuantumReg],
|
23
23
|
) -> PauliOperator:
|
24
24
|
symbols_map = sympyify_vars(ordered_pyomo_vars)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
from pyomo.core import ConcreteModel
|
2
2
|
from pyomo.core.base.constraint import _GeneralConstraintData
|
3
|
+
from pyomo.core.base.var import VarData
|
3
4
|
from pyomo.core.expr.relational_expr import EqualityExpression
|
4
|
-
from pyomo.repn.standard_repn import _GeneralVarData
|
5
5
|
|
6
6
|
from classiq.applications.combinatorial_helpers import (
|
7
7
|
allowed_constraints,
|
@@ -12,7 +12,7 @@ from classiq.applications.combinatorial_helpers.sympy_utils import sympyify_expr
|
|
12
12
|
|
13
13
|
|
14
14
|
def is_model_penalty_supported(model: ConcreteModel) -> bool:
|
15
|
-
variables = extract(model,
|
15
|
+
variables = extract(model, VarData)
|
16
16
|
is_vars_supported = all(is_var_penalty_supported(var) for var in variables)
|
17
17
|
|
18
18
|
constraints = extract(model, _GeneralConstraintData)
|
@@ -22,7 +22,7 @@ def is_model_penalty_supported(model: ConcreteModel) -> bool:
|
|
22
22
|
return is_vars_supported and is_constraints_supported
|
23
23
|
|
24
24
|
|
25
|
-
def is_var_penalty_supported(var:
|
25
|
+
def is_var_penalty_supported(var: VarData) -> bool:
|
26
26
|
return encoding_utils.is_var_binary(var) or encoding_utils.is_var_span_power_of_2(
|
27
27
|
var
|
28
28
|
)
|
@@ -3,10 +3,14 @@ from typing import Union
|
|
3
3
|
import numpy as np
|
4
4
|
from sympy import fwht
|
5
5
|
|
6
|
+
from classiq.applications.combinatorial_helpers.pauli_helpers.pauli_utils import (
|
7
|
+
pauli_operator_to_sparse_hamiltonian,
|
8
|
+
)
|
6
9
|
from classiq.qmod import ( # type:ignore[attr-defined]
|
7
10
|
Pauli,
|
8
11
|
PauliTerm,
|
9
12
|
)
|
13
|
+
from classiq.qmod.builtins.structs import SparsePauliOp
|
10
14
|
|
11
15
|
ATOL = 1e-12
|
12
16
|
PAULI_MATRICES_DICT = {
|
@@ -93,6 +97,18 @@ def matrix_to_hamiltonian(
|
|
93
97
|
return hamiltonian
|
94
98
|
|
95
99
|
|
100
|
+
def matrix_to_pauli_operator(
|
101
|
+
mat: np.ndarray, tol: float = ATOL, is_hermitian: bool = True
|
102
|
+
) -> SparsePauliOp:
|
103
|
+
"""
|
104
|
+
The decomposition per set is done by the Walsh-Hadamard transform,
|
105
|
+
since the transformation between {e_0,e_3} ({e_1,e_2}) to {I,Z} ({X,iY}) is the Hadamard matrix.
|
106
|
+
"""
|
107
|
+
return pauli_operator_to_sparse_hamiltonian(
|
108
|
+
matrix_to_hamiltonian(mat, tol=tol, is_hermitian=is_hermitian)
|
109
|
+
)
|
110
|
+
|
111
|
+
|
96
112
|
# convert a single puali string of length N to 2**N X 2**N matrix
|
97
113
|
def pauli_string_to_mat(seq: list[Pauli]) -> np.ndarray:
|
98
114
|
real_matrix = PAULI_MATRICES_DICT[seq[0]]
|
@@ -101,8 +117,24 @@ def pauli_string_to_mat(seq: list[Pauli]) -> np.ndarray:
|
|
101
117
|
return real_matrix
|
102
118
|
|
103
119
|
|
120
|
+
def _sparse_pauli_to_list(operator: SparsePauliOp) -> list[PauliTerm]:
|
121
|
+
terms_list = []
|
122
|
+
for term in operator.terms:
|
123
|
+
pauli_list = [Pauli.I for i in range(operator.num_qubits)]
|
124
|
+
for p in term.paulis: # type:ignore[attr-defined]
|
125
|
+
pauli_list[p.index] = p.pauli
|
126
|
+
terms_list.append(
|
127
|
+
PauliTerm(coefficient=term.coefficient, pauli=list(reversed(pauli_list)))
|
128
|
+
)
|
129
|
+
return terms_list
|
130
|
+
|
131
|
+
|
104
132
|
# return matrix from hamiltonian
|
105
|
-
def hamiltonian_to_matrix(
|
133
|
+
def hamiltonian_to_matrix(
|
134
|
+
hamiltonian: Union[list[PauliTerm], SparsePauliOp],
|
135
|
+
) -> np.ndarray:
|
136
|
+
if isinstance(hamiltonian, SparsePauliOp):
|
137
|
+
hamiltonian = _sparse_pauli_to_list(hamiltonian)
|
106
138
|
matrix = np.zeros(
|
107
139
|
[2 ** len(hamiltonian[0].pauli), 2 ** len(hamiltonian[0].pauli)],
|
108
140
|
dtype=np.complex_,
|