From bcf22269e8fde580db619ff89ff0b32da1133dc5 Mon Sep 17 00:00:00 2001 From: Peter Vacho Date: Tue, 11 Mar 2025 01:02:41 +0100 Subject: [PATCH] Add necessary utils --- src/function.py | 42 +++++++++++++++++ src/functions.py | 116 +++++++++++++++++++++++++++++++++++++++++++++++ src/types.py | 4 ++ 3 files changed, 162 insertions(+) create mode 100644 src/function.py create mode 100644 src/functions.py create mode 100644 src/types.py diff --git a/src/function.py b/src/function.py new file mode 100644 index 0000000..5b9106b --- /dev/null +++ b/src/function.py @@ -0,0 +1,42 @@ +from collections.abc import Callable +from dataclasses import dataclass +from typing import NamedTuple + +import numpy as np + +from src.types import INPUT_VECTOR + + +class Interval(NamedTuple): + """Interval determining a number range (both inclusive).""" + + min: float + max: float + + def random_point(self, dimensions: int, rng: np.random.Generator | None = None) -> INPUT_VECTOR: + """Generate an N-dimensional random point, where all numbers lie within this interval. + + Args: + dimensions: The amount of dimensions this point should have. + rng: Random generator instance (None for a new rng). + """ + if rng is None: + rng = np.random.default_rng() + + return rng.uniform(self.min, self.max, size=dimensions) + + +@dataclass +class Function: + """Class representing an N-dimensional function. + + This function can work with arbitrary number of dimensions, where each input dimension is restricted + within the specified definition interval. + """ + + function: Callable[[INPUT_VECTOR], float] + definition_interval: Interval + + def __call__(self, params: INPUT_VECTOR) -> float: + """Evaluate the function.""" + return self.function(params) diff --git a/src/functions.py b/src/functions.py new file mode 100644 index 0000000..fe6e1b2 --- /dev/null +++ b/src/functions.py @@ -0,0 +1,116 @@ +"""Various N-dimensional well-known optimization functions. + +All of the functions here work with N (# of dimensions) ranging from 1 to infinity. +""" + +import numpy as np + +from src.function import Function, Interval +from src.types import INPUT_VECTOR + + +def _sphere_function(vector: INPUT_VECTOR) -> float: + """Compute the sphere function. + + Arguments: + vector: An n-dimensional input vector. + + Returns: + float: The function value at the given input vector. + + See: https://www.sfu.ca/~ssurjano/spheref.html + """ + return np.sum(vector**2) + + +sphere_function = Function(_sphere_function, Interval(-10, 10)) + + +def _zakharov_function(vector: INPUT_VECTOR) -> float: + """Compute the Zakharov function. + + Arguments: + vector: An n-dimensional input vector. + + Returns: + float: The function value at the given input vector. + + See: https://www.sfu.ca/~ssurjano/zakharov.html + """ + d = vector.shape[0] + + # First term: sum(x_i^2) + term_1 = np.sum(vector**2) + + # Second term: (sum(0.5 * i * x_i))^2 + term_2 = np.sum(0.5 * np.arange(1, d + 1) * vector) ** 2 + + # Third term: (sum(0.5 * i * x_i))^4 + term_3 = np.sum(0.5 * np.arange(1, d + 1) * vector) ** 4 + + return term_1 + term_2 + term_3 + + +zakharov_function = Function(_zakharov_function, Interval(-10, 10)) + + +def _rosenbrock_function(vector: INPUT_VECTOR) -> float: + """Compute the Rosenbrock function. + + Arguments: + vector: An n-dimensional input vector. + + Returns: + float: The function value at the given input vector. + + See: https://www.sfu.ca/~ssurjano/rosenbrockf.html + """ + return np.sum(100 * (vector[:-1] ** 2 - vector[1:]) ** 2 + (1 - vector[:-1]) ** 2) + + +rosenbrock_function = Function(_rosenbrock_function, Interval(-10, 10)) + + +def _schwefel_function(vector: INPUT_VECTOR) -> float: + """Compute the Schwefel function. + + Arguments: + vector: An n-dimensional input vector. + + Returns: + float: The function value at the given input vector. + + See: https://www.sfu.ca/~ssurjano/schwefel.html + """ + d = vector.shape[0] + + return 418.9829 * d - np.sum(vector * np.sin(np.sqrt(np.abs(vector)))) + + +schwefel_function = Function(_schwefel_function, Interval(-500, 500)) + + +def _styblinski_tang_function(vector: INPUT_VECTOR) -> float: + """Compute the Styblinski-Tang function. + + Arguments: + vector: An n-dimensional input vector. + + Returns: + float: The function value at the given input vector. + + See: https://www.sfu.ca/~ssurjano/stybtang.html + """ + return 0.5 * np.sum(vector**4 - 16 * vector**2 + 5 * vector) + + +styblinski_tang_function = Function(_styblinski_tang_function, Interval(-5, 5)) + + +available_functions = { + "Sphere": sphere_function, + "Zakharov": zakharov_function, + "Rosenbrock": rosenbrock_function, + "Schwefel": schwefel_function, + "Styblinski-Tang": styblinski_tang_function, +} diff --git a/src/types.py b/src/types.py new file mode 100644 index 0000000..a868754 --- /dev/null +++ b/src/types.py @@ -0,0 +1,4 @@ +import numpy as np +import numpy.typing as npt + +type INPUT_VECTOR = npt.NDArray[np.float64]