# This code is part of a Qiskit project.
#
# (C) Copyright IBM 2021, 2023.
#
# 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
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.
"""Converters to flip problem sense, e.g. maximization to minimization and vice versa."""
import copy
from typing import Optional, List, Union
import numpy as np
from .quadratic_program_converter import QuadraticProgramConverter
from ..exceptions import QiskitOptimizationError
from ..problems.quadratic_objective import ObjSense
from ..problems.quadratic_program import QuadraticProgram
class _FlipProblemSense(QuadraticProgramConverter):
"""Flip the sense of a problem, e.g. converts from maximization to minimization and
vice versa, regardless of the current sense."""
def __init__(self) -> None:
self._src_num_vars: Optional[int] = None
def convert(self, problem: QuadraticProgram) -> QuadraticProgram:
"""Flip the sense of a problem.
Args:
problem: The problem to be flipped.
Returns:
A converted problem, that has the flipped sense.
"""
# copy original number of variables as reference.
self._src_num_vars = problem.get_num_vars()
desired_sense = self._get_desired_sense(problem)
# flip the problem sense
if problem.objective.sense != desired_sense:
desired_problem = copy.deepcopy(problem)
desired_problem.objective.sense = desired_sense
desired_problem.objective.constant = (-1) * problem.objective.constant
desired_problem.objective.linear = (-1) * problem.objective.linear.coefficients
desired_problem.objective.quadratic = (-1) * problem.objective.quadratic.coefficients
else:
desired_problem = problem
return desired_problem
def _get_desired_sense(self, problem: QuadraticProgram) -> ObjSense:
"""
Computes a desired sense of the problem. By default, flip the sense.
Args:
problem: a problem to check
Returns:
A desired sense, if the problem was a minimization problem, then the sense is
maximization and vice versa.
"""
if problem.objective.sense == ObjSense.MAXIMIZE:
return ObjSense.MINIMIZE
else:
return ObjSense.MAXIMIZE
def interpret(self, x: Union[np.ndarray, List[float]]) -> np.ndarray:
"""Convert the result of the converted problem back to that of the original problem.
Args:
x: The result of the converted problem or the given result in case of FAILURE.
Returns:
The result of the original problem.
Raises:
QiskitOptimizationError: if the number of variables in the result differs from
that of the original problem.
"""
if len(x) != self._src_num_vars:
raise QiskitOptimizationError(
f"The number of variables in the passed result differs from "
f"that of the original problem, should be {self._src_num_vars}, but got {len(x)}."
)
return np.asarray(x)
[docs]class MaximizeToMinimize(_FlipProblemSense):
"""Convert a maximization problem to a minimization problem only if it is a maximization
problem, otherwise problem's sense is unchanged."""
def _get_desired_sense(self, problem: QuadraticProgram) -> ObjSense:
return ObjSense.MINIMIZE
[docs]class MinimizeToMaximize(_FlipProblemSense):
"""Convert a minimization problem to a maximization problem only if it is a minimization
problem, otherwise problem's sense is unchanged."""
def _get_desired_sense(self, problem: QuadraticProgram) -> ObjSense:
return ObjSense.MAXIMIZE