• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

qiskit-community / qiskit-optimization / 16851316047

09 Aug 2025 04:17PM UTC coverage: 91.98% (-0.2%) from 92.15%
16851316047

Pull #672

github

web-flow
Merge 6a1508a8c into c0dfe67d9
Pull Request #672: (WIP) Add V2 Primitives Support and Qiskit 2.0

203 of 226 new or added lines in 25 files covered. (89.82%)

49 existing lines in 8 files now uncovered.

5987 of 6509 relevant lines covered (91.98%)

0.92 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.83
/qiskit_optimization/algorithms/minimum_eigen_optimizer.py
1
# This code is part of a Qiskit project.
2
#
3
# (C) Copyright IBM 2020, 2025.
4
#
5
# This code is licensed under the Apache License, Version 2.0. You may
6
# obtain a copy of this license in the LICENSE.txt file in the root directory
7
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
8
#
9
# Any modifications or derivative works of this code must retain this
10
# copyright notice, and modified files need to carry a notice indicating
11
# that they have been altered from the originals.
12

13
"""A wrapper for minimum eigen solvers to be used within the optimization module."""
14
from typing import List, Optional, Union, cast
1✔
15

16
import numpy as np
1✔
17
from qiskit.quantum_info import SparsePauliOp
1✔
18

19
from ..converters.quadratic_program_to_qubo import QuadraticProgramConverter, QuadraticProgramToQubo
1✔
20
from ..exceptions import QiskitOptimizationError
1✔
21
from ..minimum_eigensolvers import (
1✔
22
    NumPyMinimumEigensolver,
23
    NumPyMinimumEigensolverResult,
24
)
25
from ..minimum_eigensolvers.sampling_mes import (
1✔
26
    SamplingMinimumEigensolver,
27
    SamplingMinimumEigensolverResult,
28
)
29
from ..problems.quadratic_program import QuadraticProgram, Variable
1✔
30
from .optimization_algorithm import (
1✔
31
    OptimizationAlgorithm,
32
    OptimizationResult,
33
    OptimizationResultStatus,
34
    SolutionSample,
35
)
36

37
MinimumEigensolver = Union[SamplingMinimumEigensolver, NumPyMinimumEigensolver]
1✔
38
MinimumEigensolverResult = Union[SamplingMinimumEigensolverResult, NumPyMinimumEigensolverResult]
1✔
39

40

41
class MinimumEigenOptimizationResult(OptimizationResult):
1✔
42
    """Minimum Eigen Optimizer Result."""
43

44
    def __init__(  # pylint: disable=too-many-positional-arguments
1✔
45
        self,
46
        x: Optional[Union[List[float], np.ndarray]],
47
        fval: Optional[float],
48
        variables: List[Variable],
49
        status: OptimizationResultStatus,
50
        samples: Optional[List[SolutionSample]] = None,
51
        min_eigen_solver_result: Optional[MinimumEigensolverResult] = None,
52
        raw_samples: Optional[List[SolutionSample]] = None,
53
    ) -> None:
54
        """
55
        Args:
56
            x: the optimal value found by ``SamplingMinimumEigensolver`` or ``NumPyMinimumEigensolver``.
57
            fval: the optimal function value.
58
            variables: the list of variables of the optimization problem.
59
            status: the termination status of the optimization algorithm.
60
            min_eigen_solver_result: the result obtained from the underlying algorithm.
61
            samples: the x values, the objective function value of the original problem,
62
                the probability, and the status of sampling.
63
            raw_samples: the x values of the QUBO, the objective function value of the QUBO,
64
                and the probability of sampling.
65
        """
66
        super().__init__(
1✔
67
            x=x,
68
            fval=fval,
69
            variables=variables,
70
            status=status,
71
            raw_results=None,
72
            samples=samples,
73
        )
74
        self._min_eigen_solver_result = min_eigen_solver_result
1✔
75
        self._raw_samples = raw_samples
1✔
76

77
    @property
1✔
78
    def min_eigen_solver_result(self) -> MinimumEigensolverResult:
1✔
79
        """Returns a result object obtained from the instance of
80
        ``SamplingMinimumEigensolver`` or ``NumPyMinimumEigensolver``."""
81
        return self._min_eigen_solver_result
1✔
82

83
    @property
1✔
84
    def raw_samples(self) -> Optional[List[SolutionSample]]:
1✔
85
        """Returns the list of raw solution samples of
86
        ``SamplingMinimumEigensolver`` or ``NumPyMinimumEigensolver``.
87

88
        Returns:
89
            The list of raw solution samples of
90
            ``SamplingMinimumEigensolver`` or ``NumPyMinimumEigensolver``.
91
        """
92
        return self._raw_samples
1✔
93

94

95
class MinimumEigenOptimizer(OptimizationAlgorithm):
1✔
96
    """A wrapper for minimum eigen solvers.
97

98
    This class provides a wrapper for minimum eigen solvers from Qiskit to be used within
99
    the optimization module.
100
    It assumes a problem consisting only of binary or integer variables as well as linear equality
101
    constraints thereof. It converts such a problem into a Quadratic Unconstrained Binary
102
    Optimization (QUBO) problem by expanding integer variables into binary variables and by adding
103
    the linear equality constraints as weighted penalty terms to the objective function. The
104
    resulting QUBO is then translated into an Ising Hamiltonian whose minimal eigen vector and
105
    corresponding eigenstate correspond to the optimal solution of the original optimization
106
    problem. The provided minimum eigen solver is then used to approximate the ground state of the
107
    Hamiltonian to find a good solution for the optimization problem.
108

109
    Examples:
110
        Outline of how to use this class:
111

112
    .. code-block::
113

114
        from qiskit_optimization.minimum_eigensolvers import QAOA
115
        from qiskit_optimization.problems import QuadraticProgram
116
        from qiskit_optimization.algorithms import MinimumEigenOptimizer
117
        problem = QuadraticProgram()
118
        # specify problem here
119
        # specify minimum eigen solver to be used, e.g., QAOA
120
        qaoa = QAOA(...)
121
        optimizer = MinimumEigenOptimizer(qaoa)
122
        result = optimizer.solve(problem)
123
    """
124

125
    def __init__(
1✔
126
        self,
127
        min_eigen_solver: MinimumEigensolver,
128
        penalty: Optional[float] = None,
129
        converters: Optional[
130
            Union[QuadraticProgramConverter, List[QuadraticProgramConverter]]
131
        ] = None,
132
    ) -> None:
133
        """
134
        This initializer takes the minimum eigen solver to be used to approximate the ground state
135
        of the resulting Hamiltonian as well as a optional penalty factor to scale penalty terms
136
        representing linear equality constraints. If no penalty factor is provided, a default
137
        is computed during the algorithm (TODO).
138

139
        Args:
140
            min_eigen_solver: The eigen solver to find the ground state of the Hamiltonian.
141
            penalty: The penalty factor to be used, or ``None`` for applying a default logic.
142
            converters: The converters to use for converting a problem into a different form.
143
                By default, when None is specified, an internally created instance of
144
                :class:`~qiskit_optimization.converters.QuadraticProgramToQubo` will be used.
145

146
        Raises:
147
            TypeError: If minimum eigensolver has an invalid type.
148
            TypeError: When one of converters has an invalid type.
149
            QiskitOptimizationError: When the minimum eigensolver does not return an eigenstate.
150
        """
151
        if not min_eigen_solver.supports_aux_operators():
1✔
UNCOV
152
            raise QiskitOptimizationError(
×
153
                "Given MinimumEigensolver does not return the eigenstate "
154
                "and is not supported by the MinimumEigenOptimizer."
155
            )
156
        self._min_eigen_solver = min_eigen_solver
1✔
157
        self._penalty = penalty
1✔
158

159
        self._converters = self._prepare_converters(converters, penalty)
1✔
160

161
    def get_compatibility_msg(self, problem: QuadraticProgram) -> str:
1✔
162
        """Checks whether a given problem can be solved with this optimizer.
163

164
        Checks whether the given problem is compatible, i.e., whether the problem can be converted
165
        to a QUBO, and otherwise, returns a message explaining the incompatibility.
166

167
        Args:
168
            problem: The optimization problem to check compatibility.
169

170
        Returns:
171
            A message describing the incompatibility.
172
        """
173
        return QuadraticProgramToQubo.get_compatibility_msg(problem)
1✔
174

175
    @property
1✔
176
    def min_eigen_solver(self) -> MinimumEigensolver:
1✔
177
        """Returns the minimum eigensolver."""
UNCOV
178
        return self._min_eigen_solver
×
179

180
    @min_eigen_solver.setter
1✔
181
    def min_eigen_solver(self, min_eigen_solver: MinimumEigensolver) -> None:
1✔
182
        """Sets the minimum eigensolver."""
UNCOV
183
        self._min_eigen_solver = min_eigen_solver
×
184

185
    def solve(self, problem: QuadraticProgram) -> MinimumEigenOptimizationResult:
1✔
186
        """Tries to solves the given problem using the optimizer.
187

188
        Runs the optimizer to try to solve the optimization problem.
189

190
        Args:
191
            problem: The problem to be solved.
192

193
        Returns:
194
            The result of the optimizer applied to the problem.
195

196
        Raises:
197
            QiskitOptimizationError: If problem not compatible.
198
        """
199
        self._verify_compatibility(problem)
1✔
200

201
        # convert problem to QUBO minimization problem
202
        problem_ = self._convert(problem, self._converters)
1✔
203

204
        # construct operator and offset
205
        operator, offset = problem_.to_ising()
1✔
206

207
        return self._solve_internal(operator, offset, problem_, problem)
1✔
208

209
    def _solve_internal(
1✔
210
        self,
211
        operator: SparsePauliOp,
212
        offset: float,
213
        converted_problem: QuadraticProgram,
214
        original_problem: QuadraticProgram,
215
    ):
216
        # only try to solve non-empty Ising Hamiltonians
217
        eigen_result: Optional[MinimumEigensolverResult] = None
1✔
218
        if operator.num_qubits > 0:
1✔
219
            # approximate ground state of operator using min eigen solver
220
            eigen_result = self._min_eigen_solver.compute_minimum_eigenvalue(operator)
1✔
221
            # analyze results
222
            raw_samples = None
1✔
223
            if not hasattr(eigen_result, "eigenstate"):
1✔
224
                raise QiskitOptimizationError(
1✔
225
                    "MinimumEigenOptimizer does not support this minimum eigensolver "
226
                    f"{type(self._min_eigen_solver)}. "
227
                    "You can use qiskit_optimization.minimum_eigensolvers."
228
                    "SamplingMinimumEigensolver instead."
229
                )
230
            if eigen_result.eigenstate is not None:
1✔
231
                raw_samples = self._eigenvector_to_solutions(
1✔
232
                    eigen_result.eigenstate, converted_problem
233
                )
234
                raw_samples.sort(key=lambda x: x.fval)
1✔
235
        else:
236
            # if Hamiltonian is empty, then the objective function is constant to the offset
237
            x = np.zeros(converted_problem.get_num_binary_vars())
×
UNCOV
238
            fval = offset
×
UNCOV
239
            raw_samples = [SolutionSample(x, fval, 1.0, OptimizationResultStatus.SUCCESS)]
×
240

241
        if raw_samples is None:
1✔
242
            # if not function value is given, then something went wrong, e.g., a
243
            # NumPyMinimumEigensolver has been configured with an infeasible filter criterion.
244
            return MinimumEigenOptimizationResult(
1✔
245
                x=None,
246
                fval=None,
247
                variables=original_problem.variables,
248
                status=OptimizationResultStatus.FAILURE,
249
                samples=None,
250
                raw_samples=None,
251
                min_eigen_solver_result=eigen_result,
252
            )
253

254
        # translate result back to integers and eventually maximization
255
        samples, best_raw = self._interpret_samples(original_problem, raw_samples, self._converters)
1✔
256
        return cast(
1✔
257
            MinimumEigenOptimizationResult,
258
            self._interpret(
259
                x=best_raw.x,
260
                converters=self._converters,
261
                problem=original_problem,
262
                result_class=MinimumEigenOptimizationResult,
263
                samples=samples,
264
                raw_samples=raw_samples,
265
                min_eigen_solver_result=eigen_result,
266
            ),
267
        )
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc