{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Bathymetry maps\n",
"\n",
"## Problem\n",
"It is desired to determine the number of bathymetry maps $n$ of a local area that should be produced to maximize the profit of a company. The total cost of production and distribution is €$75$ per unit $n$. The revenues are proportional to the number of units multiplied by its price: $Revenues = n \\cdot Price $\n",
"\n",
"The demand depends on the price ($Price = 150 - 0.01n^2$), as shown in the graph:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"import numpy as np\n",
"import matplotlib.pylab as plt\n",
"n = np.linspace(0,120,100)\n",
"price = 150 - 0.01 * n**2\n",
"plt.plot(n,price)\n",
"plt.xlabel('$n$ (-)')\n",
"plt.ylabel('$Price$ (€)')\n",
"plt.ylim(0,160)\n",
"plt.xlim(0,120)\n",
"ax = plt.gca()\n",
"ax.grid(True, which='both')\n",
"ax.spines['left'].set_position('zero')\n",
"ax.spines['right'].set_color('none')\n",
"ax.yaxis.tick_left()\n",
"ax.spines['bottom'].set_position('zero')\n",
"ax.spines['top'].set_color('none')\n",
"ax.xaxis.tick_bottom()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The profit can be estimated as the revenues minus the total costs."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Model\n",
"The function for the profit can be found by combining the relations in the problem statement. However, this is the profit which should be maximized. To turn this into a minimization problem, the profit can be multiplied with $-1$. The final model of this problem results in:\n",
"\n",
"$$ \\mathop {\\min }\\limits_n \\left(75n - \\left( 150 - 0.01n^2 \\right) n \\right) $$"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"An approach to solve this problem might be to try out some values. You can do so in the applet below. The plot below shows the negative profit for some number of bathmetry maps sold. Try and adjust the values for $n$, the number of bathmetry maps sold. How small can you get the negative profit?"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from ipywidgets import widgets, interact\n",
"\n",
"n_range = np.linspace(0,100,100)\n",
"def func(n,truth):\n",
" fig, ax = plt.subplots(1, 1)\n",
" title = 'Negative profit for amount of bathmetry maps sold is € ' + str(round(75*n - (150 - 0.01 * n**2)*n,2))\n",
" ax.clear()\n",
" ax.set_title(title)\n",
" if truth:\n",
" ax.plot(n_range,75*n_range - (150 - 0.01 * n_range**2)*n_range)\n",
" ax.plot(n,75*n - (150 - 0.01 * n**2)*n,'o');\n",
" ax.set_xlim([0,100])\n",
" ax.set_xlabel('Number of bathmetry maps $n$ sold (-)')\n",
" ax.set_ylabel('Negative profit (€)')\n",
" ax.set_ylim([-3000,3000])\n",
" ax.spines['right'].set_color('none')\n",
" ax.spines['top'].set_color('none')\n",
" ax.spines['bottom'].set_position('zero')\n",
" ax.spines['left'].set_position('zero')\n",
" plt.draw();\n",
"\n",
"interact(func, n = widgets.IntSlider(min=0, max=100, value=20, step=1, description=\"n\"), truth = widgets.Checkbox(value=False, description='Show objective function', disabled=False));"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"As this case is only one-dimensional and the potential range of values is limited, this approach (exhaustive search) is valid: evaluating all possible values for $n$ doesn't take a lot of computing power. The resulting values from the objective function show a clear minimum. For problem in which the objective function required more computational power or the amount of dimensions of the design variables increases, this approach quickly becomes infeasible."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Method\n",
"\n",
"This model is described using `scipy.optimize.minimize` according to the standard structure in this course.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Importing libraries\n",
"For this problem, we'll use all three packages `scipy`, `numpy`, `matplotlib`."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import scipy as sp \n",
"import numpy as np\n",
"import matplotlib.pylab as plt"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Define variables\n",
"There are very few variables in this problem. In fact, the only variable we have to specify is the initial guess for the optimization algorithm. The objective function will be treated later.\n",
"The length of $n$ doesn't have to be specified."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"n0 = 20"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Define objective function\n",
"In the objective function, the formula given in the model description can be inserted. Or, each individual step can be calculated on a seperate line. Again, note that the profit is multiplied with $-1$ to maximize the profit in the minimization formulation.\n",
"This results in:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def negprofit(n):\n",
" price = 150 - 0.01 * n**2\n",
" revenues = price * n\n",
" totalcost = 75 * n\n",
" profit = revenues - totalcost\n",
" return -profit"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Solve the problem\n",
"Now, the problem can be solved. The result is stored in the variables `result` which is printed."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"result = sp.optimize.minimize(negprofit,n0)\n",
"print(result)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Postprocess results\n",
"As seen before, this problem is very small and can be solved by evaluating all possible values (or applying algebra). These values can be plotted and the optimum solution is clearly in the minimum."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"n_range = np.linspace(0,100,100)\n",
"negprofit_result = negprofit(n_range)\n",
"plt.figure()\n",
"plt.plot(n_range,negprofit_result)\n",
"plt.plot(result.x,result.fun,'o');\n",
"plt.xlabel('$n$')\n",
"plt.ylabel('Negative profit');\n",
"ax = plt.gca()\n",
"ax.spines['right'].set_color('none')\n",
"ax.spines['top'].set_color('none')\n",
"ax.spines['bottom'].set_position('zero')\n",
"ax.spines['left'].set_position('zero')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Exercise\n",
"\n",
"\n",
"\n",
""
]
}
],
"metadata": {
"kernelspec": {
"display_name": "base",
"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.5"
},
"orig_nbformat": 4
},
"nbformat": 4,
"nbformat_minor": 2
}