Source code for jmetal.core.problem
import random
from abc import ABC, abstractmethod
from typing import Generic, List, TypeVar
from jmetal.core.observer import Observer
from jmetal.core.solution import (
    BinarySolution,
    FloatSolution,
    IntegerSolution,
    PermutationSolution,
)
from jmetal.logger import get_logger
logger = get_logger(__name__)
S = TypeVar("S")
[docs]
class Problem(Generic[S], ABC):
    """Class representing problems."""
    MINIMIZE = -1
    MAXIMIZE = 1
    def __init__(self):
        self.reference_front: List[S] = []
        self.directions: List[int] = []
        self.labels: List[str] = []
[docs]
    @abstractmethod
    def number_of_variables(self) -> int:
        pass 
[docs]
    @abstractmethod
    def number_of_objectives(self) -> int:
        pass 
[docs]
    @abstractmethod
    def number_of_constraints(self) -> int:
        pass 
[docs]
    @abstractmethod
    def create_solution(self) -> S:
        """Creates a random_search solution to the problem.
        :return: Solution."""
        pass 
[docs]
    @abstractmethod
    def evaluate(self, solution: S) -> S:
        """Evaluate a solution. For any new problem inheriting from :class:`Problem`, this method should be replaced.
        Note that this framework ASSUMES minimization, thus solutions must be evaluated in consequence.
        :return: Evaluated solution."""
        pass 
[docs]
    @abstractmethod
    def name(self) -> str:
        pass 
 
[docs]
class DynamicProblem(Problem[S], Observer, ABC):
[docs]
    @abstractmethod
    def the_problem_has_changed(self) -> bool:
        pass 
[docs]
    @abstractmethod
    def clear_changed(self) -> None:
        pass 
 
[docs]
class BinaryProblem(Problem[BinarySolution], ABC):
    """Class representing binary problems."""
    def __init__(self):
        super(BinaryProblem, self).__init__()
        self.number_of_bits_per_variable = []
[docs]
    def number_of_bits_per_variable_list(self):
        return self.number_of_bits_per_variable 
[docs]
    def total_number_of_bits(self):
        return sum(self.number_of_bits_per_variable) 
 
[docs]
class FloatProblem(Problem[FloatSolution], ABC):
    """Class representing float problems."""
    def __init__(self):
        super(FloatProblem, self).__init__()
        self.lower_bound = []
        self.upper_bound = []
[docs]
    def number_of_variables(self) -> int:
        return len(self.lower_bound) 
[docs]
    def create_solution(self) -> FloatSolution:
        new_solution = FloatSolution(
            self.lower_bound, self.upper_bound, self.number_of_objectives(), self.number_of_constraints()
        )
        new_solution.variables = [
            random.uniform(self.lower_bound[i] * 1.0, self.upper_bound[i] * 1.0)
            for i in range(self.number_of_variables())
        ]
        return new_solution 
 
[docs]
class IntegerProblem(Problem[IntegerSolution], ABC):
    """Class representing integer problems."""
    def __init__(self):
        super(IntegerProblem, self).__init__()
        self.lower_bound = []
        self.upper_bound = []
[docs]
    def number_of_variables(self) -> int:
        return len(self.lower_bound) 
[docs]
    def create_solution(self) -> IntegerSolution:
        new_solution = IntegerSolution(
            self.lower_bound, self.upper_bound, self.number_of_objectives(), self.number_of_constraints()
        )
        new_solution.variables = [
            round(random.uniform(self.lower_bound[i] * 1.0, self.upper_bound[i] * 1.0))
            for i in range(self.number_of_variables())
        ]
        return new_solution 
 
[docs]
class PermutationProblem(Problem[PermutationSolution], ABC):
    """Class representing permutation problems."""
    def __init__(self):
        super(PermutationProblem, self).__init__() 
[docs]
class OnTheFlyFloatProblem(FloatProblem):
    """ Class for defining float problems on the fly.
        Example:
        >>> # Defining problem Srinivas on the fly
        >>> def f1(x: [float]):
        >>>     return 2.0 + (x[0] - 2.0) * (x[0] - 2.0) + (x[1] - 1.0) * (x[1] - 1.0)
        >>>
        >>> def f2(x: [float]):
        >>>     return 9.0 * x[0] - (x[1] - 1.0) * (x[1] - 1.0)
        >>>
        >>> def c1(x: [float]):
        >>>     return 1.0 - (x[0] * x[0] + x[1] * x[1]) / 225.0
        >>>
        >>> def c2(x: [float]):
        >>>     return (3.0 * x[1] - x[0]) / 10.0 - 1.0
        >>>
        >>> problem = OnTheFlyFloatProblem()\
            .set_name("Srinivas")\
            .add_variable(-20.0, 20.0)\
            .add_variable(-20.0, 20.0)\
            .add_function(f1)\
            .add_function(f2)\
            .add_constraint(c1)\
            .add_constraint(c2)
    """
    def __init__(self):
        super(OnTheFlyFloatProblem, self).__init__()
        self.functions = []
        self.constraints = []
        self.problem_name = None
[docs]
    def set_name(self, name) -> "OnTheFlyFloatProblem":
        self.problem_name = name
        return self 
[docs]
    def add_function(self, function) -> "OnTheFlyFloatProblem":
        self.functions.append(function)
        return self 
[docs]
    def add_constraint(self, constraint) -> "OnTheFlyFloatProblem":
        self.constraints.append(constraint)
        return self 
[docs]
    def add_variable(self, lower_bound, upper_bound) -> "OnTheFlyFloatProblem":
        self.lower_bound.append(lower_bound)
        self.upper_bound.append(upper_bound)
        return self 
[docs]
    def number_of_objectives(self) -> int:
        return len(self.functions) 
[docs]
    def number_of_constraints(self) -> int:
        return len(self.constraints) 
[docs]
    def evaluate(self, solution: FloatSolution) -> None:
        for i in range(self.number_of_objectives()):
            solution.objectives[i] = self.functions[i](solution.variables)
        for i in range(self.number_of_constraints()):
            solution.constraints[i] = self.constraints[i](solution.variables) 
[docs]
    def name(self) -> str:
        return self.problem_name