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