Source code for evox.problems.numerical.basic

__all__ = [
    "ShiftAffineNumericalProblem",
    "Ackley",
    "Griewank",
    "Rastrigin",
    "Rosenbrock",
    "Schwefel",
    "Sphere",
    "Ellipsoid",
    "ackley_func",
    "griewank_func",
    "rastrigin_func",
    "rosenbrock_func",
    "schwefel_func",
    "sphere_func",
    "ellipsoid_func",
]


import torch

from evox.core import Problem


[docs] class ShiftAffineNumericalProblem(Problem): """A numerical problem with a shift and affine transformations to the input points.""" def __init__(self, shift: torch.Tensor | None = None, affine: torch.Tensor | None = None): """Initialize the ShiftAffineNumericalProblem. :param shift: The shift vector. Defaults to None. None represents no shift. :param affine: The affine transformation matrix. Defaults to None. None represents no affine transformation. """ super().__init__() if affine is not None: assert affine.ndim == 2 and affine.shape[0] == affine.shape[1], "affine must be a square matrix" self.affine = affine[None, :, :] else: self.affine = None if shift is not None: assert shift.ndim == 1, "shift must be a vector" if affine is not None: assert affine.shape[0] == shift.shape[0], "affine and shift must have the same dimension" self.shift = shift[None, :] else: self.shift = None
[docs] def evaluate(self, pop: torch.Tensor) -> torch.Tensor: """Evaluate the given population by shifting and applying an affine transformation to the input points first, and then evaluating the points with the actual function. :param pop: The population of points to evaluate. :return: The evaluated fitness of the population. """ if self.shift is not None: pop = pop + self.shift if self.affine is not None: pop = torch.matmul(pop[:, None, :], self.affine).squeeze(1) return self._true_evaluate(pop)
[docs] def ackley_func(a: float, b: float, c: float, x: torch.Tensor) -> torch.Tensor: return ( -a * torch.exp(-b * torch.sqrt(torch.mean(x**2, dim=1))) - torch.exp(torch.mean(torch.cos(c * x), dim=1)) + a + torch.e )
[docs] class Ackley(ShiftAffineNumericalProblem): """The Ackley function whose minimum is x = [0, ..., 0]""" def __init__(self, a: float = 20.0, b: float = 0.2, c: float = 2 * torch.pi, **kwargs): """Initialize the Ackley function with the given parameters. :param a: The parameter $a$ in the equation. Defaults to 20.0. :param b: The parameter $b$ in the equation. Defaults to 0.2. :param c: The parameter $c$ in the equation. Defaults to 2 * pi. :param **kwargs: The keyword arguments (`shift` and `affine`) to pass to the superclass `ShiftAffineNumericalProblem`. """ super().__init__(**kwargs) self.a = a self.b = b self.c = c
[docs] def _true_evaluate(self, x: torch.Tensor) -> torch.Tensor: return ackley_func(self.a, self.b, self.c, x)
[docs] def griewank_func(x: torch.Tensor) -> torch.Tensor: f = 1 / 4000 * torch.sum(x**2, dim=1) - torch.prod(torch.cos(x / torch.sqrt(torch.arange(1, x.size(1) + 1)))) + 1 return f
[docs] class Griewank(ShiftAffineNumericalProblem): """The Griewank function whose minimum is x = [0, ..., 0]""" def __init__(self, **kwargs): """Initialize the Griewank function with the given parameters. :param **kwargs: The keyword arguments (`shift` and `affine`) to pass to the superclass `ShiftAffineNumericalProblem`. """ super().__init__(**kwargs)
[docs] def _true_evaluate(self, x: torch.Tensor) -> torch.Tensor: return griewank_func(x)
[docs] def rastrigin_func(x: torch.Tensor) -> torch.Tensor: return 10 * x.size(1) + torch.sum(x**2 - 10 * torch.cos(2 * torch.pi * x), dim=1)
[docs] class Rastrigin(ShiftAffineNumericalProblem): """The Rastrigin function whose minimum is x = [0, ..., 0]""" def __init__(self, **kwargs): """Initialize the Griewank function with the given parameters. :param **kwargs: The keyword arguments (`shift` and `affine`) to pass to the superclass `ShiftAffineNumericalProblem`. """ super().__init__(**kwargs)
[docs] def _true_evaluate(self, x: torch.Tensor) -> torch.Tensor: return rastrigin_func(x)
[docs] def rosenbrock_func(x): f = torch.sum(100 * ((x[:, 1:]) - x[:, : x.size(1) - 1] ** 2) ** 2 + (x[:, : x.size(1) - 1] - 1) ** 2, dim=1) return f
[docs] class Rosenbrock(ShiftAffineNumericalProblem): """The Rosenbrock function whose minimum is x = [1, ..., 1]""" def __init__(self, **kwargs): """Initialize the Griewank function with the given parameters. :param **kwargs: The keyword arguments (`shift` and `affine`) to pass to the superclass `ShiftAffineNumericalProblem`. """ super().__init__(**kwargs)
[docs] def _true_evaluate(self, x: torch.Tensor) -> torch.Tensor: return rosenbrock_func(x)
[docs] def schwefel_func(x): return 418.9828872724338 * x.size(1) - torch.sum(x * torch.sin(torch.sqrt(torch.abs(x))), dim=1)
[docs] class Schwefel(ShiftAffineNumericalProblem): """The Schwefel function whose minimum is x = [420.9687, ..., 420.9687]""" def __init__(self, **kwargs): """Initialize the Griewank function with the given parameters. :param **kwargs: The keyword arguments (`shift` and `affine`) to pass to the superclass `ShiftAffineNumericalProblem`. """ super().__init__(**kwargs)
[docs] def _true_evaluate(self, x: torch.Tensor) -> torch.Tensor: return schwefel_func(x)
[docs] def sphere_func(x): return torch.sum(x**2, dim=1)
[docs] class Sphere(ShiftAffineNumericalProblem): """The sphere function whose minimum is x = [0, ..., 0]""" def __init__(self, **kwargs): """Initialize the Griewank function with the given parameters. :param **kwargs: The keyword arguments (`shift` and `affine`) to pass to the superclass `ShiftAffineNumericalProblem`. """ super().__init__(**kwargs)
[docs] def _true_evaluate(self, x: torch.Tensor) -> torch.Tensor: return sphere_func(x)
[docs] class Ellipsoid(ShiftAffineNumericalProblem): """The Ellipsoid function whose minimum is x = [0, ..., 0]""" def __init__(self, **kwargs): """Initialize the Ellipsoid function with the given parameters. :param **kwargs: The keyword arguments (`shift` and `affine`) to pass to the superclass `ShiftAffineNumericalProblem`. """ super().__init__(**kwargs)
[docs] def _true_evaluate(self, x: torch.Tensor) -> torch.Tensor: return ellipsoid_func(x)
[docs] def ellipsoid_func(x: torch.Tensor): return torch.sum(torch.arange(1, x.size(1) + 1, device=x.device) * x**2, dim=1)