A Basic AMPL Model#
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.
This notebook introduces basic concepts of AMPL needed to formulate and solve the production planning problem introduced in a companion notebook:
Variables
Objectives
Constraints
Solving
Reporting the solution
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.
Preliminary Step: Install AMPL and Python tools#
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.
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.
# install dependencies
%pip install -q amplpy matplotlib pandas
Step 1. Import AMPL#
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:
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.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.
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.
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.
from amplpy import AMPL, ampl_notebook
ampl = ampl_notebook(
modules=["cbc", "highs"], # modules to install
license_uuid="default", # license to use
) # instantiate AMPL object and register magics
Using default Community Edition License for Colab. Get yours at: https://ampl.com/ce
Licensed to AMPL Community Edition License for the AMPL Model Colaboratory (https://colab.ampl.com).
Step 2. Decision variables#
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.
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.
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.
%%ampl_eval
# define decision variables
reset;
var xM >= 0;
var xA >= 0, <= 80;
var xB >= 0, <= 100;
var yU >= 0, <= 40;
var yV >= 0;
Step 3. Objective#
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.)
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.
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.
%%ampl_eval
# define objective function
maximize Profit:
270*yU + 210*yV - 10*xM - 50*xA - 40*xB;
Step 4. Constraints#
AMPL specifies constraints in much the same way as the objective, with just a few differences:
An AMPL constraint definition begins with the keywords
subject to
.The expression for a constraint contains a less-than (
<=
), greater-than (>=
), or equals (=
) operator.
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.
(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.)
%%ampl_eval
# define constraints
subj to raw_materials: 10*yU + 9*yV <= xM;
subj to labor_A: 2*yU + 1*yV <= xA;
subj to labor_B: 1*yU + 1*yV <= xB;
Step 5. Solving#
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.
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.
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:
The AMPL commands
show
andexpand
display the model components and expressions, so that you can check that they have been specified correctly. We use theamplpy.AMPL.eval
function to run these commands in AMPL.The
amplpy.AMPL.option
attribute sets the"solver"
option to one of the solvers that we loaded in Step 2.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.
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.
# exhibit the model that has been built
ampl.eval("show;")
ampl.eval("expand;")
# solve using two different solvers
ampl.option["solver"] = "cbc"
ampl.solve()
ampl.option["solver"] = "highs"
ampl.solve()
variables: xA xB xM yU yV
constraints: labor_A labor_B raw_materials
objective: Profit
maximize Profit:
-10*xM - 50*xA - 40*xB + 270*yU + 210*yV;
subject to raw_materials:
-xM + 10*yU + 9*yV <= 0;
subject to labor_A:
-xA + 2*yU + yV <= 0;
subject to labor_B:
-xB + yU + yV <= 0;
cbc 2.10.7: cbc 2.10.7: optimal solution; objective 2400
0 simplex iterations
HiGHS 1.5.1: HiGHS 1.5.1: optimal solution; objective 2400
0 simplex iterations
0 barrier iterations
Step 6. Reporting the solution#
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 notebook.
Accessing solution values with display
#
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.
# display a component of the model
ampl.display("Profit")
ampl.display("270*yU + 210*yV", "10*xM + 50*xA + 40*xB")
ampl.display("_varname", "_var")
Profit = 2400
270*yU + 210*yV = 16800
10*xM + 50*xA + 40*xB = 14400
: _varname _var :=
1 xM 720
2 xA 80
3 xB 80
4 yU 0
5 yV 80
;
Accessing solution values with get_value
#
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.
print(f" Profit = {ampl.get_value('Profit'): 9.2f}")
print(f"Revenue = {ampl.get_value('270*yU + 210*yV'): 9.2f}")
print(f" Cost = {ampl.get_value('10*xM + 50*xA + 40*xB'): 9.2f}")
Profit = 2400.00
Revenue = 16800.00
Cost = 14400.00
Creating reports with Pandas and Matplotlib#
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.
import pandas as pd
# create pandas series for production and raw materials
production = pd.Series(
{
"U": ampl.get_value("yU"),
"V": ampl.get_value("yV"),
}
)
raw_materials = pd.Series(
{
"A": ampl.get_value("xA"),
"B": ampl.get_value("xB"),
"M": ampl.get_value("xM"),
}
)
# display pandas series
display(production)
display(raw_materials)
U 0.0
V 80.0
dtype: float64
A 80.0
B 80.0
M 720.0
dtype: float64
import matplotlib.pyplot as plt
# create grid of subplots
fig, ax = plt.subplots(1, 2, figsize=(8, 2))
# show pandas series as horizontal bar plots
production.plot(ax=ax[0], kind="barh", title="Production")
raw_materials.plot(ax=ax[1], kind="barh", title="Raw Materials")
# show vertical axis in descending order
ax[0].invert_yaxis()
ax[1].invert_yaxis()
Appendix???#
Mention .mod file (model isolation) and pure Python execution?
%%writefile production_planning_basic.mod
# decision variables
var x_M >= 0;
var x_A >= 0, <= 80;
var x_B >= 0, <= 100;
var y_U >= 0, <= 40;
var y_V >= 0;
# auxiliary variables
var revenue = 270 * y_U + 210 * y_V;
var cost = 10 * x_M + 50 * x_A + 40 * x_B;
# objective
maximize profit: revenue - cost;
# constraints
s.t. raw_materials: 10 * y_U + 9 * y_V <= x_M;
s.t. labor_A: 2 * y_U + 1 * y_V <= x_A;
s.t. labor_B: 1 * y_U + 1 * y_V <= x_B;
Overwriting production_planning_basic.mod
# Create AMPL instance and load the model
ampl = AMPL()
ampl.read("production_planning_basic.mod")
# Select a solver and solve the problem
ampl.option["solver"] = SOLVER
ampl.solve()
cbc 2.10.7: cbc 2.10.7: optimal solution; objective 2400
0 simplex iterations