{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "(user_guide_optimization_bga)=\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Genetic Algorithms" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Genetic algorithms (GAs) are a type of meteheuristic optimization algorithm inspired by the principles of natural selection and genetics. They are used to find approximate solutions to complex problems by mimicking the process of evolution. In a genetic algorithm, a population of potential solutions (individuals) is evolved over successive generations. Each individual is evaluated based on a fitness function, and the best-performing individuals are selected to create offspring through processes like crossover (recombination) and mutation. Over time, the population evolves toward better solutions. Genetic algorithms are particularly useful for solving problems with large, complex search spaces where traditional optimization methods might struggle. They are widely used in areas such as engineering, computer science, and artificial intelligence. **The class is designed to align with the 'survival of the fittest' principle by default, prioritizing maximization.**" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Binary Genetic Algorithm (BGA)" ] }, { "cell_type": "markdown", "metadata": { "editable": true, "slideshow": { "slide_type": "" }, "tags": [] }, "source": [ "\ud83d\udcd6 {ref}`BGA API Reference `." ] }, { "cell_type": "markdown", "metadata": { "editable": true, "slideshow": { "slide_type": "" }, "tags": [] }, "source": [ "A widely used variation is the binary genetic algorithm ({py:class}`~sigmaepsilon.math.optimize.bga.BinaryGeneticAlgorithm`), in which each candidate solution is represented as a sequence of binary digits (0s and 1s). In this encoding, each bit (gene) represents a specific attribute or decision variable of the solution, and the entire set of genes for an individual within the population forms a genom ({py:class}`~sigmaepsilon.math.optimize.ga.Genom`)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We will demonstrate the efficiency of the BGA by minimizing the Rosenbrock function, a widely used benchmark problem for evaluating the performance of nonlinear programming methods." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": true, "slideshow": { "slide_type": "" }, "tags": [] }, "outputs": [], "source": [ "from sigmaepsilon.math.optimize import BinaryGeneticAlgorithm as BGA\n", "from typing import Iterable\n", "from numbers import Number\n", "\n", "\n", "def Rosenbrock(x: Iterable[Number], a: Number = 1, b: Number = 100) -> float:\n", " return (a - x[0]) ** 2 + b * (x[1] - x[0] ** 2) ** 2\n", "\n", "\n", "ranges = [[-2, 2], [-1, 3]]\n", "bga = BGA(Rosenbrock, ranges, length=12, nPop=100, minimize=True, maxage=20)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To solve the problem, call the `solve` method, which returns an instance of {py:class}`~sigmaepsilon.math.optimize.ga.Genom`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "result = bga.solve()\n", "type(result), result.phenotype, result.fitness" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can get a more detailed view about the actual state of the optimizer by accessing the `state` attribute of the instance." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "bga.state" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This has a `to_scipy` method, if you prefer to see an instance of [scipy.optimize.OptimizeResult](https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.OptimizeResult.html#scipy.optimize.OptimizeResult) here. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "bga.state.to_scipy()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you want, you can have more control over the iterations using the `evolve` method, that runs a specified amount of evolutions:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import matplotlib.pyplot as plt\n", "\n", "bga = BGA(Rosenbrock, ranges, length=12, nPop=100, minimize=True)\n", "history = [Rosenbrock(bga.best_phenotype())]\n", "\n", "for _ in range(100):\n", " bga.evolve(1)\n", " history.append(bga.champion.fitness)\n", "\n", "plt.plot(history)\n", "plt.title('History of the best solution')\n", "plt.show()\n", "\n", "x = bga.champion.phenotype\n", "fx = bga.champion.fitness\n", "\n", "print(f\"The minimum value is f(x) = {fx} at x = {x}.\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import matplotlib.pyplot as plt\n", "\n", "# Generate grid data\n", "x = np.linspace(-2, 2, 400)\n", "y = np.linspace(-1, 3, 400)\n", "X, Y = np.meshgrid(x, y)\n", "Z = np.array([[Rosenbrock([xi, yi]) for xi, yi in zip(row_x, row_y)] for row_x, row_y in zip(X, Y)])\n", "\n", "# Create the plot with contour lines\n", "plt.figure(figsize=(8, 6))\n", "levels = [0, 1, 5, 10, 20, 50, 100, 200, 500, 800]\n", "contour = plt.contour(X, Y, Z, levels=levels, cmap='viridis')\n", "plt.clabel(contour, inline=True, fontsize=8)\n", "plt.title('Rosenbrock Function with Contour Lines')\n", "plt.xlabel('x')\n", "plt.ylabel('y')\n", "\n", "x = result.phenotype\n", "plt.scatter(x[0], x[1], color=\"red\", s=100, marker='s', label=\"BGA\")\n", "\n", "plt.scatter(1, 1, color=\"green\", s=100, marker='^', label=\"MIN\")\n", "\n", "plt.legend()\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For reproducible runs, or to run several independent instances in the same process, pass a `seed` to the constructor; this seeds a per-instance random number generator used for every stochastic operation (population initialization, crossover, mutation, selection) instead of the global NumPy random state:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": true, "slideshow": { "slide_type": "" }, "tags": [] }, "outputs": [], "source": [ "bga_1 = BGA(Rosenbrock, ranges, length=12, nPop=100, minimize=True, seed=0)\n", "bga_2 = BGA(Rosenbrock, ranges, length=12, nPop=100, minimize=True, seed=0)\n", "result_1 = bga_1.solve()\n", "result_2 = bga_2.solve()\n", "result_1.phenotype == result_2.phenotype" ] } ], "metadata": { "kernelspec": { "display_name": "sigmaepsilon-math-NMVzm02N-py3.12", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.7" }, "widgets": { "application/vnd.jupyter.widget-state+json": { "state": {}, "version_major": 2, "version_minor": 0 } } }, "nbformat": 4, "nbformat_minor": 4 }