{ "cells": [ { "cell_type": "markdown", "metadata": { "id": "AVkGJGu-Ob6L" }, "source": [ "# A Basic AMPL Model\n", "\n", "AMPL is an algebraic modeling language for mathematical optimization that integrates with the Python programming environment. It enables users to define optimization models consisting of decision variables, objective functions, and constraints, and to compute solutions using a variety of open-source and commercial solvers.\n", "\n", "This notebook introduces basic concepts of AMPL needed to formulate and solve the [production planning problem](https://github.com/fdabrandao/MO-book-with-AMPL/blob/dev/notebooks/01/production-planning.ipynb) introduced in a companion notebook:\n", "\n", "* Variables\n", "* Objectives\n", "* Constraints\n", "* Solving\n", "* Reporting the solution\n", "\n", "The AMPL model shown in this notebook is a direct translation of the mathematical model into basic AMPL components. In this approach, parameter values from the mathematical model are included directly in the AMPL model for simplicity. This method works well for problems with a small number of decision variables and constraints, but it limits the reuse of the model. Another notebook will demonstrate AMPL features for writing models for more generic, \"data-driven\" applications." ] }, { "cell_type": "markdown", "metadata": { "id": "e3JrgUKrbV01" }, "source": [ "## Preliminary Step: Install AMPL and Python tools\n", "\n", "We start by installing amplpy, the application programming interface (or API) that integrates the AMPL modeling language with the Python programming language. Also we install two Python utilities, matplotlib and pandas, that will be used in the parts of this notebook that display results.\n", "\n", "These installations need to be done only once for each Python environment on a personal computer. A new installation must be done for each new Colab session, however." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "4AYeavKO4ze7", "outputId": "42b98818-a526-4901-9ade-83c7f633b955", "tags": [] }, "outputs": [], "source": [ "# install dependencies\n", "%pip install -q amplpy matplotlib pandas" ] }, { "cell_type": "markdown", "metadata": { "id": "aUVhMnK0bV06" }, "source": [ "## Step 1. Import AMPL\n", "\n", "The first step for a new AMPL model is to import the needed components into the AMPL environment. The Python code shown below is used at the beginning of every notebook. There are just two elements that may vary according to your needs:\n", "\n", "* The `modules` line lists the solvers that you intend to use. In the present notebook we list `\"cbc\"` and `\"highs\"` to request the free CBC and HiGHS solvers.\n", "\n", "* The `license_uuid` is a code that determines your permissions to use AMPL and commercial solvers. Here the UUID is set to `\"default\"` which gets you full-featured versions of AMPL and most popular solvers, limited only by the size of the problems that can be solved; this is sufficient for all of our small examples.\n", "\n", "If you have obtained a different license for AMPL and one or more solvers, you can get its UUID from your account on the AMPL Portal. If you are using these notebooks in a course, your instructor may give you a UUID to use.\n", "\n", "This step also creates a new object, named `ampl`, that will be referenced when you want to refer to various components and methods of the AMPL system within Python statements." ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "8HlRikx1bV07", "outputId": "770b0a32-69a3-45cc-d6b9-134537c3f37c", "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Using default Community Edition License for Colab. Get yours at: https://ampl.com/ce\n", "Licensed to AMPL Community Edition License for the AMPL Model Colaboratory (https://colab.ampl.com).\n" ] } ], "source": [ "from amplpy import AMPL, ampl_notebook\n", "\n", "ampl = ampl_notebook(\n", " modules=[\"cbc\", \"highs\"], # modules to install\n", " license_uuid=\"default\", # license to use\n", ") # instantiate AMPL object and register magics" ] }, { "cell_type": "markdown", "metadata": { "id": "r8-CieFJbV1U" }, "source": [ "## Step 2. Decision variables\n", "\n", "To define the decision variables of your model, you use AMPL `var` statements. Our basic production planning model has 5 decision variables, so they can be defined by 5 `var` statements.\n", "\n", "Each statement starts with the keyword `var` and a unique name for the variable. Then lower and upper bounds for the variable are specified by `>=` and `<=` phrases. Bounds are optional; in this model, `>= 0` is specified for all 5 variables, but some of them have no specified upper bound.\n", "\n", "The `%%ampl_eval` line at the beginning of this cell tells Python to send all of the statements in the cell to AMPL. To add an AMPL comment line, put a `#` character at the beginning of the line." ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "id": "HwlpibttbV1V", "tags": [] }, "outputs": [], "source": [ "%%ampl_eval\n", "# define decision variables\n", "\n", "reset;\n", "\n", "var xM >= 0;\n", "var xA >= 0, <= 80;\n", "var xB >= 0, <= 100;\n", "\n", "var yU >= 0, <= 40;\n", "var yV >= 0;" ] }, { "cell_type": "markdown", "metadata": { "id": "7-yczIMEbV17" }, "source": [ "## Step 3. Objective\n", "\n", "Since this is a maximization problem, an AMPL `maximize` statement specifies the objective function. (If it were a minimization, a `minimize` statement would be used instead.)\n", "\n", "The `maximize` keyword is followed by a name for the objective — we can call it `Profit` — and a colon (`:`) character. Then the expression for the objective function is written in AMPL just the same as we previously wrote it in the mathematical formulation.\n", "\n", "Notice that all AMPL statements are ended with a semicolon (`;`) character. A long statement can be spread over more than one line, as in the case of our `maximize` statement for this model." ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "id": "RX_zc5rjbV18", "tags": [] }, "outputs": [], "source": [ "%%ampl_eval\n", "# define objective function\n", "\n", "maximize Profit:\n", " 270*yU + 210*yV - 10*xM - 50*xA - 40*xB;" ] }, { "cell_type": "markdown", "metadata": { "id": "J6d-mWSQbV1_" }, "source": [ "## Step 4. Constraints\n", "\n", "AMPL specifies constraints in much the same way as the objective, with just a few differences:\n", "\n", "* An AMPL constraint definition begins with the keywords `subject to`.\n", "\n", "* The expression for a constraint contains a less-than (`<=`), greater-than (`>=`), or equals (`=`) operator.\n", "\n", "Just as for the objective, each constraint has a name, and the expressions for the three constraints are the same as in the mathematical formulation.\n", "\n", "(You can abbreviate `subject to` as `subj to` or `s.t.` Or you can leave it out entirely; a statement that does not begin with a keyword is assumed by AMPL to be a constraint definition.)\n", "\n" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "id": "ZC_SnWUNbV2B", "tags": [] }, "outputs": [], "source": [ "%%ampl_eval\n", "# define constraints\n", "\n", "subj to raw_materials: 10*yU + 9*yV <= xM;\n", "subj to labor_A: 2*yU + 1*yV <= xA;\n", "subj to labor_B: 1*yU + 1*yV <= xB;" ] }, { "cell_type": "markdown", "metadata": { "id": "WRjeJmzfbV2C" }, "source": [ "## Step 5. Solving\n", "\n", "With the model now fully specified, the next step is to compute optimal values for the decision variables. From this point onward, our cells will contain Python statements and function calls, including calls to amplpy functions that manage the integration of AMPL and Python.\n", "\n", "There are many ways to assign values to the variables, so that all of the bounds and all of the constraints are satisfied. Each of these ways gives a *feasible* solution for our model. A solution is *optimal* if it gives the highest possible value of the objective function among all the feasible solutions.\n", "\n", "To compute an optimal solution, we call a *solver:* a separate program that applies numerical algorithms to determine optimal values for the variables. There are just a few steps:\n", "\n", "* The AMPL commands `show` and `expand` display the model components and expressions, so that you can check that they have been specified correctly. We use the `amplpy.AMPL.eval` function to run these commands in AMPL.\n", "\n", "* The `amplpy.AMPL.option` attribute sets the `\"solver\"` option to one of the solvers that we loaded in Step 2.\n", "\n", "* The `amplpy.AMPL.solve` function invokes the chosen solver. AMPL takes care of converting the model to the form that the solver requires for its computation, and converting the results back to a form that can be used in Python.\n", "\n", "To show how different solvers can be tested, we conclude by setting a different solver and solving again. As expected, the two solvers report the same optimal objective value, 2400." ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "Es_J3gd6bV2C", "outputId": "ec830ed4-e1dd-4168-d594-d17b48c1b299", "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "variables: xA xB xM yU yV\n", "\n", "constraints: labor_A labor_B raw_materials\n", "\n", "objective: Profit\n", "maximize Profit:\n", "\t-10*xM - 50*xA - 40*xB + 270*yU + 210*yV;\n", "\n", "subject to raw_materials:\n", "\t-xM + 10*yU + 9*yV <= 0;\n", "\n", "subject to labor_A:\n", "\t-xA + 2*yU + yV <= 0;\n", "\n", "subject to labor_B:\n", "\t-xB + yU + yV <= 0;\n", "\n", "cbc 2.10.7: \b\b\b\b\b\b\b\b\b\b\b\bcbc 2.10.7: optimal solution; objective 2400\n", "0 simplex iterations\n", "HiGHS 1.5.1: HiGHS 1.5.1: optimal solution; objective 2400\n", "0 simplex iterations\n", "0 barrier iterations\n" ] } ], "source": [ "# exhibit the model that has been built\n", "ampl.eval(\"show;\")\n", "ampl.eval(\"expand;\")\n", "\n", "# solve using two different solvers\n", "ampl.option[\"solver\"] = \"cbc\"\n", "ampl.solve()\n", "\n", "ampl.option[\"solver\"] = \"highs\"\n", "ampl.solve()" ] }, { "cell_type": "markdown", "metadata": { "id": "MZDcujpLbV2D" }, "source": [ "## Step 6. Reporting the solution\n", "\n", "The final step in most applications is to report the solution in a suitable format. For this example, we demonstrate simple tabular and graphic reports using the Pandas library. For an overview of other ways to report and visualize the solutions, see also the appendix of the [gasoline-distribution](https://github.com/fdabrandao/MO-book-with-AMPL/tree/dev/notebooks/04/gasoline-distributon.ipynb) notebook." ] }, { "cell_type": "markdown", "metadata": { "id": "BbCuGda0bV2D" }, "source": [ "### Accessing solution values with `display`\n", "\n", "The amplpy `ampl.display` function shows the values of one or more AMPL expressions, computed from the current values of the variables. You can also apply this function to special expresions such as `_var` for the values of all the variables, and `_varname` for the names of all the variables." ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "6ZzrTlsRbV2E", "outputId": "deba6b63-8e69-4f03-a854-a8d60f4d945f" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Profit = 2400\n", "\n", "270*yU + 210*yV = 16800\n", "10*xM + 50*xA + 40*xB = 14400\n", "\n", ": _varname _var :=\n", "1 xM 720\n", "2 xA 80\n", "3 xB 80\n", "4 yU 0\n", "5 yV 80\n", ";\n", "\n" ] } ], "source": [ "# display a component of the model\n", "ampl.display(\"Profit\")\n", "ampl.display(\"270*yU + 210*yV\", \"10*xM + 50*xA + 40*xB\")\n", "\n", "ampl.display(\"_varname\", \"_var\")" ] }, { "cell_type": "markdown", "metadata": { "id": "sHsa8Jp-bV2F" }, "source": [ "### Accessing solution values with `get_value`\n", "\n", "After an optimal solution has been successfully computed, the value of an AMPL expression can be retrieved in Python by use of the `ampl.get_value` function. When combined with Python `f` strings, `ampl.get_value` provides a convenient means of creating formatted reports." ] }, { "cell_type": "code", "execution_count": 46, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "qeigwJQkbV2I", "outputId": "1a642d79-ec8f-46c6-fe45-ec0b0d2a4319" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " Profit = 2400.00\n", "Revenue = 16800.00\n", " Cost = 14400.00\n" ] } ], "source": [ "print(f\" Profit = {ampl.get_value('Profit'): 9.2f}\")\n", "print(f\"Revenue = {ampl.get_value('270*yU + 210*yV'): 9.2f}\")\n", "print(f\" Cost = {ampl.get_value('10*xM + 50*xA + 40*xB'): 9.2f}\")" ] }, { "cell_type": "markdown", "metadata": { "id": "reZQ-4bHbV2L" }, "source": [ "### Creating reports with Pandas and Matplotlib\n", "\n", "Pandas, a freely available library for working with data in Python, is widely used in the data science community. Here we use a Pandas `Series()` object to hold and display solution data. We can then visualize the data using the matplotlib library, for instance with a bar chart." ] }, { "cell_type": "code", "execution_count": 40, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 139 }, "id": "R4PQ1pLobV2O", "outputId": "68efc530-a1cd-4aee-e332-f608b7e37732" }, "outputs": [ { "data": { "text/plain": [ "U 0.0\n", "V 80.0\n", "dtype: float64" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "A 80.0\n", "B 80.0\n", "M 720.0\n", "dtype: float64" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import pandas as pd\n", "\n", "# create pandas series for production and raw materials\n", "production = pd.Series(\n", " {\n", " \"U\": ampl.get_value(\"yU\"),\n", " \"V\": ampl.get_value(\"yV\"),\n", " }\n", ")\n", "\n", "raw_materials = pd.Series(\n", " {\n", " \"A\": ampl.get_value(\"xA\"),\n", " \"B\": ampl.get_value(\"xB\"),\n", " \"M\": ampl.get_value(\"xM\"),\n", " }\n", ")\n", "\n", "# display pandas series\n", "display(production)\n", "display(raw_materials)" ] }, { "cell_type": "code", "execution_count": 41, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 237 }, "id": "oy61QNFgbV2Q", "outputId": "918746a8-4783-494a-c87c-a4dd23eb040d" }, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAApMAAADcCAYAAAAshyptAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAkjklEQVR4nO3deXhU5d3/8c9MIpOEkASSQIyExSBEWSqyWMISrNQEIhSkVAL0IUDtA7KIWhQeWhDEBi+12laF61FIKsgWHwSLCzsYENkUkMXIbkCWImRBQmIy9+8PfxkZkkByCDNA3q/rmgtyn3vmfOdWvnw458wZmzHGCAAAALDA7u0CAAAAcPMiTAIAAMAywiQAAAAsI0wCAADAMsIkAAAALCNMAgAAwDLCJAAAACwjTAIAAMAywiQAAAAsI0zCq2w2m5577jmP73fdunWy2Wxat26dx/cNANWV1Z5Pz76xESarubS0NNlsNtfDz89PTZs21ahRo3Tq1Clvl3fN3nzzTaWlpXm7DAA3gcv7oa+vr+644w4lJyfr+PHj3i5PR44ccdU2bdq0MucMHDhQNptNgYGBlvYxb948vfbaa9dQJaojX28XgBvD1KlT1bhxY128eFEbNmzQjBkz9NFHH2n37t0KCAjwdnmWvfnmmwoLC1NycrLbeJcuXZSfn68aNWp4pzAAN6xL++Hnn3+utLQ0bdiwQbt375afn5+3y5Ofn5/mz5+vP//5z27jP/zwg5YuXXpNNc6bN0+7d+/W2LFjr7HKsuXn58vXl+hxq+HIJCRJ3bt316BBg/SHP/xBaWlpGjt2rA4fPqylS5eWOf+HH37wcIVVy263y8/PT3Y7fwQAuLu0H7799tv605/+pIMHD+qDDz7wdmmSpB49emjv3r3auXOn2/jSpUtVWFioX//6116qrGxOp1MXL16U9FMQJkzeevibFGX61a9+JUk6fPiwkpOTFRgYqIMHD6pHjx6qVauWBg4cKOmnUPn0008rKipKDodDzZo108svvyxjjNvrFRQU6Mknn1R4eLhq1aqlXr166dixY6X2m5ycrEaNGpUaf+6552Sz2UqNz507V+3bt1dAQIBq166tLl26aMWKFZKkRo0aac+ePVq/fr3r1FDXrl0llX/9TXp6utq0aSN/f3+FhYVp0KBBpU5vlazH8ePH1bt3bwUGBio8PFx/+tOfVFxcXKH1BXDz6Ny5syTp4MGDrrHCwkJNmjRJbdq0UXBwsGrWrKnOnTtr7dq1bs+977779Mgjj7iNtWzZUjabTbt27XKNLVy4UDabTfv27btqPR06dFDjxo01b948t/F3331XCQkJqlOnTqnnLF26VImJiYqMjJTD4VB0dLSef/55t57VtWtXffjhhzp69KirZ17ajwsKCjR58mQ1adJEDodDUVFReuaZZ1RQUOC2L5vNplGjRundd99V8+bN5XA49Mknn7i2XXrN5NGjR/X444+rWbNm8vf3V2hoqPr166cjR45cdR3279+vvn37KiIiQn5+fqpfv7769++vnJycqz4XVYt/HqBMJU0zNDRUklRUVKT4+Hh16tRJL7/8sgICAmSMUa9evbR27VoNGzZM9957r5YvX65x48bp+PHjevXVV12v94c//EFz587VgAEDFBsbqzVr1igxMfGaapwyZYqee+45xcbGaurUqapRo4Y2b96sNWvW6KGHHtJrr72m0aNHKzAwUBMnTpQk1atXr9zXS0tL05AhQ9SuXTulpKTo1KlT+vvf/66NGzfqyy+/VEhIiGtucXGx4uPjdf/99+vll1/WqlWr9Morryg6OlojRoy4pvcF4MZSEmxq167tGsvNzdXbb7+tpKQkPfbYY8rLy9OsWbMUHx+vLVu26N5775X0UxCdP3++63lnz57Vnj17ZLfblZGRoVatWkmSMjIyFB4errvvvrtCNSUlJWnu3LmaPn26bDabzpw5oxUrVmjOnDmu4HaptLQ0BQYG6qmnnlJgYKDWrFmjSZMmKTc3Vy+99JIkaeLEicrJydGxY8dc/bvk2kun06levXppw4YN+uMf/6i7775bX331lV599VV98803WrJkidv+1qxZo0WLFmnUqFEKCwsr8yCBJG3dulWfffaZ+vfvr/r16+vIkSOaMWOGunbtqr1795Z7mVVhYaHi4+NVUFCg0aNHKyIiQsePH9eyZcuUnZ2t4ODgCq0jqohBtZaammokmVWrVpn//Oc/JisryyxYsMCEhoYaf39/c+zYMTN48GAjyYwfP97tuUuWLDGSzLRp09zGf/vb3xqbzWYOHDhgjDFmx44dRpJ5/PHH3eYNGDDASDKTJ092jQ0ePNg0bNiwVJ2TJ082l/7vun//fmO3202fPn1McXGx21yn0+n6ffPmzU1cXFyp11u7dq2RZNauXWuMMaawsNDUrVvXtGjRwuTn57vmLVu2zEgykyZNcqtRkpk6darba7Zu3dq0adOm1L4A3BzK6ofvvfeeCQ8PNw6Hw2RlZbnmFhUVmYKCArfnnzt3ztSrV88MHTrUNZaenm4kmb179xpjjPnggw+Mw+EwvXr1Mo8++qhrXqtWrUyfPn2uWN/hw4eNJPPSSy+Z3bt3G0kmIyPDGGPMG2+8YQIDA80PP/xgBg8ebGrWrOn23AsXLpR6vf/+7/82AQEB5uLFi66xxMTEMnvwnDlzjN1ud+2vxMyZM40ks3HjRteYJGO3282ePXtKvc7lPb+sujZt2mQkmXfeecc1dnnP/vLLL40kk56eXur58DxOc0OS1K1bN4WHhysqKkr9+/dXYGCg3n//fd1xxx2uOZcfcfvoo4/k4+OjMWPGuI0//fTTMsbo448/ds2TVGretVzgvWTJEjmdTk2aNKnUdY9lnQ6/mm3btun06dN6/PHH3S5eT0xMVExMjD788MNSzxk+fLjbz507d9ahQ4cqvW8AN5ZL++Fvf/tb1axZUx988IHq16/vmuPj4+P6AJ/T6dTZs2dVVFSktm3b6osvvnDNKzlF/umnn0r66Qhku3bt9Otf/1oZGRmSpOzsbO3evds1tyKaN2+uVq1auY56zps3T7/5zW/KPZLn7+/v+n1eXp7OnDmjzp0768KFC/r666+vur/09HTdfffdiomJ0ZkzZ1yPkkuiLj+9HxcXp3vuueeqr3tpXT/++KO+//57NWnSRCEhIW7reLmSI4/Lly/XhQsXrrofXF+ESUiS3njjDa1cuVJr167V3r17dejQIcXHx7u2+/r6ujVS6adrXSIjI1WrVi238ZLTNEePHnX9arfbFR0d7TavWbNmlus9ePCg7HZ7hZpVRZTUWlZNMTExru0l/Pz8FB4e7jZWu3ZtnTt3rkrqAeA9Jf3wvffeU48ePXTmzBk5HI5S8/71r3+pVatW8vPzU2hoqMLDw/Xhhx+6XbNXr1493XXXXa7gmJGRoc6dO6tLly767rvvdOjQIW3cuFFOp7NSYVKSBgwYoPT0dB04cECfffaZBgwYUO7cPXv2qE+fPgoODlZQUJDCw8M1aNAgSarQNYb79+/Xnj17FB4e7vZo2rSpJOn06dNu8xs3blyh95Cfn69Jkya5rrsPCwtTeHi4srOzr1hX48aN9dRTT+ntt99WWFiY4uPj9cYbb3C9pJdwzSQkSe3bt1fbtm3L3e5wODzyyefyjireaB9s8fHx8XYJAK6TS/th79691alTJw0YMECZmZmuawjnzp2r5ORk9e7dW+PGjVPdunXl4+OjlJQUtw/qSFKnTp20evVq5efna/v27Zo0aZJatGihkJAQZWRkaN++fQoMDFTr1q0rVWdSUpImTJigxx57TKGhoXrooYfKnJedna24uDgFBQVp6tSpio6Olp+fn7744gs9++yzcjqdV92X0+lUy5Yt9be//a3M7VFRUW4/X3rE8UpGjx6t1NRUjR07Vh06dFBwcLBsNpv69+9/1bpeeeUVJScna+nSpVqxYoXGjBmjlJQUff7556UOfuD6IkzCsoYNG2rVqlXKy8tzOzpZcsqkYcOGrl+dTqcOHjzoduQvMzOz1GvWrl1b2dnZpcYvPzIYHR0tp9OpvXv3ui50L0tFT3mX1JqZmek6bXNpnSXbAVQvJQHxgQce0Ouvv67x48dLkt577z3deeedWrx4sVufmTx5cqnX6Ny5s1JTU7VgwQIVFxcrNjZWdrtdnTp1coXJ2NjYSv8jtUGDBurYsaPWrVunESNGlHvLnXXr1un777/X4sWL1aVLF9f44cOHS80tr2dGR0dr586devDBBy1dSlSe9957T4MHD9Yrr7ziGrt48WKZfw+UpWXLlmrZsqX+/Oc/67PPPlPHjh01c+bMcm/qjuuD09ywrEePHiouLtbrr7/uNv7qq6/KZrOpe/fukuT69R//+IfbvLK+ZSE6Olo5OTlut8w4ceKE3n//fbd5vXv3lt1u19SpU0v969VccluimjVrVqgptW3bVnXr1tXMmTPdbnPx8ccfa9++fdf8yXMAN6+uXbuqffv2eu2111z3SywJfpf2m82bN2vTpk2lnl9y+vrFF19Uq1atXNf7de7cWatXr9a2bdsqfYq7xLRp0zR58mSNHj263Dll1VpYWKg333yz1NyaNWuWear4d7/7nY4fP6633nqr1Lb8/HzL9x728fEpdSu5f/7zn1c9G5Wbm6uioiK3sZYtW8put5e6VRGuP45MwrKePXvqgQce0MSJE3XkyBH94he/0IoVK7R06VKNHTvWdY3kvffeq6SkJL355pvKyclRbGysVq9erQMHDpR6zf79++vZZ59Vnz59NGbM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s2bNGrNt2zbToUMH06FDBy9W7TmXfrLQmOqzFlu2bDG+vr7mhRdeMPv37zfvvvuuCQgIMHPnznXNmT59ugkJCTFLly41u3btMr/5zW9uydvhDB482Nxxxx2uWwMtXrzYhIWFmWeeecY1p7qsRUVU115aoip6asntbh566CGzY8cO88knn5jw8PBb7nY3Zalsz60ua1VVPTkhIcG0bt3abN682WzYsMHcdddd3Bqoqv3zn/80DRo0MDVq1DDt27c3n3/+uadL8DhJZT5SU1Ndc/Lz883jjz9uateubQICAkyfPn3MiRMnvFe0B13e2KrTWvz73/82LVq0MA6Hw8TExJj//d//ddvudDrNX/7yF1OvXj3jcDjMgw8+aDIzM71U7fWTm5trnnjiCdOgQQPj5+dn7rzzTjNx4kS3245Ul7WoqOrYS0tUVU89cuSI6d69u/H39zdhYWHm6aefNj/++KOH343nWem51WWtqqInf//99yYpKckEBgaaoKAgM2TIEJOXl+fJt+FxNmMu+YoJAAAAoBL4bm4AAABYRpgEAACAZYRJAAAAWEaYBAAAgGWESQAAAFhGmAQAAIBlhEkAAABYRpgEAACAZYRJAAAAWEaYBAAAgGWESQAAAFhGmAQAAIBl/w/WnoX/+vHR+gAAAABJRU5ErkJggg==", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import matplotlib.pyplot as plt\n", "\n", "# create grid of subplots\n", "fig, ax = plt.subplots(1, 2, figsize=(8, 2))\n", "\n", "# show pandas series as horizontal bar plots\n", "production.plot(ax=ax[0], kind=\"barh\", title=\"Production\")\n", "raw_materials.plot(ax=ax[1], kind=\"barh\", title=\"Raw Materials\")\n", "\n", "# show vertical axis in descending order\n", "ax[0].invert_yaxis()\n", "ax[1].invert_yaxis()" ] }, { "cell_type": "markdown", "metadata": { "id": "ecm9QpoybV2R" }, "source": [ "### Appendix???\n", "Mention .mod file (model isolation) and pure Python execution?" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "yfCHxWg5bV2S", "outputId": "ad06d5a2-ad9e-42be-c6ea-7e97e38f19c5" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Overwriting production_planning_basic.mod\n" ] } ], "source": [ "%%writefile production_planning_basic.mod\n", "\n", "# decision variables\n", "var x_M >= 0;\n", "var x_A >= 0, <= 80;\n", "var x_B >= 0, <= 100;\n", "\n", "var y_U >= 0, <= 40;\n", "var y_V >= 0;\n", "\n", "# auxiliary variables\n", "var revenue = 270 * y_U + 210 * y_V;\n", "var cost = 10 * x_M + 50 * x_A + 40 * x_B;\n", "\n", "# objective\n", "maximize profit: revenue - cost;\n", "\n", "# constraints\n", "s.t. raw_materials: 10 * y_U + 9 * y_V <= x_M;\n", "s.t. labor_A: 2 * y_U + 1 * y_V <= x_A;\n", "s.t. labor_B: 1 * y_U + 1 * y_V <= x_B;" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "NGDnnW4QbV2S", "outputId": "3fee29dc-1339-42e8-a822-68235d763c04" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "cbc 2.10.7: \b\b\b\b\b\b\b\b\b\b\b\bcbc 2.10.7: optimal solution; objective 2400\n", "0 simplex iterations\n" ] } ], "source": [ "# Create AMPL instance and load the model\n", "ampl = AMPL()\n", "ampl.read(\"production_planning_basic.mod\")\n", "\n", "# Select a solver and solve the problem\n", "ampl.option[\"solver\"] = SOLVER\n", "ampl.solve()" ] } ], "metadata": { "colab": { "include_colab_link": true, "provenance": [] }, "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.10.6" }, "latex_envs": { "LaTeX_envs_menu_present": true, "autoclose": false, "autocomplete": true, "bibliofile": "biblio.bib", "cite_by": "apalike", "current_citInitial": 1, "eqLabelWithNumbers": true, "eqNumInitial": 1, "hotkeys": { "equation": "Ctrl-E", "itemize": "Ctrl-I" }, "labels_anchors": false, "latex_user_defs": false, "report_style_numbering": false, "user_envs_cfg": false } }, "nbformat": 4, "nbformat_minor": 0 }