Skip to content

Commit

Permalink
let MinimumEigenOptimizer allow new primitive-based algorithms
Browse files Browse the repository at this point in the history
  • Loading branch information
t-imamichi committed Nov 11, 2022
1 parent 3dfa511 commit 62334fc
Show file tree
Hide file tree
Showing 4 changed files with 497 additions and 75 deletions.
50 changes: 37 additions & 13 deletions qiskit_optimization/algorithms/minimum_eigen_optimizer.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2020, 2021.
# (C) Copyright IBM 2020, 2022.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
Expand All @@ -11,24 +11,38 @@
# that they have been altered from the originals.

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

import numpy as np
from qiskit.algorithms.minimum_eigen_solvers import MinimumEigensolver as LegacyMinimumEigensolver
from qiskit.algorithms.minimum_eigen_solvers import (
MinimumEigensolverResult as LegacyMinimumEigensolverResult,
)
from qiskit.algorithms.minimum_eigensolvers import (
NumPyMinimumEigensolver,
NumPyMinimumEigensolverResult,
SamplingMinimumEigensolver,
SamplingMinimumEigensolverResult,
)
from qiskit.opflow import OperatorBase, PauliOp, PauliSumOp

from qiskit.algorithms import MinimumEigensolver, MinimumEigensolverResult
from qiskit.opflow import OperatorBase
from ..converters.quadratic_program_to_qubo import QuadraticProgramConverter, QuadraticProgramToQubo
from ..deprecation import DeprecatedType, warn_deprecated
from ..exceptions import QiskitOptimizationError
from ..problems.quadratic_program import QuadraticProgram, Variable
from .optimization_algorithm import (
OptimizationResultStatus,
OptimizationAlgorithm,
OptimizationResult,
OptimizationResultStatus,
SolutionSample,
)
from ..exceptions import QiskitOptimizationError
from ..converters.quadratic_program_to_qubo import (
QuadraticProgramToQubo,
QuadraticProgramConverter,
)
from ..problems.quadratic_program import QuadraticProgram, Variable

MinimumEigensolver = Union[
SamplingMinimumEigensolver, NumPyMinimumEigensolver, LegacyMinimumEigensolver
]
MinimumEigensolverResult = Union[
SamplingMinimumEigensolverResult, NumPyMinimumEigensolverResult, LegacyMinimumEigensolverResult
]


class MinimumEigenOptimizationResult(OptimizationResult):
Expand Down Expand Up @@ -137,11 +151,18 @@ def __init__(
TypeError: When one of converters has an invalid type.
QiskitOptimizationError: When the minimum eigensolver does not return an eigenstate.
"""

if isinstance(min_eigen_solver, LegacyMinimumEigensolver):
warn_deprecated(
"0.5.0",
DeprecatedType.ARGUMENT,
f"min_eigen_solver as {LegacyMinimumEigensolver.__name__}",
new_name=f"min_eigen_solver as {SamplingMinimumEigensolver.__name__} "
f"or {NumPyMinimumEigensolver.__name__}",
)
if not min_eigen_solver.supports_aux_operators():
raise QiskitOptimizationError(
"Given MinimumEigensolver does not return the eigenstate "
+ "and is not supported by the MinimumEigenOptimizer."
"and is not supported by the MinimumEigenOptimizer."
)
self._min_eigen_solver = min_eigen_solver
self._penalty = penalty
Expand Down Expand Up @@ -206,6 +227,9 @@ def _solve_internal(
# only try to solve non-empty Ising Hamiltonians
eigen_result: Optional[MinimumEigensolverResult] = None
if operator.num_qubits > 0:
# NumPyEigensolver does not accept PauliOp but PauliSumOp
if isinstance(operator, PauliOp):
operator = PauliSumOp.from_list([(operator.primitive.to_label(), operator.coeff)])
# approximate ground state of operator using min eigen solver
eigen_result = self._min_eigen_solver.compute_minimum_eigenvalue(operator)
# analyze results
Expand Down
37 changes: 29 additions & 8 deletions qiskit_optimization/algorithms/optimization_algorithm.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,16 @@
from abc import ABC, abstractmethod
from dataclasses import dataclass
from enum import Enum
from typing import List, Union, Any, Optional, Dict, Type, Tuple, cast
from typing import Any, Dict, List, Optional, Tuple, Type, Union, cast
from warnings import warn

import numpy as np
from qiskit.opflow import DictStateFn, StateFn
from qiskit.quantum_info import Statevector
from qiskit.result import QuasiDistribution

from qiskit.opflow import StateFn, DictStateFn
from ..converters.quadratic_program_to_qubo import QuadraticProgramConverter, QuadraticProgramToQubo
from ..exceptions import QiskitOptimizationError
from ..converters.quadratic_program_to_qubo import QuadraticProgramToQubo, QuadraticProgramConverter
from ..problems.quadratic_program import QuadraticProgram, Variable


Expand Down Expand Up @@ -518,7 +520,7 @@ def _interpret_samples(

@staticmethod
def _eigenvector_to_solutions(
eigenvector: Union[dict, np.ndarray, StateFn],
eigenvector: Union[QuasiDistribution, Statevector, dict, np.ndarray, StateFn],
qubo: QuadraticProgram,
min_probability: float = 1e-6,
) -> List[SolutionSample]:
Expand Down Expand Up @@ -566,7 +568,25 @@ def generate_solution(bitstr, qubo, probability):
)

solutions = []
if isinstance(eigenvector, dict):
if isinstance(eigenvector, QuasiDistribution):
probabilities = eigenvector.binary_probabilities()
# iterate over all samples
for bitstr, sampling_probability in probabilities.items():
# add the bitstring, if the sampling probability exceeds the threshold
if sampling_probability >= min_probability:
solutions.append(generate_solution(bitstr, qubo, sampling_probability))

elif isinstance(eigenvector, Statevector):
probabilities = eigenvector.probabilities()
num_qubits = eigenvector.num_qubits
# iterate over all states and their sampling probabilities
for i, sampling_probability in enumerate(probabilities):
# add the i-th state if the sampling probability exceeds the threshold
if sampling_probability >= min_probability:
bitstr = f"{i:b}".rjust(num_qubits, "0")
solutions.append(generate_solution(bitstr, qubo, sampling_probability))

elif isinstance(eigenvector, dict):
# When eigenvector is a dict, square the values since the values are normalized.
# See https://github.com/Qiskit/qiskit-terra/pull/5496 for more details.
probabilities = {bitstr: val**2 for (bitstr, val) in eigenvector.items()}
Expand All @@ -579,7 +599,6 @@ def generate_solution(bitstr, qubo, probability):
elif isinstance(eigenvector, np.ndarray):
num_qubits = int(np.log2(eigenvector.size))
probabilities = np.abs(eigenvector * eigenvector.conj())

# iterate over all states and their sampling probabilities
for i, sampling_probability in enumerate(probabilities):
# add the i-th state if the sampling probability exceeds the threshold
Expand All @@ -588,6 +607,8 @@ def generate_solution(bitstr, qubo, probability):
solutions.append(generate_solution(bitstr, qubo, sampling_probability))

else:
raise TypeError("Unsupported format of eigenvector. Provide a dict or numpy.ndarray.")

raise TypeError(
f"Eigenvector should be QuasiDistribution, Statevector, dict or numpy.ndarray. "
f"But, it was {type(eigenvector)}."
)
return solutions
Loading

0 comments on commit 62334fc

Please sign in to comment.