In this post, we’ll implement a genetic algorithm using Python and NumPy.
A genetic algorithm is a type of optimization algorithm that mimics natural selection to find the optimal point (or points) in the design space. It uses a population of solutions that evolve over several generations toward better solutions. Each individual in the population represents a potential solution to the problem encoded in a way that can be evaluated for its fitness.
Over successive generations, individuals with higher fitness are more likely to pass their traits on to the next generation. An individual’s traits are passed down to the next generation using three main genetic operators: selection, crossover, and mutation.
As its name hints, the selection operator chooses the fittest individuals in the current population. The selected individuals will produce offspring. Next, the crossover operator combines the genetic information of two (or more) parent individuals to create one or more offspring. Thus, crossover introduces variation into future populations and mixes successful traits. Finally, the new population resulting from the crossover operator is subject to mutations, or small, random changes in the individuals’ characteristics. Mutations help introduce diversity into populations and avoid premature convergence to suboptimal solutions.
The Problem
The problem we’ll try to solve can be formally stated as:
The objective function \(f\) is known as the Michalewicz function, which we can define in Python like this:
Code
import numpy as np# Set the random seed for reproducibilitynp.random.seed(42)def michalewicz_function(X: np.ndarray) -> np.ndarray:""" Computes the Michalewicz function for a given 2D input array. The Michalewicz function is a test problem for optimization algorithms, particularly known for its steep valleys and ridges. It is defined for two input variables, x1 and x2, with the following equation: f(x1, x2) = -sin(x1) * (sin(x1^2 / pi))^20 - sin(x2) * (sin(2 * x2^2 / pi))^20 Parameters ---------- X : np.ndarray A 2D numpy array of shape (n_samples, 2) where each row contains the values of x1 and x2. X[:, 0] corresponds to x1 and X[:, 1] corresponds to x2. Returns ------- np.ndarray A 1D numpy array of shape (n_samples,) containing the computed Michalewicz function values for each (x1, x2) pair. Example ------- >>> X = np.array([[2.20, 1.57], [2.90, 2.30]]) >>> michalewicz_function(X) array([-1.80114072e+00, -2.54559837e-08]) """ x1, x2 = X[:, 0], X[:, 1] term1 =-np.sin(x1) * np.sin(x1**2/ np.pi) **20 term2 =-np.sin(x2) * np.sin(2* x2**2/ np.pi) **20return term1 + term2