Skip to content
This repository has been archived by the owner on Dec 7, 2021. It is now read-only.

Add feasibility checks to optimizers/converters #1199

Merged
merged 45 commits into from
Sep 11, 2020
Merged
Show file tree
Hide file tree
Changes from 40 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
f283cd2
Draft for issue#1134
KahanMajmudar Aug 13, 2020
ab2c833
Resolved merge conflict by incorporating new changes
KahanMajmudar Aug 13, 2020
97bddff
Merge branch 'master' into issue1134
KahanMajmudar Aug 13, 2020
96e3622
resolved issue in quadratic_problem.py
KahanMajmudar Aug 13, 2020
1eab962
incorporated changes as suggested by @adekusar-drl
KahanMajmudar Aug 14, 2020
a5cab4a
Merge branch 'master' into issue1134
manoelmarques Aug 14, 2020
408a54a
Merge branch 'master' into issue1134
KahanMajmudar Aug 15, 2020
558228a
Merge branch 'master' of https://github.com/Qiskit/qiskit-aqua into i…
KahanMajmudar Aug 15, 2020
56a29c5
Merge branch 'issue1134' of https://github.com/KahanMajmudar/qiskit-a…
KahanMajmudar Aug 15, 2020
bd9ab0f
Merge branch 'master' into issue1134
t-imamichi Aug 18, 2020
4058d56
Merge branch 'master' into issue1134
t-imamichi Aug 19, 2020
c5b41cf
Merge branch 'master' of https://github.com/Qiskit/qiskit-aqua into i…
KahanMajmudar Aug 19, 2020
ce6008f
Merge branch 'issue1134' of https://github.com/KahanMajmudar/qiskit-a…
KahanMajmudar Aug 19, 2020
791838e
fix linting issues
KahanMajmudar Aug 19, 2020
c4f2f0f
Merge branch 'master' into issue1134
t-imamichi Aug 20, 2020
1ae4f8f
Merge branch 'master' into issue1134
KahanMajmudar Aug 20, 2020
6cc3f65
added is_feasible method in all optimizers, added changes suggested b…
KahanMajmudar Aug 20, 2020
dca6d88
Merge branch 'issue1134' of https://github.com/KahanMajmudar/qiskit-a…
KahanMajmudar Aug 20, 2020
721dffe
removed cast import
KahanMajmudar Aug 20, 2020
80cc949
Merge branch 'master' into issue1134
manoelmarques Aug 21, 2020
9e0834a
Merge branch 'master' into issue1134
manoelmarques Aug 21, 2020
ef020a5
Merge branch 'master' into issue1134
KahanMajmudar Aug 21, 2020
b137bf2
Merge branch 'master' into issue1134
KahanMajmudar Aug 21, 2020
5a7f8e5
Merge branch 'master' into issue1134
t-imamichi Aug 26, 2020
d9282e0
Merge branch 'master' into issue1134
KahanMajmudar Aug 26, 2020
130b7d6
Merge branch 'issue1134' of https://github.com/KahanMajmudar/qiskit-a…
KahanMajmudar Aug 26, 2020
218d2f5
Merge branch 'master' into issue1134
t-imamichi Aug 31, 2020
77e7279
Merge branch 'master' into issue1134
manoelmarques Sep 2, 2020
eec2538
Merge branch 'master' into issue1134
t-imamichi Sep 2, 2020
538a13f
made changes as per points 1 to 3
KahanMajmudar Sep 2, 2020
4425e97
Merge branch 'master' into issue1134
manoelmarques Sep 2, 2020
5ec3a6d
made suggested changes by @adekusar-drl
KahanMajmudar Sep 3, 2020
038009b
Merge branch 'issue1134' of https://github.com/KahanMajmudar/qiskit-a…
KahanMajmudar Sep 3, 2020
3ad20b8
Merge branch 'master' into issue1134
adekusar-drl Sep 6, 2020
8d6f784
Merge branch 'master' into issue1134
t-imamichi Sep 9, 2020
274f9bc
some cleaning
adekusar-drl Sep 9, 2020
fbaecaa
Merge branch 'master' into issue1134
adekusar-drl Sep 9, 2020
d58f934
added reno
adekusar-drl Sep 10, 2020
e73c091
Merge branch 'issue1134' of https://github.com/KahanMajmudar/qiskit-a…
adekusar-drl Sep 10, 2020
818cadd
fixed reno
adekusar-drl Sep 10, 2020
8a0a10e
Update releasenotes/notes/feasibility-check-b99605f771e745b7.yaml
adekusar-drl Sep 10, 2020
286d68d
Update releasenotes/notes/feasibility-check-b99605f771e745b7.yaml
adekusar-drl Sep 10, 2020
718ad72
Merge branch 'master' of https://github.com/Qiskit/qiskit-aqua into i…
adekusar-drl Sep 10, 2020
cb56d8c
code review
adekusar-drl Sep 10, 2020
dfe4537
Merge branch 'master' into issue1134
woodsp-ibm Sep 10, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions qiskit/optimization/algorithms/admm_optimizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
from qiskit.aqua.algorithms import NumPyMinimumEigensolver

from .minimum_eigen_optimizer import MinimumEigenOptimizer
from .optimization_algorithm import OptimizationAlgorithm, OptimizationResult
from .optimization_algorithm import (OptimizationResultStatus, OptimizationAlgorithm,
OptimizationResult)
from .slsqp_optimizer import SlsqpOptimizer
from ..problems.constraint import Constraint
from ..problems.linear_constraint import LinearConstraint
Expand Down Expand Up @@ -180,15 +181,16 @@ class ADMMOptimizationResult(OptimizationResult):
""" ADMMOptimization Result."""

def __init__(self, x: np.ndarray, fval: float, variables: List[Variable],
state: ADMMState) -> None:
state: ADMMState, status: OptimizationResultStatus) -> None:
"""
Args:
x: the optimal value found by ADMM.
fval: the optimal function value.
variables: the list of variables of the optimization problem.
state: the internal computation state of ADMM.
status: Termination status of an optimization algorithm
"""
super().__init__(x=x, fval=fval, variables=variables, raw_results=state)
super().__init__(x=x, fval=fval, variables=variables, status=status, raw_results=state)

@property
def state(self) -> ADMMState:
Expand Down Expand Up @@ -375,13 +377,15 @@ def solve(self, problem: QuadraticProgram) -> ADMMOptimizationResult:
objective_value = objective_value * sense

# convert back integer to binary
base_result = OptimizationResult(solution, objective_value, original_variables)
base_result = OptimizationResult(solution, objective_value, original_variables,
OptimizationResultStatus.SUCCESS)
base_result = int2bin.interpret(base_result)

# third parameter is our internal state of computations.
result = ADMMOptimizationResult(x=base_result.x, fval=base_result.fval,
variables=base_result.variables,
state=self._state)
state=self._state,
status=self._get_feasibility_status(problem, base_result.x))

# debug
self._log.debug("solution=%s, objective=%s at iteration=%s",
Expand Down
4 changes: 2 additions & 2 deletions qiskit/optimization/algorithms/cplex_optimizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,9 @@ def solve(self, problem: QuadraticProgram) -> OptimizationResult:
sol = cplex.solution

# create results
result = OptimizationResult(x=sol.get_values(),
fval=sol.get_objective_value(),
result = OptimizationResult(x=sol.get_values(), fval=sol.get_objective_value(),
variables=problem.variables,
status=self._get_feasibility_status(problem, sol.get_values()),
raw_results=sol)

# return solution
Expand Down
17 changes: 11 additions & 6 deletions qiskit/optimization/algorithms/grover_optimizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@
from qiskit.aqua.algorithms.amplitude_amplifiers.grover import Grover
from qiskit.aqua.components.initial_states import Custom
from qiskit.aqua.components.oracles import CustomCircuitOracle
from qiskit.circuit.library import QuadraticForm
from qiskit.providers import BaseBackend
from .optimization_algorithm import OptimizationAlgorithm, OptimizationResult
from qiskit.circuit.library import QuadraticForm
from .optimization_algorithm import (OptimizationResultStatus, OptimizationAlgorithm,
OptimizationResult)
from ..converters.quadratic_program_to_qubo import QuadraticProgramToQubo
from ..problems import Variable
from ..problems.quadratic_program import QuadraticProgram
Expand Down Expand Up @@ -261,15 +262,17 @@ def solve(self, problem: QuadraticProgram) -> OptimizationResult:

# Compute function value
fval = problem_init.objective.evaluate(opt_x)
result = OptimizationResult(x=opt_x, fval=fval, variables=problem_.variables)
result = OptimizationResult(x=opt_x, fval=fval, variables=problem_.variables,
status=OptimizationResultStatus.SUCCESS)

# cast binaries back to integers
result = self._qubo_converter.interpret(result)

return GroverOptimizationResult(x=result.x, fval=result.fval, variables=result.variables,
operation_counts=operation_count, n_input_qubits=n_key,
n_output_qubits=n_value, intermediate_fval=fval,
threshold=threshold)
threshold=threshold,
status=self._get_feasibility_status(problem, result.x))

def _measure(self, circuit: QuantumCircuit) -> str:
"""Get probabilities from the given backend, and picks a random outcome."""
Expand Down Expand Up @@ -333,7 +336,8 @@ class GroverOptimizationResult(OptimizationResult):

def __init__(self, x: Union[List[float], np.ndarray], fval: float, variables: List[Variable],
operation_counts: Dict[int, Dict[str, int]], n_input_qubits: int,
n_output_qubits: int, intermediate_fval: float, threshold: float) -> None:
n_output_qubits: int, intermediate_fval: float, threshold: float,
status: OptimizationResultStatus) -> None:
"""
Constructs a result object with the specific Grover properties.

Expand All @@ -347,8 +351,9 @@ def __init__(self, x: Union[List[float], np.ndarray], fval: float, variables: Li
intermediate_fval: The intermediate value of the objective function of the solution,
that is expected to be identical with ``fval``.
threshold: The threshold of Grover algorithm.
status: the termination status of the optimization algorithm.
"""
super().__init__(x, fval, variables, None)
super().__init__(x, fval, variables, status, None)
self._operation_counts = operation_counts
self._n_input_qubits = n_input_qubits
self._n_output_qubits = n_output_qubits
Expand Down
20 changes: 12 additions & 8 deletions qiskit/optimization/algorithms/minimum_eigen_optimizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,28 @@
from qiskit.aqua.algorithms import MinimumEigensolver, MinimumEigensolverResult
from qiskit.aqua.operators import StateFn, DictStateFn

from .optimization_algorithm import OptimizationAlgorithm, OptimizationResult
from .optimization_algorithm import (OptimizationResultStatus, OptimizationAlgorithm,
OptimizationResult)
from ..converters.quadratic_program_to_qubo import QuadraticProgramToQubo
from ..problems.quadratic_program import QuadraticProgram, Variable


class MinimumEigenOptimizationResult(OptimizationResult):
""" Minimum Eigen Optimizer Result."""

def __init__(self, x: Union[List[float], np.ndarray], fval: float,
variables: List[Variable],
samples: List[Tuple[str, float, float]],
def __init__(self, x: Union[List[float], np.ndarray], fval: float, variables: List[Variable],
status: OptimizationResultStatus, samples: List[Tuple[str, float, float]],
min_eigen_solver_result: Optional[MinimumEigensolverResult] = None) -> None:
"""
Args:
x: the optimal value found by ``MinimumEigensolver``.
fval: the optimal function value.
variables: the list of variables of the optimization problem.
status: the termination status of the optimization algorithm.
samples: the basis state as bitstring, the QUBO value, and the probability of sampling.
min_eigen_solver_result: the result obtained from the underlying algorithm.
"""
super().__init__(x, fval, variables, None)
super().__init__(x, fval, variables, status, None)
self._samples = samples
self._min_eigen_solver_result = min_eigen_solver_result

Expand Down Expand Up @@ -178,12 +179,15 @@ def solve(self, problem: QuadraticProgram) -> MinimumEigenOptimizationResult:
samples = [(x_str, offset, 1.0)]

# translate result back to integers
result = OptimizationResult(x=x, fval=fval, variables=problem_.variables)
result = OptimizationResult(x=x, fval=fval, variables=problem_.variables,
status=OptimizationResultStatus.SUCCESS)
result = self._qubo_converter.interpret(result)

return MinimumEigenOptimizationResult(x=result.x, fval=result.fval,
variables=result.variables,
samples=samples,
min_eigen_solver_result=eigen_result)
status=self._get_feasibility_status(problem,
result.x),
samples=samples, min_eigen_solver_result=eigen_result)


def _eigenvector_to_solutions(eigenvector: Union[dict, np.ndarray, StateFn],
Expand Down
4 changes: 2 additions & 2 deletions qiskit/optimization/algorithms/multistart_optimizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@
from scipy.stats import uniform

from qiskit.optimization import QuadraticProgram, INFINITY
from qiskit.optimization.algorithms.optimization_algorithm import (OptimizationAlgorithm,
OptimizationResult)
from .optimization_algorithm import OptimizationAlgorithm, OptimizationResult

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -93,6 +92,7 @@ def multi_start_solve(self, minimize: Callable[[np.array], Tuple[np.array, Any]]
rest_sol = rest

return OptimizationResult(x=x_sol, fval=fval_sol, variables=problem.variables,
status=self._get_feasibility_status(problem, x_sol),
raw_results=rest_sol)

@property
Expand Down
38 changes: 27 additions & 11 deletions qiskit/optimization/algorithms/optimization_algorithm.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,19 @@
from ..problems.quadratic_program import QuadraticProgram, Variable


class OptimizationResultStatus(Enum):
"""Termination status of an optimization algorithm."""

SUCCESS = 0
"""the optimization algorithm succeeded to find an optimal solution."""

FAILURE = 1
woodsp-ibm marked this conversation as resolved.
Show resolved Hide resolved
"""the optimization algorithm ended in a failure."""

INFEASIBLE = 2
"""the optimization algorithm obtained an infeasible solution."""


class OptimizationAlgorithm(ABC):
"""An abstract class for optimization algorithms in Qiskit's optimization module."""

Expand Down Expand Up @@ -84,18 +97,21 @@ def _verify_compatibility(self, problem: QuadraticProgram) -> None:
if msg:
raise QiskitOptimizationError('Incompatible problem: {}'.format(msg))

def _get_feasibility_status(self, problem: QuadraticProgram,
x: Union[List[float], np.ndarray]) -> OptimizationResultStatus:
"""Returns whether the input result is feasible or not for the given problem.

class OptimizationResultStatus(Enum):
"""Termination status of an optimization algorithm."""

SUCCESS = 0
"""the optimization algorithm succeeded to find an optimal solution."""
Args:
problem: Problem to verify.
x: the input result list.

FAILURE = 1
"""the optimization algorithm ended in a failure."""
Returns:
The status of the result.
"""
is_feasible = problem.is_feasible(x)

INFEASIBLE = 2
"""the optimization algorithm obtained an infeasible solution."""
return OptimizationResultStatus.SUCCESS if is_feasible \
else OptimizationResultStatus.INFEASIBLE


class OptimizationResult:
Expand Down Expand Up @@ -140,8 +156,8 @@ class OptimizationResult:

def __init__(self, x: Union[List[float], np.ndarray], fval: float,
variables: List[Variable],
raw_results: Optional[Any] = None,
status: OptimizationResultStatus = OptimizationResultStatus.SUCCESS) -> None:
status: OptimizationResultStatus,
woodsp-ibm marked this conversation as resolved.
Show resolved Hide resolved
raw_results: Optional[Any] = None) -> None:
"""
Args:
x: the optimal value found in the optimization.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
from qiskit.aqua.algorithms import NumPyMinimumEigensolver
from qiskit.aqua.utils.validation import validate_min

from .optimization_algorithm import OptimizationAlgorithm, OptimizationResult
from .optimization_algorithm import (OptimizationResultStatus, OptimizationAlgorithm,
OptimizationResult)
from .minimum_eigen_optimizer import MinimumEigenOptimizer, MinimumEigenOptimizationResult
from ..converters.quadratic_program_to_qubo import QuadraticProgramToQubo
from ..exceptions import QiskitOptimizationError
Expand Down Expand Up @@ -50,9 +51,8 @@ class IntermediateResult(Enum):

class RecursiveMinimumEigenOptimizationResult(OptimizationResult):
"""Recursive Eigen Optimizer Result."""
def __init__(self, x: Union[List[float], np.ndarray], fval: float,
variables: List[Variable],
replacements: Dict[str, Tuple[str, int]],
def __init__(self, x: Union[List[float], np.ndarray], fval: float, variables: List[Variable],
status: OptimizationResultStatus, replacements: Dict[str, Tuple[str, int]],
history: Tuple[List[MinimumEigenOptimizationResult], OptimizationResult]) -> None:
"""
Constructs an instance of the result class.
Expand All @@ -61,6 +61,7 @@ def __init__(self, x: Union[List[float], np.ndarray], fval: float,
x: the optimal value found in the optimization.
fval: the optimal function value.
variables: the list of variables of the optimization problem.
status: the termination status of the optimization algorithm.
replacements: a dictionary of substituted variables. Key is a variable being
substituted, value is a tuple of substituting variable and a weight, either 1 or -1.
history: a tuple containing intermediate results. The first element is a list of
Expand All @@ -70,7 +71,7 @@ def __init__(self, x: Union[List[float], np.ndarray], fval: float,
:class:`~qiskit.optimization.algorithm.OptimizationResult` obtained at the last step
via `min_num_vars_optimizer`.
"""
super().__init__(x, fval, variables, None)
super().__init__(x, fval, variables, status, None)
self._replacements = replacements
self._history = history

Expand Down Expand Up @@ -281,13 +282,16 @@ def find_value(x, replacements, var_values):
# construct result
x_v = [var_values[x_aux.name] for x_aux in problem_ref.variables]
fval = result.fval
result = OptimizationResult(x=x_v, fval=fval, variables=problem_ref.variables)
result = OptimizationResult(x=x_v, fval=fval, variables=problem_ref.variables,
status=OptimizationResultStatus.SUCCESS)
result = self._qubo_converter.interpret(result)

return RecursiveMinimumEigenOptimizationResult(x=result.x, fval=result.fval,
variables=result.variables,
replacements=replacements,
history=history)
history=history,
status=(self._get_feasibility_status
(problem, result.x)))

def _find_strongest_correlation(self, correlations):

Expand Down
16 changes: 10 additions & 6 deletions qiskit/optimization/algorithms/slsqp_optimizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from scipy.optimize import fmin_slsqp

from .multistart_optimizer import MultiStartOptimizer
from .optimization_algorithm import OptimizationResult
from .optimization_algorithm import OptimizationResultStatus, OptimizationResult
from ..exceptions import QiskitOptimizationError
from ..problems import Variable
from ..problems.constraint import Constraint
Expand All @@ -32,8 +32,9 @@ class SlsqpOptimizationResult(OptimizationResult):
SLSQP optimization result, defines additional properties that may be returned by the optimizer.
"""
def __init__(self, x: Union[List[float], np.ndarray], fval: float, variables: List[Variable],
fx: Optional[np.ndarray] = None, its: Optional[int] = None,
imode: Optional[int] = None, smode: Optional[str] = None) -> None:
status: OptimizationResultStatus, fx: Optional[np.ndarray] = None,
its: Optional[int] = None, imode: Optional[int] = None,
smode: Optional[str] = None) -> None:
"""
Constructs a result object with properties specific to SLSQP.

Expand All @@ -46,8 +47,9 @@ def __init__(self, x: Union[List[float], np.ndarray], fval: float, variables: Li
imode: The exit mode from the optimizer
(see the documentation of ``scipy.optimize.fmin_slsqp``).
smode: Message describing the exit mode from the optimizer.
status: the termination status of the optimization algorithm.
"""
super().__init__(x, fval, variables, None)
super().__init__(x, fval, variables, status, None)
self._fx = fx
self._its = its
self._imode = imode
Expand Down Expand Up @@ -217,8 +219,10 @@ def _minimize(x_0: np.array) -> Tuple[np.array, Any]:
result = self.multi_start_solve(_minimize, problem)

if self._full_output:
return SlsqpOptimizationResult(result.x, result.fval, result.variables,
return SlsqpOptimizationResult(x=result.x, fval=result.fval, variables=result.variables,
status=self._get_feasibility_status(problem, result.x),
fx=result.raw_results[0], its=result.raw_results[1],
imode=result.raw_results[2], smode=result.raw_results[3])
else:
return SlsqpOptimizationResult(result.x, result.fval, result.variables)
return SlsqpOptimizationResult(x=result.x, fval=result.fval, variables=result.variables,
status=self._get_feasibility_status(problem, result.x))
2 changes: 1 addition & 1 deletion qiskit/optimization/converters/inequality_to_equality.py
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,7 @@ def interpret(self, result: OptimizationResult) -> OptimizationResult:
names = [x.name for x in self._dst.variables]
new_x = self._interpret_var(names, result.x)
return OptimizationResult(x=new_x, fval=result.fval, variables=self._src.variables,
raw_results=result.raw_results, status=result.status)
status=result.status, raw_results=result.raw_results)

def _interpret_var(self, names, vals) -> List[int]:
# interpret slack variables
Expand Down
2 changes: 1 addition & 1 deletion qiskit/optimization/converters/integer_to_binary.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ def interpret(self, result: OptimizationResult) -> OptimizationResult:
"""
new_x = self._interpret_var(result.x)
return OptimizationResult(x=new_x, fval=result.fval, variables=self._src.variables,
raw_results=result.raw_results)
status=result.status, raw_results=result.raw_results)

def _interpret_var(self, vals: Union[List[float], np.ndarray]) -> List[float]:
# interpret integer values
Expand Down
4 changes: 2 additions & 2 deletions qiskit/optimization/converters/linear_equality_to_penalty.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,8 +190,8 @@ def interpret(self, result: OptimizationResult) -> OptimizationResult:
new_status = OptimizationResultStatus.INFEASIBLE

return OptimizationResult(x=result.x, fval=substituted_qp.objective.constant,
variables=self._src.variables, raw_results=result.raw_results,
status=new_status)
variables=self._src.variables, status=new_status,
raw_results=result.raw_results)

@property
def penalty(self) -> Optional[float]:
Expand Down
Loading