Tutorial 1: introduction to BenchmarkFunction class

Benchmark functions are essential for testing optimization algorithms. In this tutorial, you’ll learn how to use the BenchmarkFunction class, which provides a structured way to define and evaluate benchmark functions for optimization purposes.

The BenchmarkFunction class allows you to:

  • Define a (multidimensional) function;

  • Specify bounds along each dimension;

  • Evaluate the function at specific points;

  • Retrieve the optimal value of the function.

As an example, we will consider the Rastringin function, which is a popular benchmark for optimization algorithms. It is known to be a multimodal function with many local minima, defined as follows:

\[f(\mathbf{x}) = 10 \cdot D + \sum_{i=1}^{D} \left( x_i^2 - 10 \cdot \cos(2 \pi x_i) \right)\]

where \(x_i \in [-5.12, 5.12]\) and \(D\) is the dimension of the problem.

It has global minimum at \(f(\mathbf{x}^*) = 0\) for \(\mathbf{x}^* = (0, 0, \ldots, 0)\).

A general BenchmarkFunction object should be initialized with:

  • A name for the function (optional);

  • The function itself (as a callable object);

  • The bounds for each variable (as a NumPy-array);

  • The optimal solution (as a NumPy-array).

Let’s see how we can define the Rastringin function for \(D=5\).

[2]:
import numpy as np
from beeoptimal.benchmarks import BenchmarkFunction
[3]:
D                = 5
name             = f"Rastringin-{D}d"
function         = lambda x: (10*len(x) + np.sum((x**2 - 10*np.cos(2*np.pi*x))))
bounds           = np.array([[-5.12, 5.12]]*D)
optimal_solution = np.zeros(D)
[4]:
Rastringin2dBenchmark = BenchmarkFunction(
    name             = name,
    fun              = function,
    bounds           = bounds,
    optimal_solution = optimal_solution
)
[5]:
print(f"\nBenchmark:\n {Rastringin2dBenchmark.name}")
print(f"\nDefault Bounds:\n {Rastringin2dBenchmark.bounds}")
print(f"\nOptimal Solution:\n {Rastringin2dBenchmark.optimal_solution}")
print(f"\nOptimal Value:\n {Rastringin2dBenchmark.optimal_value}")

Benchmark:
 Rastringin-5d

Default Bounds:
 [[-5.12  5.12]
 [-5.12  5.12]
 [-5.12  5.12]
 [-5.12  5.12]
 [-5.12  5.12]]

Optimal Solution:
 [0. 0. 0. 0. 0.]

Optimal Value:
 0.0

Given a new point \(\mathbf{x} \in \mathbb{R}^{5}\), the function can be evaluated as follows:

[6]:
x   = np.random.uniform(low=Rastringin2dBenchmark.bounds[:,0], high=Rastringin2dBenchmark.bounds[:,1], size=D)
f_x = Rastringin2dBenchmark.evaluate(x)

print(f"{Rastringin2dBenchmark.name} function evaluated at point x={x} --> f(x)={f_x}")
Rastringin-5d function evaluated at point x=[ 0.10538191 -4.49508415 -3.52525349  5.09557108  3.70357909] --> f(x)=128.9333795057978