{ "cells": [ { "cell_type": "markdown", "id": "f350b263", "metadata": {}, "source": [ "# Breakwater blocks\n", "\n", "\n", "## Problem\n", "It is desired to design the cheapest breakwater block (box-shaped). The price of the brick will depend only on the amount of used material. It is required that each face has a minimum surface of $0.8$ $\\text{m}^2$. Also, for stability reasons, the block weight has to be larger than $3000$ $\\text{kg}$. Let’s assume concrete density of $2500$ $\\text{kg}/{m}^2$." ] }, { "cell_type": "markdown", "id": "56fc737c", "metadata": {}, "source": [ "## Model\n", "\n", "We need to define our model in the form of a nonlinear constrained optimization model to apply `scipy.optimize.minimize`.\n", "\n", "We'll define the model as follows:\n", "- Design variables: width, height and depth of a block\n", "- Objective function: minimum volume of the block\n", "- Inequality constraint functions: minimum surface area of each face of $0.8$ $\\text{m}^2$ and maximum weight of $3000$ $\\text{kg}$\n", "- Equality constraint functions: none\n", "- Bounds: positive dimensions\n", "\n", "### Design variables\n", "Let's start with our design variables. In this case a logical choice could be the width, height and depth of our block\n", "\n", "$$\n", "x=\\left[ \\begin{matrix}\n", " {{x}_{width}} \\\\\n", " {{x}_{depth}} \\\\\n", " {{x}_{height}} \\\\\n", "\\end{matrix} \\right]=\\left[ \\begin{matrix}\n", " {{x}_{1}} \\\\\n", " {{x}_{2}} \\\\\n", " {{x}_{3}} \\\\\n", "\\end{matrix} \\right]\n", "$$\n", "\n", "### Objective function\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", "$$ \\mathop {\\min }\\limits_x f\\left(x\\right) = x_1 \\cdot x_2 \\cdot x_3 $$" ] }, { "cell_type": "markdown", "id": "cee945ed", "metadata": {}, "source": [ "### Inequality constraints\n", "\n", "Let's continue with the inequality constraints, which should deal with the required positive dimensions, minimum surface area of each face of $0.8$ $\\text{m}^2$ and maximum weight of $3000$ $\\text{kg}$. These can be defined in the form ${{g}}\\left(x_{ij}\\right) \\le 0$ as:\n", "\n", "$$\n", "g_1\\left(x\\right) = -x_{1} \\cdot x_2 + 0.8 \\\\\n", "g_2\\left(x\\right) = -x_{2} \\cdot x_3 + 0.8 \\\\\n", "g_3\\left(x\\right) = -x_{1} \\cdot x_3 + 0.8 \\\\\n", "g_4\\left(x\\right) = -x_{1} \\cdot x_2 \\cdot x_3 \\cdot 2500 + 3000 \\\\\n", "$$\n", "\n", "\n", "" ] }, { "cell_type": "markdown", "id": "86c8ca56", "metadata": {}, "source": [ "### Bounds\n", "\n", "The dimensions of the block cannot be negative. Therefore, the bounds can be defined as:\n", "\n", "$$\n", "0<{{x}_{i}}\\text{ } i=1,2,3\n", "$$" ] }, { "cell_type": "markdown", "id": "242cdb5a", "metadata": {}, "source": [ "### Find best solution manually\n", "\n", "Try and adjust the values for $x_1$, $x_2$ and $x_3$. Can you find the optimal solution?" ] }, { "cell_type": "code", "execution_count": 1, "id": "8893850b", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "c88d2a133b934d6caa764252d6b826f0", "version_major": 2, "version_minor": 0 }, "text/plain": [ "interactive(children=(FloatSlider(value=2.0, description='x_1', max=5.0, step=0.01), FloatSlider(value=2.0, de…" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "### Find solution manually\n", "\n", "from ipywidgets import widgets, interact\n", "import numpy as np\n", "\n", "def func(x):\n", " vol = x[0]*x[1]*x[2]\n", " return vol\n", "\n", "def nonlinconfun(x):\n", " c1 = 0.8 - x[0]*x[1]\n", " c2 = 0.8 - x[0]*x[2]\n", " c3 = 0.8 - x[1]*x[2]\n", " c4 = 3000 - 2500 * x[0] * x[1] * x[2]\n", " return np.array([c1,c2,c3,c4])\n", "\n", "def eval(x_1,x_2,x_3):\n", " x = np.array([x_1,x_2,x_3]) \n", " print(\"Objective function: \", round(func(x),3),\"m^3\")\n", " cons = nonlinconfun(x)\n", " if cons[0] < 0:\n", " print(\"Constraint functions 1: \", round(cons[0],2),\"m^2\",\"πŸ‘\")\n", " else:\n", " print(\"Constraint functions 1: \", round(cons[0],2),\"m^2\",\"πŸ‘Ž\")\n", " if cons[1] < 0:\n", " print(\"Constraint functions 2: \", round(cons[1],2),\"m^2\",\"πŸ‘\")\n", " else:\n", " print(\"Constraint functions 2: \", round(cons[1],2),\"m^2\",\"πŸ‘Ž\")\n", " if cons[2] < 0:\n", " print(\"Constraint functions 3: \", round(cons[2],2),\"m^2\",\"πŸ‘\")\n", " else:\n", " print(\"Constraint functions 3: \", round(cons[2],2),\"m^2\",\"πŸ‘Ž\")\n", " if cons[3] < 0:\n", " print(\"Constraint functions 4: \", round(cons[3]),\"kg\",\"πŸ‘\")\n", " else:\n", " print(\"Constraint functions 4: \", round(cons[3]),\"kg\",\"πŸ‘Ž\")\n", " return\n", "\n", "interact(eval,\n", " \n", " x_1 = widgets.FloatSlider(min=0, max=5, value=2, step=0.01, description=\"x_1\"),\n", " x_2 = widgets.FloatSlider(min=0, max=5, value=2, step=0.01, description=\"x_2\"),\n", " x_3 = widgets.FloatSlider(min=0, max=5, value=2, step=0.01, description=\"x_3\"));" ] }, { "cell_type": "markdown", "id": "2a609af6", "metadata": {}, "source": [ "## Method\n", "\n", "Now let's solve this problem using an optimization method." ] }, { "cell_type": "markdown", "id": "e567e5b5", "metadata": {}, "source": [ "### Import libraries" ] }, { "cell_type": "code", "execution_count": 5, "id": "2871c599", "metadata": {}, "outputs": [], "source": [ "import scipy as sp \n", "import numpy as np" ] }, { "cell_type": "markdown", "id": "c1e8ff92", "metadata": {}, "source": [ "### Define variables\n", "As before, we don't need to specify our variable $x$ itself. However, this optimization method requires an initial guess. An arbitrary value is chosen here:" ] }, { "cell_type": "code", "execution_count": 6, "id": "a7a3e663", "metadata": {}, "outputs": [], "source": [ "x0 = np.array([5,0,1])" ] }, { "cell_type": "markdown", "id": "1333c1bb", "metadata": {}, "source": [ "### Define objective function\n", "\n", "The objective function gives:" ] }, { "cell_type": "code", "execution_count": 7, "id": "c753c482", "metadata": {}, "outputs": [], "source": [ "def func(x):\n", " vol = x[0]*x[1]*x[2]\n", " return vol" ] }, { "cell_type": "markdown", "id": "7d93884f", "metadata": {}, "source": [ "### Define constrain functions" ] }, { "cell_type": "markdown", "id": "b18490c9", "metadata": {}, "source": [ " We had no equality constraints. Unlike before with linear constrained problems, we need an object which defines the upper and lower bounds. As this problem has only an upper bound of $0$, the lower bound is set to $\\infty$ which is `np.inf` in python. Note that a single constraint object can include multiple constraints." ] }, { "cell_type": "code", "execution_count": 8, "id": "fb2a2361", "metadata": {}, "outputs": [], "source": [ "def nonlinconfun(x):\n", " c1 = 0.8 - x[0]*x[1]\n", " c2 = 0.8 - x[0]*x[2]\n", " c3 = 0.8 - x[1]*x[2]\n", " c4 = 3000 - 2500 * x[0] * x[1] * x[2]\n", " return np.array([c1,c2,c3,c4])\n", "\n", "cons = sp.optimize.NonlinearConstraint(nonlinconfun, np.array([-np.inf,-np.inf,-np.inf,-np.inf]), np.array([0,0,0,0]))" ] }, { "cell_type": "markdown", "id": "0b45073a", "metadata": {}, "source": [ "### Define bounds\n", "The bounds result in:" ] }, { "cell_type": "code", "execution_count": 9, "id": "42656ee2", "metadata": {}, "outputs": [], "source": [ "bounds = [[0, None],\n", " [0, None],\n", " [0, None]]" ] }, { "cell_type": "markdown", "id": "76ddef0f", "metadata": {}, "source": [ "### Solve the problem" ] }, { "cell_type": "markdown", "id": "18dba09c", "metadata": {}, "source": [ "Now let's solve the problem. The `cons` object can be added directly, in the case of equality constraints as well you can define a list of constrainer objects as an input." ] }, { "cell_type": "code", "execution_count": 10, "id": "d2d014c1", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " message: Optimization terminated successfully\n", " success: True\n", " status: 0\n", " fun: 1.2000000000024624\n", " x: [ 1.353e+00 1.488e+00 5.962e-01]\n", " nit: 10\n", " jac: [ 8.871e-01 8.065e-01 2.013e+00]\n", " nfev: 41\n", " njev: 10\n" ] } ], "source": [ "result = sp.optimize.minimize(fun = func,x0 = x0,bounds = bounds,constraints=cons)\n", "print(result)" ] }, { "cell_type": "markdown", "id": "702e8343", "metadata": {}, "source": [ "## Exercise\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.9.18" } }, "nbformat": 4, "nbformat_minor": 5 }