Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
ccbe011
Add MaximizeToMinimize conv with UnitTest case
Apr 23, 2021
133a1d3
Make all algorithms use MaximizeToMinimize
Apr 23, 2021
38b5936
Add reno release note for feature
Apr 29, 2021
15a8faf
Fix inline imports of MaximizeToMinimize
May 1, 2021
3b5542c
Fix MultistartAlgorithm solve logic
May 1, 2021
f3a6fc5
Remove logger in maximize_to_minimize
May 1, 2021
4e265da
Extend MaximizeToMinimize test case
May 1, 2021
18724f6
Merge 'main' into maxmin-conv
May 7, 2021
18d2b53
Apply make black
May 7, 2021
36daf0c
Merge branch 'main' into maxmin-conv
t-imamichi May 12, 2021
b24478d
Make Max2Min conv efficient
May 12, 2021
245a955
Let algorithms use _convert
May 12, 2021
cb9c7c6
Not append max2min in _prepare_converters
May 12, 2021
ec9f7e3
Merge branch 'main' into maxmin-conv
May 12, 2021
0068a30
Merge branch 'main' into maxmin-conv
May 16, 2021
012fe39
Fix docstring
May 16, 2021
a4a8a7a
Remove blank lines
May 16, 2021
a23e0ef
Merge branch 'main' into maxmin-conv
t-imamichi May 18, 2021
d38d641
Merge branch 'main' into maxmin-conv
tomtuamnuq May 20, 2021
1c0953f
Remove unnecessary function declarations
May 20, 2021
ecee542
Merge branch 'main' into maxmin-conv
t-imamichi May 21, 2021
232d8b9
Merge branch 'main' into maxmin-conv
t-imamichi May 21, 2021
3fb2469
Update test/algorithms/test_warm_start_qaoa.py
adekusar-drl May 21, 2021
8fd318c
Update test/algorithms/test_warm_start_qaoa.py
t-imamichi May 21, 2021
3911d11
Update test/algorithms/test_warm_start_qaoa.py
t-imamichi May 21, 2021
969ec92
Merge branch 'main' into maxmin-conv
t-imamichi May 21, 2021
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
46 changes: 7 additions & 39 deletions qiskit_optimization/algorithms/admm_optimizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@
from .slsqp_optimizer import SlsqpOptimizer
from ..problems.constraint import Constraint
from ..problems.linear_constraint import LinearConstraint
from ..problems.quadratic_objective import QuadraticObjective
from ..problems.quadratic_program import QuadraticProgram
from ..problems.variable import VarType, Variable
from ..converters import MaximizeToMinimize

UPDATE_RHO_BY_TEN_PERCENT = 0
UPDATE_RHO_BY_RESIDUALS = 1
Expand Down Expand Up @@ -300,12 +300,10 @@ def solve(self, problem: QuadraticProgram) -> ADMMOptimizationResult:
# map integer variables to binary variables
from ..converters.integer_to_binary import IntegerToBinary

int2bin = IntegerToBinary()
original_problem = problem
problem = int2bin.convert(problem)

# we deal with minimization in the optimizer, so turn the problem to minimization
problem, sense = self._turn_to_minimization(problem)
converters = [IntegerToBinary(), MaximizeToMinimize()]
original_problem = problem
problem = self._convert(problem, converters)

# create our computation state.
self._state = ADMMState(problem, self._params.rho_initial)
Expand Down Expand Up @@ -394,53 +392,23 @@ def solve(self, problem: QuadraticProgram) -> ADMMOptimizationResult:
binary_vars, continuous_vars, objective_value = self._get_best_merit_solution()
solution = self._revert_solution_indexes(binary_vars, continuous_vars)

# flip the objective sign again if required
objective_value = objective_value * sense

self._log.debug(
"solution=%s, objective=%s at iteration=%s",
solution,
objective_value,
iteration,
"solution=%s, objective=%s at iteration=%s", solution, objective_value, iteration
)

# convert back integer to binary
# convert back integer to binary and eventually minimization to maximization
# `state` is our internal state of computations.
return cast(
ADMMOptimizationResult,
self._interpret(
x=solution,
converters=int2bin,
converters=converters,
problem=original_problem,
result_class=ADMMOptimizationResult,
state=self._state,
),
)

@staticmethod
def _turn_to_minimization(
problem: QuadraticProgram,
) -> Tuple[QuadraticProgram, float]:
"""
Turns the problem to `ObjSense.MINIMIZE` by flipping the sign of the objective function
if initially it is `ObjSense.MAXIMIZE`. Otherwise returns the original problem.

Args:
problem: a problem to turn to minimization.

Returns:
A copy of the problem if sign flip is required, otherwise the original problem and
the original sense of the problem in the numerical representation.
"""
sense = problem.objective.sense.value
if problem.objective.sense == QuadraticObjective.Sense.MAXIMIZE:
problem = copy.deepcopy(problem)
problem.objective.sense = QuadraticObjective.Sense.MINIMIZE
problem.objective.constant = (-1) * problem.objective.constant
problem.objective.linear = (-1) * problem.objective.linear.coefficients
problem.objective.quadratic = (-1) * problem.objective.quadratic.coefficients
return problem, sense

@staticmethod
def _get_variable_indices(op: QuadraticProgram, var_type: VarType) -> List[int]:
"""Returns a list of indices of the variables of the specified type.
Expand Down
16 changes: 11 additions & 5 deletions qiskit_optimization/algorithms/cobyla_optimizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from ..infinity import INFINITY
from ..problems.constraint import Constraint
from ..problems.quadratic_program import QuadraticProgram
from ..converters import MaximizeToMinimize


class CobylaOptimizer(MultiStartOptimizer):
Expand Down Expand Up @@ -117,9 +118,10 @@ def solve(self, problem: QuadraticProgram) -> OptimizationResult:
"""
self._verify_compatibility(problem)

# construct quadratic objective function
def objective(x):
return problem.objective.sense.value * problem.objective.evaluate(x)
# we deal with minimization in the optimizer, so turn the problem to minimization
max2min = MaximizeToMinimize()
original_problem = problem
problem = self._convert(problem, max2min)

# initialize constraints list
constraints = []
Expand Down Expand Up @@ -164,7 +166,7 @@ def ub_constraint(x, u_b=upperbound, j=i):
# actual minimization function to be called by multi_start_solve
def _minimize(x_0: np.ndarray) -> Tuple[np.ndarray, Any]:
x = fmin_cobyla(
objective,
problem.objective.evaluate,
x_0,
constraints,
rhobeg=self._rhobeg,
Expand All @@ -175,4 +177,8 @@ def _minimize(x_0: np.ndarray) -> Tuple[np.ndarray, Any]:
)
return x, None

return self.multi_start_solve(_minimize, problem)
result = self.multi_start_solve(_minimize, problem)
# eventually convert back minimization to maximization
return self._interpret(
x=result.x, problem=original_problem, converters=max2min, raw_results=result.raw_results
)
26 changes: 9 additions & 17 deletions qiskit_optimization/algorithms/grover_optimizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,19 +172,11 @@ def solve(self, problem: QuadraticProgram) -> OptimizationResult:

self._verify_compatibility(problem)

# convert problem to QUBO
# convert problem to minimization QUBO problem
problem_ = self._convert(problem, self._converters)
problem_init = deepcopy(problem_)

# convert to minimization problem
if problem_.objective.sense == problem_.objective.Sense.MAXIMIZE:
problem_.objective.sense = problem_.objective.Sense.MINIMIZE
problem_.objective.constant = -problem_.objective.constant
for i, val in problem_.objective.linear.to_dict().items():
problem_.objective.linear[i] = -val
for (i, j), val in problem_.objective.quadratic.to_dict().items():
problem_.objective.quadratic[i, j] = -val
self._num_key_qubits = len(problem_.objective.linear.to_array())
self._num_key_qubits = len(problem_.objective.linear.to_array()) # type: ignore

# Variables for tracking the optimum.
optimum_found = False
Expand Down Expand Up @@ -268,7 +260,7 @@ def solve(self, problem: QuadraticProgram) -> OptimizationResult:
raw_samples = self._eigenvector_to_solutions(
self._circuit_results, problem_init
)
raw_samples.sort(key=lambda x: problem_.objective.sense.value * x.fval)
raw_samples.sort(key=lambda x: x.fval)
samples = self._interpret_samples(problem, raw_samples, self._converters)
else:
# Using Durr and Hoyer method, increase m.
Expand Down Expand Up @@ -299,10 +291,10 @@ def solve(self, problem: QuadraticProgram) -> OptimizationResult:
optimum_key = 0

opt_x = np.array([1 if s == "1" else 0 for s in ("{0:%sb}" % n_key).format(optimum_key)])
# Compute function value
# Compute function value of minimization QUBO
fval = problem_init.objective.evaluate(opt_x)

# cast binaries back to integers
# cast binaries back to integers and eventually minimization to maximization
return cast(
GroverOptimizationResult,
self._interpret(
Expand Down Expand Up @@ -386,14 +378,14 @@ def __init__(
operation_counts: The counts of each operation performed per iteration.
n_input_qubits: The number of qubits used to represent the input.
n_output_qubits: The number of qubits used to represent the output.
intermediate_fval: The intermediate value of the objective function of the solution,
that is expected to be identical with ``fval``.
intermediate_fval: The intermediate value of the objective function of the
minimization qubo solution, that is expected to be consistent to ``fval``.
threshold: The threshold of Grover algorithm.
status: the termination status of the optimization algorithm.
samples: the x values, the objective function value of the original problem,
the probability, and the status of sampling.
raw_samples: the x values of the QUBO, the objective function value of the QUBO,
and the probability of sampling.
raw_samples: the x values of the QUBO, the objective function value of the
minimization QUBO, and the probability of sampling.
"""
super().__init__(
x=x,
Expand Down
7 changes: 4 additions & 3 deletions qiskit_optimization/algorithms/minimum_eigen_optimizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ def solve(self, problem: QuadraticProgram) -> MinimumEigenOptimizationResult:
"""
self._verify_compatibility(problem)

# convert problem to QUBO
# convert problem to QUBO minimization problem
problem_ = self._convert(problem, self._converters)

# construct operator and offset
Expand Down Expand Up @@ -219,7 +219,7 @@ def _solve_internal(
raw_samples = self._eigenvector_to_solutions(
eigen_result.eigenstate, converted_problem
)
raw_samples.sort(key=lambda x: converted_problem.objective.sense.value * x.fval)
raw_samples.sort(key=lambda x: x.fval)
x = raw_samples[0].x
fval = raw_samples[0].fval

Expand All @@ -241,7 +241,8 @@ def _solve_internal(
raw_samples=None,
min_eigen_solver_result=eigen_result,
)
# translate result back to integers

# translate result back to integers and eventually maximization
samples = self._interpret_samples(original_problem, raw_samples, self._converters)
return cast(
MinimumEigenOptimizationResult,
Expand Down
24 changes: 12 additions & 12 deletions qiskit_optimization/algorithms/multistart_optimizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,15 @@
import logging
import time
from abc import ABC
from typing import Optional, Callable, Tuple, Any
from typing import Callable, Tuple, Any, Optional

import numpy as np
from scipy.stats import uniform

from ..problems.quadratic_program import QuadraticProgram
from ..infinity import INFINITY
from .optimization_algorithm import OptimizationAlgorithm, OptimizationResult
from ..converters import MaximizeToMinimize

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -73,6 +74,11 @@ def multi_start_solve(
x_sol: Optional[np.ndarray] = None
rest_sol: Optional[Tuple] = None

# we deal with minimization in the optimizer, so turn the problem to minimization
max2min = MaximizeToMinimize()
original_problem = problem
problem = self._convert(problem, max2min)

# Implementation of multi-start optimizer
for trial in range(self._trials):
x_0 = np.zeros(problem.get_num_vars())
Expand All @@ -86,21 +92,15 @@ def multi_start_solve(
x, rest = minimize(x_0)
logger.debug("minimize done in: %s seconds", str(time.time() - t_0))

# we minimize, to get actual objective value we must multiply by the sense value
fval = problem.objective.evaluate(x) * problem.objective.sense.value
fval = problem.objective.evaluate(x)
# we minimize the objective
if fval < fval_sol:
# here we get back to the original sense of the problem
fval_sol = fval * problem.objective.sense.value
fval_sol = fval
x_sol = x
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,
# eventually convert back minimization to maximization
return self._interpret(
x_sol, problem=original_problem, converters=max2min, raw_results=rest_sol
)

@property
Expand Down
5 changes: 1 addition & 4 deletions qiskit_optimization/algorithms/optimization_algorithm.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,7 @@

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


Expand Down
20 changes: 11 additions & 9 deletions qiskit_optimization/algorithms/slsqp_optimizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from ..problems import Variable
from ..problems.constraint import Constraint
from ..problems.quadratic_program import QuadraticProgram
from ..converters import MaximizeToMinimize

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -183,13 +184,10 @@ def solve(self, problem: QuadraticProgram) -> OptimizationResult:
QiskitOptimizationError: If the problem is incompatible with the optimizer.
"""
self._verify_compatibility(problem)

# construct quadratic objective function
def _objective(x):
return problem.objective.sense.value * problem.objective.evaluate(x)

def _objective_gradient(x):
return problem.objective.sense.value * problem.objective.evaluate_gradient(x)
# we deal with minimization in the optimizer, so turn the problem to minimization
max2min = MaximizeToMinimize()
original_problem = problem
problem = self._convert(problem, max2min)

# initialize constraints and bounds
slsqp_bounds = []
Expand Down Expand Up @@ -222,12 +220,12 @@ def _objective_gradient(x):
# actual minimization function to be called by multi_start_solve
def _minimize(x_0: np.ndarray) -> Tuple[np.ndarray, Any]:
output = fmin_slsqp(
_objective,
problem.objective.evaluate,
x_0,
eqcons=slsqp_eq_constraints,
ieqcons=slsqp_ineq_constraints,
bounds=slsqp_bounds,
fprime=_objective_gradient,
fprime=problem.objective.evaluate_gradient,
iter=self._iter,
acc=self._acc,
iprint=self._iprint,
Expand All @@ -241,6 +239,10 @@ def _minimize(x_0: np.ndarray) -> Tuple[np.ndarray, Any]:

# actual optimization goes here
result = self.multi_start_solve(_minimize, problem)
# eventually convert back minimization to maximization
result = self._interpret(
x=result.x, problem=original_problem, converters=max2min, raw_results=result.raw_results
)

if self._full_output:
return SlsqpOptimizationResult(
Expand Down
4 changes: 2 additions & 2 deletions qiskit_optimization/algorithms/warm_start_qaoa_optimizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ def solve(self, problem: QuadraticProgram) -> MinimumEigenOptimizationResult:
if len(message) > 0:
raise QiskitOptimizationError(f"Incompatible problem: {message}")

# convert problem to QUBO or another form if converters are specified
# convert problem to minimization QUBO or another form if converters are specified
converted_problem = self._convert(problem, self._converters)

# if the pre-solver can't solve the problem then it should be relaxed.
Expand Down Expand Up @@ -336,7 +336,7 @@ def solve(self, problem: QuadraticProgram) -> MinimumEigenOptimizationResult:
return results[0]
else:
samples = self._aggregator.aggregate(results)
samples.sort(key=lambda sample: converted_problem.objective.sense.value * sample.fval)
samples.sort(key=lambda sample: problem.objective.sense.value * sample.fval)

# translate result back to the original variables
return cast(
Expand Down
3 changes: 3 additions & 0 deletions qiskit_optimization/converters/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,20 +40,23 @@
InequalityToEquality
IntegerToBinary
LinearEqualityToPenalty
MaximizeToMinimize
QuadraticProgramToQubo

"""

from .integer_to_binary import IntegerToBinary
from .inequality_to_equality import InequalityToEquality
from .linear_equality_to_penalty import LinearEqualityToPenalty
from .maximize_to_minimize import MaximizeToMinimize
from .quadratic_program_to_qubo import QuadraticProgramToQubo
from .quadratic_program_converter import QuadraticProgramConverter

__all__ = [
"InequalityToEquality",
"IntegerToBinary",
"LinearEqualityToPenalty",
"MaximizeToMinimize",
"QuadraticProgramConverter",
"QuadraticProgramToQubo",
]
Loading