From a222271aadbb764ec8d327c4e1e588990abb0e15 Mon Sep 17 00:00:00 2001 From: Tom van Woudenberg Date: Wed, 22 May 2024 17:50:10 +0200 Subject: [PATCH] Add stripped version --- book/pages/metaheuristics_example.ipynb | 6 +- book/pages/metaheuristics_stripped.ipynb | 512 +++++++++++++++++++++++ 2 files changed, 515 insertions(+), 3 deletions(-) create mode 100644 book/pages/metaheuristics_stripped.ipynb diff --git a/book/pages/metaheuristics_example.ipynb b/book/pages/metaheuristics_example.ipynb index c6044e1..4a84a39 100644 --- a/book/pages/metaheuristics_example.ipynb +++ b/book/pages/metaheuristics_example.ipynb @@ -7,9 +7,9 @@ "source": [ "# Construction site\n", "\n", - "%```{custom_download_link} ./metaheuristics_stripped.ipynb\n", - "%:replace_default: \"True\"\n", - "%```\n", + "```{custom_download_link} ./metaheuristics_stripped.ipynb\n", + ":replace_default: \"True\"\n", + "```\n", "\n", "Click {fa}`rocket` --> {guilabel}`Live Code` to activate live coding on this page!\n", "\n", diff --git a/book/pages/metaheuristics_stripped.ipynb b/book/pages/metaheuristics_stripped.ipynb new file mode 100644 index 0000000..f81cbaf --- /dev/null +++ b/book/pages/metaheuristics_stripped.ipynb @@ -0,0 +1,512 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "f350b263", + "metadata": {}, + "source": [ + "# Construction site\n", + "\n", + "## Problem\n", + "\n", + "\n", + "As managers of a construction site, we have to decide the required equipment to move a given volume of excavated soil. In order to achieve the deadline for this working unit, we need to guarantee an averaged efficiency of $2700$ $\\cfrac{\\text{m}^3}{h}$ during one month.\n", + "\n", + "We have our own equipment, and also we can sub-contract another (just one) company. The efficiency of each equipment and the cost are given in the table:\n", + "\n", + "\n", + "| Own equip. | | Company 1 | | Company 2 | |\n", + "|:-----------------------:|:---------------------:|:-----------------------:|:---------------------:|:-----------------------:|:---------------------:|\n", + "| Eff. $\\left(\\frac{\\text{m}^3}{h}\\right)$ | Cost $\\left(\\frac{€}{h}\\right)$ | Eff. $\\left(\\frac{\\text{m}^3}{h}\\right)$ | Cost $\\left(\\frac{€}{h}\\right)$ | Eff. $\\left(\\frac{\\text{m}^3}{h}\\right)$ | Cost $\\left(\\frac{€}{h}\\right)$ |\n", + "| 200 | 500 | 470 | 4.000 | 640 | 5.400 |\n", + "| 240 | 800 | 700 | 5.700 | 730 | 5.500 |\n", + "| 265 | 1.000 | 800 | 6.500 | 775 | 6.800 |\n", + "| 330 | 1.500 | | | | |\n", + "\n", + "Then, we can use part or all of our own equipment with some of the equipment options provided by another company. \n", + "\n", + "What is the optimal equipment combination that minimizes the cost?" + ] + }, + { + "cell_type": "markdown", + "id": "56fc737c", + "metadata": {}, + "source": [ + "## Model\n", + "\n", + "We'll define the model as follows:\n", + "- Design variables: Whether or not to use each type of equipment\n", + "- Objective function: sum of the costs of the selected equipment\n", + "- Constraint functions: make use of one sub-contractor and create an efficiency of at least $2700$ $\\cfrac{\\text{m}^3}{h}$\n", + "\n", + "### Design variables\n", + "Let's start with our design variables. let's define them as a list of integer values either being $0$ or $1$, which selects the equipment type.\n", + "\n", + "$$\n", + "x=\\left[ \\begin{matrix}\n", + " {{x}_{\\text{First item of own equipment}}} \\\\\n", + " {{x}_{\\text{Second item of own equipment}}} \\\\\n", + " {{x}_{\\text{Third item of own equipment}}} \\\\\n", + " {{x}_{\\text{Fourth item of own equipment}}} \\\\\n", + " {{x}_{\\text{First item of company 1}}} \\\\\n", + " {{x}_{\\text{Second item of company 1}}} \\\\\n", + " {{x}_{\\text{Third item of company 1}}} \\\\\n", + " {{x}_{\\text{First item of company 2}}} \\\\\n", + " {{x}_{\\text{Second item of company 2}}} \\\\\n", + " {{x}_{\\text{Third item of company 2}}} \\\\\n", + "\\end{matrix} \\right]=\\left[ \\begin{matrix}\n", + " {{x}_{1}} \\\\\n", + " {{x}_{2}} \\\\\n", + " {{x}_{3}} \\\\\n", + " {{x}_{4}} \\\\\n", + " {{x}_{5}} \\\\\n", + " {{x}_{6}} \\\\\n", + " {{x}_{7}} \\\\\n", + " {{x}_{8}} \\\\\n", + " {{x}_{9}} \\\\\n", + " {{x}_{10}} \\\\\n", + "\\end{matrix} \\right]\n", + "$$\n", + "\n", + "The design variables either being $0$ or $1$ can be defined mathemetically:\n", + "\n", + "$$\n", + "x_i\\in \\left\\{ 0,1 \\right\\}\\text{ } i=1,2,...,10\n", + "$$\n", + "\n", + "### Objective function\n", + "\n", + "Now we can define the objective function as the product of the dimension to represent $\\mathop {\\min }\\limits_x f\\left(x\\right) $:\n", + "\n", + "$$\n", + "f\\left(x\\right) = 500 \\cdot x_1 + 800 \\cdot x_2 + 1000 \\cdot x_3 + 1500 \\cdot x_4 + 4000 \\cdot x_5 + 5700 \\cdot x_6 + 6500 \\cdot x_7 + 5400 \\cdot x_8 + 5500 \\cdot x_9 + 6800 \\cdot x_{10}\n", + "$$\n", + "\n", + "As this is a linear relation, this can be converted to matrix notation. In case of the design variables in the form $\\mathop {\\min }\\limits_x {c^T}x$ with $c$:\n", + "\n", + "$$\n", + "c = {{\\left[ \\begin{matrix}\n", + " 500 & 800 & 1000 & 1500 & 4000 & 5700 & 6500 & 5400 & 5500 & 6800 \\\\\n", + "\\end{matrix} \\right]}^{T}}\n", + "$$\n" + ] + }, + { + "cell_type": "markdown", + "id": "ab0d0aaf", + "metadata": {}, + "source": [ + "### Constraint function\n", + "\n", + "We need to define two constraint functions, let's start with the inequality constraint functions as ${{g}}\\left(x\\right) \\le 0$:\n", + "\n", + "$$\n", + "{{g}}\\left(x\\right) = -200 \\cdot x_1 - 240 \\cdot x_2 - 265 \\cdot x_3 - 330 \\cdot x_4 - 470 \\cdot x_5 - 700 \\cdot x_6 - 800 \\cdot x_7 - 640 \\cdot x_8 - 730 \\cdot x_9 - 775 \\cdot {x_{10}} +2700\n", + "$$\n", + "\n", + "As this is a linear relation, this can be converted to matrix notation in the form ${A_{ub}}x \\le {b_{ub}}$:\n", + "\n", + "$$\n", + "\\left[ \\begin{matrix}\n", + " -200 & -240 & -265 & -330 & -470 & -700 & -800 & -640 & -730 & -775\n", + "\\end{matrix} \\right] x \\le -2700 \n", + "$$\n", + "\n", + "Let's add the equality constraint function. As soon as we hire one piece of equipment this should be violated. It is defined as ${{h}}\\left(x\\right) \\le 0$:\n", + "\n", + "$$\n", + "{{h}}\\left(x\\right) = \\max \\left( x_5, x_6, x_7 \\right) \\cdot \\max \\left( x_8, x_9, x_{10} \\right)\n", + "$$\n", + "\n", + "Clearly, this function is not linear, so we cannot define it in matrix formulation.\n" + ] + }, + { + "cell_type": "markdown", + "id": "2a609af6", + "metadata": {}, + "source": [ + "## Method\n", + "\n", + "Now let's solve this problem using both `scipy` and `pymoo`.\n" + ] + }, + { + "cell_type": "markdown", + "id": "e567e5b5", + "metadata": {}, + "source": [ + "### Import libraries" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "a771a0c5", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np" + ] + }, + { + "cell_type": "markdown", + "id": "a0fa11ef", + "metadata": {}, + "source": [ + "#### SciPy\n", + "\n", + "For SciPy this is easy:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "d4f3fe4b", + "metadata": {}, + "outputs": [], + "source": [ + "import scipy as sp" + ] + }, + { + "cell_type": "markdown", + "id": "6c5c75da", + "metadata": {}, + "source": [ + "#### pymoo\n", + "For pymoo we need to import a lot more and be more specific:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "5b0b7252", + "metadata": {}, + "outputs": [], + "source": [ + "from pymoo.problems.functional import FunctionalProblem as FunctionalProblem\n", + "from pymoo.algorithms.soo.nonconvex.ga import GA\n", + "from pymoo.optimize import minimize\n", + "from pymoo.operators.sampling.rnd import IntegerRandomSampling \n", + "from pymoo.operators.crossover.sbx import SBX\n", + "from pymoo.operators.mutation.pm import PM\n", + "from pymoo.operators.repair.rounding import RoundingRepair" + ] + }, + { + "cell_type": "markdown", + "id": "c1e8ff92", + "metadata": {}, + "source": [ + "### Define variables\n", + "This is done differently in SciPy and pymoo.\n", + "\n", + "#### SciPy\n", + "As before, we don't need to specify our variable $x$ itself. However, we do need to specify that we have integers (specifically only $0$ and $1$ which will be covered by the bounds)\n", + "In SciPy we use an array of booleans to specify which design variables are integers: " + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "1c46446d", + "metadata": {}, + "outputs": [], + "source": [ + "integers=np.array([True,True,True,True,True,True,True,True,True,True])" + ] + }, + { + "cell_type": "markdown", + "id": "d10375a7", + "metadata": {}, + "source": [ + "#### pymoo\n", + "In pymoo we don't have to specify that have integers, but we have to adapt all of our functions later on so that the outputs are integers. Furthermore, we must specify explicitly how many design variables we have:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "8dad52d6", + "metadata": {}, + "outputs": [], + "source": [ + "n_var = 10" + ] + }, + { + "cell_type": "markdown", + "id": "3f5b4bc7", + "metadata": {}, + "source": [ + "### Define bounds\n", + "Now let's continue with the bounds.\n", + "\n", + "#### SciPy\n", + "The bounds are defined as before:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "f422f0d1", + "metadata": {}, + "outputs": [], + "source": [ + "bounds = [[0,1],\n", + " [0,1],\n", + " [0,1],\n", + " [0,1],\n", + " [0,1],\n", + " [0,1],\n", + " [0,1],\n", + " [0,1],\n", + " [0,1],\n", + " [0,1]]" + ] + }, + { + "cell_type": "markdown", + "id": "b444641c", + "metadata": {}, + "source": [ + "#### pymoo\n", + "\n", + "In pymoo, the bounds are defined as separate arrays:" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "f740b252", + "metadata": {}, + "outputs": [], + "source": [ + "xl = np.array([0,0,0,0,0,0,0,0,0,0])\n", + "xu = np.array([1,1,1,1,1,1,1,1,1,1])" + ] + }, + { + "cell_type": "markdown", + "id": "1333c1bb", + "metadata": {}, + "source": [ + "### Define objective function\n", + "\n", + "Let's define the objective function." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "c753c482", + "metadata": {}, + "outputs": [], + "source": [ + "def obj(x):\n", + " return np.array([500, 800, 1000, 1500, 4000, 5700, 6500, 5400, 5500, 6800])@x" + ] + }, + { + "cell_type": "markdown", + "id": "630cf95d", + "metadata": {}, + "source": [ + "### Define constraint function\n", + "\n", + "Let's define the constraint function." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "f4173993", + "metadata": {}, + "outputs": [], + "source": [ + "g = np.array([-200, -240, -265, -330, -470, -700, -800, -640, -730, -775])\n", + "\n", + "def nonlinconfun(x):\n", + " return max(x[4:7]) * max(x[7:])" + ] + }, + { + "cell_type": "markdown", + "id": "a8500036", + "metadata": {}, + "source": [ + "#### SciPy\n", + "\n", + "In SciPy we need to store the functions in a scipy-object including the bounds" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "e35b955a", + "metadata": {}, + "outputs": [], + "source": [ + "lincon = sp.optimize.LinearConstraint(g, lb=-np.inf, ub=-2700)\n", + "nonlincon = sp.optimize.NonlinearConstraint(nonlinconfun, 0, 0)" + ] + }, + { + "cell_type": "markdown", + "id": "7635e24b", + "metadata": {}, + "source": [ + "#### pymoo\n", + "In pymoo the functions can be inserted directly in the problem object later on. However, this requires a function for the linear constraints instead of just the array" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "4f4b6f42", + "metadata": {}, + "outputs": [], + "source": [ + "def linconfun(x):\n", + " return g@x + 2700" + ] + }, + { + "cell_type": "markdown", + "id": "76ddef0f", + "metadata": {}, + "source": [ + "### Solve the problem" + ] + }, + { + "cell_type": "markdown", + "id": "18dba09c", + "metadata": {}, + "source": [ + "Now let's solve the problem using both SciPy and pymoo.\n", + "\n", + "#### SciPy" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "d2d014c1", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " message: Optimization terminated successfully.\n", + " success: True\n", + " fun: 19000.0\n", + " x: [ 1.000e+00 1.000e+00 0.000e+00 1.000e+00\n", + " 1.000e+00 1.000e+00 1.000e+00 0.000e+00\n", + " 0.000e+00 0.000e+00]\n", + " nit: 34\n", + " nfev: 1226\n", + " population: [[ 1.000e+00 1.000e+00 ... 0.000e+00 0.000e+00]\n", + " [ 1.000e+00 1.000e+00 ... 0.000e+00 0.000e+00]\n", + " ...\n", + " [ 1.000e+00 1.000e+00 ... 0.000e+00 0.000e+00]\n", + " [ 1.000e+00 1.000e+00 ... 0.000e+00 0.000e+00]]\n", + " population_energies: [ 1.900e+04 1.900e+04 ... 1.900e+04 1.900e+04]\n", + " constr: [array([ 0.000e+00]), array([ 0.000e+00])]\n", + " constr_violation: 0.0\n", + " maxcv: 0.0\n" + ] + } + ], + "source": [ + "result_scipy = sp.optimize.differential_evolution(func = obj,bounds = bounds,constraints=[lincon,nonlincon], integrality = integers)\n", + "print(result_scipy)" + ] + }, + { + "cell_type": "markdown", + "id": "83dd0088", + "metadata": {}, + "source": [ + "#### Pymoo\n", + "\n", + "In pymoo we have to define the problem and algorithm as objects, and call them from the `pymoo.minimize` function. For the `GA` functions, we repair some stuff so that we only deal with integer values" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "78d474e0", + "metadata": {}, + "outputs": [], + "source": [ + "problem = FunctionalProblem(n_var=n_var, objs=obj, constr_ieq=[linconfun],constr_eq = [nonlinconfun],xl=xl, xu = xu)\n", + "algorithm = GA(sampling=IntegerRandomSampling(),crossover=SBX(repair=RoundingRepair()),mutation=PM(repair=RoundingRepair()))" + ] + }, + { + "cell_type": "markdown", + "id": "6c0702bb", + "metadata": {}, + "source": [ + "Now we can solve the problem" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "dc04e043", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0 1 1 1 1 1 1 0 0 0]\n", + "[19500.]\n" + ] + } + ], + "source": [ + "result_pymoo = minimize(problem, algorithm)\n", + "print(result_pymoo.X)\n", + "print(result_pymoo.F)" + ] + }, + { + "cell_type": "markdown", + "id": "53de36ad", + "metadata": {}, + "source": [ + ":::{card} Test yourself\n", + "\n", + ":::" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.11.8" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}