# PyFDE tutorial¶

In this tutorial we will write a program to find (we hope so) the global minimum of the 2D Rastrigin function given by Eq. (1):

(1)$f(x,y) = 20 + (x^2 - 10\cos(2\pi x)) + (y^2 - 10\cos(2\pi y))$

Eq. (1) has a global minimum at (x=0,y=0). Our program will use PyFDE, starting by loading the required modules:

import pyfde
from math import cos, pi


The next step is to define the fitness evaluation function. This function receives one list containing the description of a vector (in this case an array with two floats), and must return the fitness evaluation, as a float. This fitness value represents the quality of the solution (the higher the better), and will guide the optimization process.

We want to minimize the value of the Rastrigin function, so our fitness evaluation will be the output of the function multiplied by -1. Thus, when the output of the function decrease, our fitness evaluation increases:

def fitness(p):
x, y = p[0], p[1]
val = 20 + (x**2 - 10*cos(2*pi*x)) + (y**2 - 10*cos(2*pi*y))
return -val


With the function defined, we can now initialize the solver state and configure its parameters:

solver = pyfde.ClassicDE(fitness, n_dim=2, n_pop=40, limits=(-5.12, 5.12))
solver.cr, solver.f = 0.9, 0.45


We used a population of 40 vectors in a 2D search space (one for each variable, x and y). We have limited the search space to values between (-5.12, 5.12). We can also configure the limits of each dimension manually by passing a list of tuples, like limits=[(-5.12, 5.12), (-2.5, 2.5)].

Lets run it:

best, fit = solver.run(n_it=150)


Now, the best found solution is available with best[0] and best[1]. The final fitness evaluation is stored in fit.

Finally, let’s print the solution:

print("**Best solution found**")
print("x, y    = {:.2f}, {:.2f} (expected: 0.00, 0.00)".format(best[0], best[1]))
print("Fitness = {:.2f}".format(fit))


The whole source code is available at tests/rastrigin.py.

## Iterators¶

To use custom stopping conditions, one can use the generator syntax:

for best, fit in solver(n_it=10):
# custom logic here, breaking when desired


It is also possible to access every solution vector by iterating the solver:

for vector, fit in solver:
print(vector, fit)


## Batch mode¶

To further enhance the performance, specially if the fitness function is also implemented in Cython, it is possible to specify a batch mode by setting the batch parameter to True in the solver constructor.

In batch mode, the fitness function will be called only once per iteration to evaluate the fitness of all the population. In this mode, the fitness function will receive the population as first parameter, and the fitness array to be updated as second parameter: fitness(double[:,::1] pop, double[::1] fit).

## Reproducibility and random seed¶

PyFDE uses the xorshift128+ algorithm as its pseudo random number generator. Every solver instance has its own internal PRNG state. The PRNG initial seed can be set by using the seed parameter in the solver constructor. If no seed is specified, then it will be computed from the current time.

Implementing its own PRNG also allows a certain reproducibility of results, since by using the same seed in each run one can obtain the exact same output even on different computers / operating systems.

## Other DE strategies¶

Besides the ClassicDE, PyFDE also implements the JADE algorithm. The JADE algorithm is an adaptative DE algorithm, that adjusts its internal CR and F parameters during the optimization procedure to adapt to the current problem fitness landscape.