Frequently asked questions

This page contains a selection of the most commonly asked questions. You can also find answers to these questions, and further support in the AMPL form

General questions

AMPL is a computer language for describing production, distribution, blending, scheduling and many other kinds of problems known generally as large-scale optimization or mathematical programming.

AMPL’s familiar algebraic notation and interactive command environment are designed to help formulate models, communicate with a wide variety of solvers, and examine solutions.

AMPL’s flexibility and convenience make it ideal for rapid prototyping and development of models, while its speed and generality provide the resources required by repeated production runs.

The Excel spreadsheet package comes with solvers for linear, nonlinear, and integer programming; it accepts enhanced add-in solvers as well. Their principal advantage lies in allowing all aspects of optimization — including formulation, solution, and analysis of results — to be performed within the spreadsheet environment. In particular, variables and constraints can be defined directly in terms of ranges of spreadsheet cells.

AMPL incorporates a far richer language than spreadsheet optimizers for describing optimization problems. As a result, AMPL is much more natural and reliable for developing and maintaining complex models. The difference is particularly pronounced when many model components have more than two dimensions (or more than two subscripts or indices, in AMPL terms).

By avoiding much of the spreadsheet overhead, AMPL also generates model instances faster than spreadsheet optimizers. AMPL’s speed advantage is significant for a few thousand model components, and becomes relatively greater as problem size increases.

AMPL’s interface design encourages developers to hook additional solvers to AMPL. Thus AMPL is available with a greater variety of solvers than spreadsheet optimizers.

AMPL models can read and write spreadsheet data that is structured in the form of relational tables, through use of the read table and write table commands.

AMPL is one of several optimization modeling systems that are designed around specialized algebraic modeling languages. These languages employ adaptations of familiar mathematical notation to describe optimization problems as

  • the minimization or maximization of algebraic expressions in numerical decision variables,

  • subject to constraints expressed as equalities or inequalities between algebraic expressions in the decision variables.

Advanced interpreters and interfaces for these languages provide support for simplifying and analyzing models. Some also provide language extensions for describing algorithmic schemes that attack difficult problems by alternately solving interrelated subproblems.

Some of the other commercially distributed algebraic modeling languages are:

  • AIMMS, providing a graphical application development environment and a GAMS compatibility mode supplementing its own language.

  • GAMS, one of the first such languages, now widely used in a number of industries.

  • LINGO, a more powerful sibling of the LINDO language widely used in elementary instruction.

  • MPL, notable for its associated graphical interface and database links.

AMPL is designed to combine and extend the expressive abilities of these languages, while remaining easy to use for elementary applications. AMPL is particularly notable for the naturalness of its syntax and for the generality of its set and indexing expressions.

AMPL provides strong support for validation, verification and reporting of optimal solutions, through numerous alternatives for presenting data and results. AMPL’s extensive preprocessing routines can automatically carry out transformations that serve to reduce problem size, convert piecewise-linearities to linear terms, and substitute out selected variables.

AMPL is further distinguished by its continuing development to serve users’ needs. Recent additions include looping and testing constructs for writing executable scripts in the AMPL command language, and facilities for defining and working with several interrelated subproblems. Numerous commands have also been added for producing diagnostic reports and custom-formatted tables.

There are also many optimization modeling systems based on representations other than algebraic modeling languages. Popular alternative forms include:

  • block-schematic diagrams, which depict a linear constraint matrix as a collection of structured submatrices (or blocks).

  • activity specifications, which describe a model in terms of activities (variables) and their effects (constraints) on inputs and outputs.

  • netforms, which use graph or network diagrams to depict models involving flows and allocations.

Each of these has special advantages for certain classes of problems. In contrast, an algebraic modeling language such as AMPL is more general. It is the most natural form for many classes of problems, and is one of the most natural forms for an even broader variety of problem classes.

The AMPL book is the most comprehensive source of information on AMPL. It is written in the form of a textbook with an appendix for reference, in contrast to the more common format of a reference manual with some tutorial material at the front. Virtually all language features are illustrated by continuing, meaningful examples, and additional exercises are provided. Chapters can be freely downloaded, and hardbound printed copies can be purchased.

For working through the examples and exercises in the book, a free size-limited student edition of AMPL may be downloaded. Courses using AMPL can get time-limited but size-unlimited versions of AMPL and solvers through the AMPL for Courses program.

The free “student” version of AMPL is limited to 300 variables (500 for linear problems) and a total of 300 objectives and constraints. However all other AMPL versions have no intrinsic limit on problem size; they are limited only by the resources available to the computer that is running AMPL.

In practice, memory is most often the limiting resource. The amount of memory used by AMPL necessarily depends on the numbers of variables and constraints, and on the number and complexity of the terms in the constraints — or in the case of linear programs, on the number of nonzero coefficients in the constraints. Memory use also depends on the size and complexity of the model’s sets and parameters. AMPL’s presolve phase may require additional memory to execute (though it may accomplish problem reductions that allow savings of memory later).

AMPL’s memory use for a linear program can be estimated crudely as 1000000 + 260 (m + n) + 50 nz bytes, where m is the number of constraints, n the number of variables, and nz the number of nonzeroes. A computer having 4GB or more of memory and running a 64-bit operating system can thus typically accommodate over a million variables and/or constraints. Memory required for a solver may have to be added, however, if the solver is to be run while AMPL remains active. See also our advice elsewhere in this FAQ for further comments on what to do (including running the solver separately) if you suspect a problem with insufficient memory.

Especially difficult problems may cause your solver to encounter its own resource limitations. Solvers that accept binary and integer variables, in particular, can consume impractical amounts of time and disk space as well as memory, even for problems of apparently modest size. Nonlinear problems can also be very time-consuming compared to linear programs in the same numbers of variables and constraints.

As a practical matter, there is no substitute for experimentation to determine the resource requirements of your particular problem. The best approach is usually to try out your model on progressively larger collections of data, so that you can record the trend in resource requirements as problem size increases.

Currently AMPL is not configured as a subroutine library whose individual pieces can be called as needed from a user’s program. A “batch” session of AMPL can be executed from a program in any popular programming language, however. This feature can be used as a high-level “call” to AMPL that is sufficient for some applications.

In a C program, you would execute AMPL by use of the system procedure, as in these examples:

system("ampl <cut.run");
system("ampl <cut.run >cut.out");

The argument to system can be anything one would type on a command line. Most commonly, an input file (cut.run above) contains a series of AMPL commands that read model and data input, invoke solvers, and write results to files. This arrangement does not provide for results to be passed back directly to your program; following the call to system, your program must get the results by reading a file that AMPL has written. Other languages (such as C++, C#, Java, Python) have their own calling conventions, but the principles are the same.

Files and Preprocessing

Use AMPL’s shell command to invoke the editor you want to use. For example, if your editor is called edit and your file is diet2a.dat, give the command

shell "edit diet2a.dat";

The editor temporarily pops up (or takes over the screen or current window). When you quit the editor, the AMPL prompt reappears, and all models, data and options are as before.

When you have returned from the editor, AMPL cannot tell which files you have changed! Thus to use a changed file, you must reset and read it again. The simplest approach is to reset and re-read everything, as in this example:

ampl: shell "edit diet.mod";
ampl: reset;
ampl: model diet.mod;
ampl: data diet2a.dat;

In most circumstances, however, it’s easier to edit files in their own windows, separate from the windows where you’re running AMPL. Some editors can be configured with syntax highlighting specific to AMPL. You still need to reset and re-read after changing a file, though.

Use AMPL’s reset data command to resample from all of the random-valued functions in the model. For example, in model steel4.mod from the AMPL book, suppose that parameter avail is changed so that its value is given by a random function:

param avail_mean {STAGE} >= 0;
param avail_variance {STAGE} >= 0;
param avail {s in STAGE} :=
        Normal (avail_mean[s], avail_variance[s]);

with corresponding data

param:   avail_mean avail_variance :=
  reheat     35         5
  roll       40         2 ;

Then AMPL will take new samples from the Normal distribution after each reset data:

ampl: model steel4.mod;
ampl: data steel4.dat;
ampl: solve;
MINOS 5.4: optimal solution found.
3 iterations, objective 187632.2489
avail [*] :=
reheat  32.3504
  roll  43.038
;
ampl: reset data avail;
ampl: solve;
MINOS 5.4: optimal solution found.
4 iterations, objective 158882.901
ampl: display avail;
avail [*] :=
reheat  32.0306
  roll  32.6855
;

Using this feature together with one of AMPL’s looping commands, you can automatically solve a series of random realizations from the model, and summarize the results:

model steel4.mod;
data steel4.dat;
param nruns := 5;
param optvalue {1..nruns};
for {k in 1..nruns} {
   reset data avail;
   solve;
   let optvalue[k] := Total_Profit;
}
display (sum {k in 1..nruns} optvalue[k]) / nruns;

If you use reset rather than reset data, then AMPL’s random number generator is reset, and the values of avail repeat from the beginning. To get a different sequence of random numbers from the generator, you must change the random number seed; give the command option randseed n — where n is a positive integer — or option randseed 0 to have AMPL choose a seed based on the system clock.

The check conditions are evaluated each time that AMPL generates (or re-generates) an instance of your model. Normally the generation of an instance is triggered by a solve command, but a few other commands such as write and solution can have the same effect.

You can force all check statements to be evaluated immediately by typing the command

check;

or by inserting this command into an AMPL script at the point where you want the checking to occur.

You can use AMPL’s write command to create a file that contains a representation of your linear or integer program in a standard format known as MPS form. To write an MPS-form file for the diet LP from Chapter 2 of the AMPL book, for example, you could proceed as follows:

ampl: model diet.mod;
ampl: data diet2a.dat;
ampl: option auxfiles rc;
ampl: write mdiet2;

AMPL interprets write m... as indicating that you want to write an MPS file, and creates the filename by appending .mps to the letters after the m. Thus our example creates a file named diet2.mps. The format of this file is explained in the Linear Programming FAQ and in the manuals for many solvers.

Because MPS form limits the row (constraint or objective) and column (variable) names to 8 characters, AMPL substitutes artificial names such as R0001 and C0007. You can ask for supplementary files of the true AMPL component names, by also resetting option auxfiles rc; in the above example, you would get files diet2.row and diet2.col. The ordering of the names in these files corresponds to their numbering in the MPS file. (For a more detailed description of write and the auxfiles option, see Sections A.18.3 and A.18.4 in the reference appendix to the AMPL book.)

An MPS file contains only the nonzero values that define one instance of your model. Thus an MPS file generated by AMPL is mainly useful as input to solvers that do not yet have a direct AMPL interface; because MPS form has been in use for a longer time than any comparable format, it is recognized by more solvers than any other file type. You may also find AMPL’s MPS-file option useful for generating new instances of test problems, for submission to libraries such as netlib’s lp/data or miplib. If you want to encourage people to use AMPL with your own solver, however, then you should consider hooking your solver to AMPL by use of AMPL’s file format, which takes less space, can be processed faster, represents numbers more accurately, and applies to a broader variety of optimization problems (especially nonlinear ones).

Several kinds of transformations may be applied by AMPL before it writes out any representation of your problem. Normally you need not be aware of these changes, because AMPL reverses them after receiving the optimal solution from the solver. If you write an MPS file, however, it will correspond to the transformed problem; its coefficients and other values may have been changed, it may lack some of the variables and constraints that you defined, or it may contain auxiliary variables and constraints that AMPL added. To get a summary of AMPL’s transformations, set option show_stats 1. To force AMPL to write the MPS file for your problem as stated, turn off the transformations.

Representations of numbers in MPS form are limited to 12 characters. As a consequence, the numbers in an MPS file may not have the full precision of the numbers that were generated from your AMPL model and data. Usually these precision discrepancies are inconsequential, but they do give rise to inaccuracies in the optimal solution computed from the MPS file.

Although MPS form is regarded as a standard, it has no definitive statement. As a result, there are a few special cases that are treated differently by different solvers; examples include variables that have an upper bound of zero and no lower bound, and integer variables declared without any bounds. AMPL’s version of MPS form is designed to avoid using these unresolved default options, so that its MPS files can be compatible with as many solvers as possible. If using the MPS file gets you a significantly different optimal value from what you get by typing solve, however, then check your solver’s definition of MPS form.

AMPL’s presolve phase attempts to transform your problem to an equivalent one that is smaller and easier to solve. Presolve first removes trivial model components, such as variables fixed at constant values and constraints that express simple lower or upper bounds. Then it applies an iterative procedure to tighten certain bounds on variables and constraints, with the result that additional variables may be fixed and constraints may be dropped. Many of these transformations are based on ideas first presented by A.L. Brearly, G. Mitra and H.P. Williams, in “Analysis of Mathematical Programming Problems Prior to Applying the Simplex Algorithm,” Mathematical Programming 8 (1975) 54-83. See also Experience with a Primal Presolve Algorithm for a detailed discussion of the implementation in AMPL.

If your model uses AMPL’s notation for piecewise-linear terms in individual variables, then AMPL transforms your problem’s piecewise-linear expressions to equivalent linear expressions, through the addition of auxiliary variables and constraints. Chapter 17 of the AMPL book describes the piecewise-linear notation in more detail. See also Expressing Special Structures in an Algebraic Modeling Language for Mathematical Programming for further discussion of how the transformation is carried out.

You can cause AMPL to eliminate certain variables from your problem, by substituting expressions specified in var declarations or in certain constraints. If the substitution comes from a constraint, then the constraint is also eliminated. See Sections 18.2 and A.8.1 of the AMPL book for more information.

To suppress the presolve phase, set option presolve 0.

To suppress transformation of piecewise-linear terms, set option pl_linearize 0.

To suppress elimination of variables by substitution from constraints, set option substout 0. (This is the default setting.) Substitutions specified within var declarations are always applied.

To see a summary of transformations performed, set option show_stats 1.

Errors and Messages

Your let command is attempting to assign a value to some set or parameter. In the set’s or parameter’s declaration, however, you have used a := phrase to permanently define the value. A let command is not permitted to redefine the value of such a parameter; hence the error message that you received.

As an example, if the model declares

param dstot {p in prod} := sum {w in whse} ds[p,w];

then dstot[p] is permanently defined to take the value sum {w in whse} ds[p,w]. You can make any changes you like to the ds[p,w] values, and AMPL will automatically change the dstot[p] values accordingly. Any command beginning let {p in prod} dstot[p] := ... will be in error, however.

If you want to be able to use let to override the defined value of a parameter, use default in place of := in your param declaration.

AMPL is signaling an invalid statement of some sort, but the error may not be a matter of “syntax” in the usual sense. For example, you could be trying to define a set index at a place where it is already in use:

ampl: display {i in ORIG}:
ampl?    supply[i] * sum {i in DEST} cost[i,j];
syntax error
context:  supply[i] * sum {i  >>> in  <<< DEST} cost[i,j];

The index i cannot be defined following sum, because it is within the scope of the index i that was defined following display.

If you receive a syntax error that is particularly hard to interpret, please let us know.

When a solver starts up, it displays a brief identification string such as MINOS 5.5 or CPLEX 12.4. Output that occurs before this string can be assumed to come from AMPL, while output after is the solver’s.

When a solver concludes its run, it returns a brief summary of the results, such as

optimal solution; objective 903202.0326
399 iterations (244 in phase I)

Output prior to this summary can be assumed to be caused by the solver, while output after is from AMPL again.

If AMPL appears to hang after a solve command but before any output from a solver, then it may be taking much more time to generate your problem than you expected. Try setting option times 1, gentimes 1 to get more output recording AMPL’s progress, as explained in the FAQ question regarding insufficient memory.

The same advice applies if AMPL hangs after some other command, such as write or expand, that forces a lot of variables or constraints to be generated.

If AMPL appears to hang after a command that produces a listing, such as display, then the listing may be much longer than you expected. Usually it is possible to make a quick estimate of the size of the listing, in order to determine whether this could be the difficulty.

If AMPL appears to hang after a line or more from the solver, then your optimization problem may be taking much more time to solve than you expected. Try setting solver directives that provide more output recording progress toward an optimum. Some solvers may also respond to a “break” sequence (such as Ctrl-C) by returning the best solution found so far. These features differ from one solver to the next and even from one algorithm to the next within the same solver; see the solver-specific documentation for details.

You have encountered a low-level error message generated by the operating system. AMPL and the solvers try to trap these messages so as to provide you with more useful information instead; but occasionally a mysterious message does get through.

Some messages, such as

no children
unrecoverable error, STAT = 1001
not enough swap space

usually indicate that AMPL can’t get enough computer resources to complete the current session. As an example, there might be insufficient disk space for AMPL to write the temporary file that the solver reads; to remedy the problem, you can reset option TMPDIR to change the directory to which temporary files are written. More often, however, the difficulty in this situation is insufficient memory, a more complicated matter that we discuss in the next question below.

Other messages, including

segmentation fault
bus error

indicate a bug in AMPL or a solver. Often there is some way to work around this trouble temporarily, but you should also look for a more permanent fix. You can consult our bug fix log to see whether this bug has been found and fixed already; if not, you can tell us about the problem through our comment form. Either way, you will want to request a newer version of the AMPL software that incorporates the fix.

If AMPL fails before passing control to the solver, your computer may not have enough memory to hold all of the variables, constraints and objectives generated by the AMPL translator. If a failure occurs after the solver takes over, memory might be short because AMPL has taken some of it for generating the problem, leaving not enough memory for the solver to be active at the same time. (Operating systems do tend to “page out” a lot of the AMPL process to disk while running the solver, though.)

As a start in diagnosing these situations, display listings of AMPL’s memory use by repeating your run with one or more of

option times 1;
option gentimes 1;
option show_stats 1;

The times listing includes the amount of memory used by each phase of the AMPL translator:

parseinitial processing of model
data readinitial processing of data
compiletranslation of model and data
genmodgeneration of an explicit optimization problem
mergepostprocessing of optimization problem
collectpostprocessing of optimization problem
presolvereduction of problem size
outputwriting of problem file to be read by solver

The gentimes listing includes memory allocated for processing each declaration in the model. If AMPL fails before invoking a solver, this information will tell you how far it got. For failures in the presolve phase, you can save some memory by setting option presolve 0, though a larger problem will be sent to the solver as a result. (Each line in a times or gentimes listing appears when processing of the phase or component is finished. Thus the cause of a memory failure must be in the phase or component after the last one that appeared in the listing. It may help to first run a smaller version of your problem, so that you can see what ought to appear in a complete, correct listing.)

If failure doesn’t occur until after the solver is invoked, then the show_stats listing also appears, giving the numbers of variables and constraints generated and the numbers removed by presolve. If there are more variables or constraints than you expected, then check the gentimes listing for a declaration that has required a very large amount of memory (relative to what the whole model requires). The offending declaration may be incorrect; or, even if it is technically correct, the declaration may specify a very large number of variables or constraints that are unnecessary to the formulation. (Even if a variable is never referenced in the model, it takes up some minimum amount of space in the data structure that AMPL maintains in memory.)

If the difficulty is indeed insufficient memory to run the solver under AMPL, then there is a good chance that you can work around the problem by running AMPL and your solver separately. Here’s an example of how this is done:

ampl: model models\multmip3.mod;
ampl: data models\multmip3.dat;
ampl: write bmultmip3;   # "b" indicates a binary problem file
ampl: quit
C:\AMPL> cplex multmip3
No MIP presolve or aggregator reductions.
C:\AMPL> ampl
ampl: model models\multmip3.mod;
ampl: data models\multmip3.dat;
ampl: solution multmip3.sol;
CPLEX 3.0: optimal integer solution; objective 235625
684 simplex iterations
126 branch-and-bound nodes
ampl: display Trans;
Trans [CLEV,*,*]
:   bands coils plate    :=
DET     0   525   100
FRA   275    50    50  ...

This is inconvenient, but it can substantially reduce the overall memory requirement.

To diagnose insufficient memory, you may need some idea of the amount of memory available for allocation by AMPL and the solver. Your computer may have a utility to help with this, or you may be able to run a short program like this (in C) that tries to allocate as much as possible:

#include <stdio.h>
#include <stdlib.h>
int main()
{
    long int avail, incr;
    avail = 0;
    for (incr = 1024 * 1024; incr >= 1; incr >>= 1)
        while (malloc(incr)) avail += incr;
    printf ("Total allocated: %i\n", avail);
}

Keep in mind that the available memory is often influenced not only by the amount of memory physically installed in the computer, but by the amount of disk space allocated to memory swapping and by the requirements of all other processes currently running on the computer.

If AMPL seems to be using much less memory that you have available, then you should also check that you are using 64-bit versions of your operating system, AMPL, and solvers. A 32-bit version cannot possibly access more the 4 gigabytes of memory, and is limited to as few as 2 gigabytes on some computers.

Sets and Indexing

In general, AMPL interprets a..b as {a, a+1, a+2, ..., b-1, b}. Thus when T is greater than one, T..1 is an empty set. To specify the ordered set that goes from T down to 1, write T..1 by -1.

You have declared set S in your model and set S := 50 .. 70 in your data. Set expressions are not recognized in AMPL’s data mode, however. Instead AMPL tries to recognize 50 .. 70 as a space-delimited list of members of S, with the result that it has found three members: the numbers 50 and 70, and the string "..".

To define S to equal 50 .. 70 by use of your data file, first declare S in your model by

param begin;
param end > begin;
set S := begin .. end;

Then state in the data file:

param begin := 50;
param end   := 70;

Alternatively, it’s legal to give set S = 50 .. 70 as your declaration of S. This is a less desirable approach, however, because it moves some of the specific data values into the model.

There’s no specific arg min operator in AMPL. However, proceeding directly from the definition, you can define the arg min explicitly by a set expression like

{s in S: b[s] = min {i in S} b[i]}

This expression gives a subset of S, however, containing all of the members of S that achieve the minimum. To get just one member representing the arg min, you can define this to be an ordered set,

set b_argmin ordered := {s in S: b[s] = min {i in S} b[i]};

Then first(b_argmin) is guaranteed to be one member of S that minimizes b[i].

As an alternative, you can use AMPL’s for and if commands to write a script that loops over S to compute the arg min explicitly:

param bmin;
param imin symbolic in S;
let bmin := Infinity;
for {i in S} {
   if b[i] < bmin then {
      let bmin := b[i];
      let imin := i;
   }
}

In general this is a slower alternative than defining b_argmin as above. It admits a greater variety of generalizations in complex cases, however.

AMPL can index model components by objects (numbers and character strings) but not by sets of objects. Hence you can’t model a power set directly. You may be able to get the same effect, however, by constructing a numbered list of subsets.

As an example, suppose that you want to index over all subsets of the set of the first n nonnegative integers. You can declare:

param n integer > 0;
set S := 0 .. n - 1;
set SS := 0 .. 2**n - 1;
set POW {k in SS} := {i in S: (k div 2**i) mod 2 = 1};

Since there are n members of S, there are 2**n subsets of S. Hence there exists a one-to-one correspondence between the members of the set SS := 0 .. 2**n - 1 and the subsets of S. By use of a simple encoding, you can make this correspondence explicit; the indexed collection of sets POW above is declared such that POW[k] is the kth distinct subset of S. (To see the whole power set, type display POW.)

Much the same can be done for an arbitrary ordered set S:

set S ordered;
param n := card {S};
set SS := 0 .. (2**n - 1);
set POW {k in SS}
   := {i in S: (k div 2**(ord(i)-1)) mod 2 = 1};

Either way, you can use indexing over POW to get the effect of indexing over the power set. In the following example, each member s of S has a weight Wt[s], and the average “weight” of all members of any subset of S is constrained not to exceed n/2:

var Wt {S} >= 0;
subj to MaxWt {k in SS}:
   sum {i in POW[k]} Wt[i] <= (n / 2) * card(POW[k]);

A more involved example, which represents a traveling salesman problem as an integer program, is provided in tsp.mod. Constraints like this can be useful for studying and testing certain formulations of combinatorial problems, provided that the underlying set (S in our example) is kept to a reasonably small size.

AMPL does not define any ordering on pairs, triples, or objects of higher dimension. Thus nextprev, and other functions that apply to objects in ordered sets cannot be applied to pairs, triples, or tuples of higher dimension. For the same reason, firstlast and other functions of ordered sets may not be applied to multi-dimensional sets.

You may apply these functions to individual indices of a multi-dimensional parameter or variable, however. The following examples are from steelT2.mod (Figure 5-3) of the AMPL book:

subject to balance0 {j in PROD}:
   Make[j,first(WEEKS)] + inv0[j]
      = Sell[j,first(WEEKS)] + Inv[j,first(WEEKS)];
subject to balance {j in PROD, t in WEEKS: ord(t) > 1}:
   Make[j,t] + Inv[j,prev(t)] = Sell[j,t] + Inv[j,t];

Most models that involve both ordered and multidimensional sets can be handled by some variation of this approach.

Either a data table gave values for the parameter with incorrect subscripts, or the parameter’s indexing set changed, causing some previously valid subscripts to become invalid. For example, in the diet.mod + diet.dat example (Figures 2-1 and 2-2) of the AMPL book, values of parameter cost are supplied for all eight members of set FOOD:

ampl: display cost;
cost [*] :=
BEEF 3.19   FISH 2.29    MCH 1.89    SPG 1.99
 CHK 2.59    HAM 2.89    MTL 1.99    TUR 2.49
;

If you remove the member CHK from FOOD, using for example a let command, then you get a message that cost["CHK"] has also been dropped from the data:

ampl: let FOOD := FOOD diff {"CHK"};
ampl: display cost;
Error executing "display" command:
error processing param cost:
        invalid subscript cost['CHK'] discarded.
cost [*] :=
BEEF 3.19    HAM 2.89    MTL 1.99    TUR 2.49
FISH 2.29    MCH 1.89    SPG 1.99

Since cost["CHK"] has now been dropped, no further error message will appear if you type display cost again.

To avoid error messages of this sort, you can define a more flexible set structure for your model as shown in dietflex.mod and dietflex.dat. The auxiliary set DIET_DROP defaults to empty, so that the problem is solved for all foods; but you can change DIET_DROP to {"CHK"} to solve without member CHK:

ampl: model dietflex.mod;
ampl: data dietflex.dat;
ampl: option show_stats 1;
ampl: solve;
8 variables, all linear
6 constraints, all linear; 47 nonzeros
1 linear objective; 8 nonzeros.
MINOS 5.4: optimal solution found.
13 iterations, objective 118.0594032
ampl: let FOOD_DROP := {"CHK"};
ampl: solve;
7 variables, all linear
6 constraints, all linear; 42 nonzeros
1 linear objective; 7 nonzeros.
MINOS 5.4: optimal solution found.
3 iterations, objective 117.3218891

Changing FOOD_DROP does not affect the set FOOD_ALL, and consequently all of the subscripts in the data remain valid.

Variables

A variable is ineligible to be substituted out of a problem if (a) it is subject to any bounds or integrality conditions, or (b) it has already appeared on the right-hand side of a constraint that was used to make a substitution.

Case (a) includes bounds or integrality conditions specified in the variable’s declaration, and also bounds that are added by AMPL’s presolve phase. Thus turning presolve off (by setting option presolve 0) may permit a greater number of substitutions.

For constraints indexed over a set, the incidence of case (b) may depend on the ordering of the set. Consider for example the constraints

var x {1..10};
subj to step {j in 1..9}: x[j] = x[j+1] + 1;

The first several constraints generated are

step[1]:  x[1] = x[2] + 1
step[2]:  x[2] = x[3] + 1
step[3]:  x[3] = x[4] + 1
step[4]:  x[4] = x[5] + 1

Constraint step[1] may be used to substitute for x[1]. Constraint step[2] may not be used to substitute for x[2], however, because x[2] has already appeared on the right-hand side of a constraint, namely step[1], that was used to make a substitution. Similarly, step[3] may be used to substitute for x[3], but step[4] may not be used to substitute for x[4], and so forth. Only the odd-numbered constraints step[j] are eliminated by substitution in this case.

If instead you write the declaration with the ordered index set reversed,

subj to step {j in 9..1 by -1} x[j] = x[j+1] + 1;

then the constraints are generated as

step[9]:  x[9] = x[10] + 1
step[8]:  x[8] = x[9] + 1
step[7]:  x[7] = x[8] + 1
step[6]:  x[6] = x[7] + 1

and case (b) does not occur. All of the constraints step[j] can be eliminated, and every variable except x[10] is substituted out as a result.

Close attention to formulation may thus be necessary to get the substitutions that you want. Set option show_stats 1 to see how many substitutions are being made, and use AMPL’s constraint expansion commands to see the constraints (in order) before and after substitution.

Symptoms of this problem are an option show_stats 1 listing that refers to nonlinear variables and constraints, and rejection by the solver with a “contains nonlinear constraints” message. The cure may be (a) to reformulate the model so that certain unintended nonlinearities are made linear, or (b) set option linelim 1 so that substituted linear variables do not become nonlinear.

Case (a) can occur because you have overlooked a simple nonlinearity, such as a variable multiplying or dividing another variable. AMPL’s built-in arithmetic functions, including simple ones such as abs and max, are treated as nonlinear when applied to variables; if ... then ... else ... expressions are also nonlinear if variables appear in the expression following the if. You may be able to convert these kinds of “nonlinearities” to equivalent linear expressions, but AMPL cannot do the conversion automatically. (The only such conversions currently built into AMPL are for the piecewise-linear functions described in Chapter 17 of the AMPL book.)

Case (b) occurs when AMPL’s defined variable feature is invoked to substitute an expression for a variable — either by using the = operator in declarations of variables, or by setting option substout 1 to infer substitutions from the constraints. Under option linelim 0, AMPL does not substitute explicitly in the constraints, but instead records the substitution as an additional piece of information in the file sent to the solver. This affords very efficient handling of sub-expressions that appear at many places in a problem, but it has the side-effect of causing all variables in a substituted expression to be treated as nonlinear. To request explicit substitution, so that linearity is preserved, leave the linelim option at its default setting of 1.

Yes, AMPL fixes the variables first, and then analyzes the model to determine whether it is linear. So if you make your model linear by fixing certain variables, you can direct AMPL to send the resulting problem instance to a linear solver for optimization.

Displays and Results

AMPL’s expand command can display any explicit linear constraint, or indexed collection of constraints. For example:

ampl: model transp.mod;
ampl: data transp.dat;
ampl: expand Demand['STL'];
s.t. Demand['STL']:
        Trans['GARY','STL'] + Trans['CLEV','STL'] +
        Trans['PITT','STL'] = 1700;
ampl: expand Supply;
s.t. Supply['GARY']:
        Trans['GARY','FRA'] + Trans['GARY','DET'] +
        Trans['GARY','LAN'] + Trans['GARY','WIN'] +
        Trans['GARY','STL'] + Trans['GARY','FRE'] +
        Trans['GARY','LAF'] = 1400;
s.t. Supply['CLEV']:
        Trans['CLEV','FRA'] + Trans['CLEV','DET'] +
        Trans['CLEV','LAN'] + Trans['CLEV','WIN'] +
        Trans['CLEV','STL'] + Trans['CLEV','FRE'] +
        Trans['CLEV','LAF'] = 2600;
s.t. Supply['PITT']:
        Trans['PITT','FRA'] + Trans['PITT','DET'] +
        Trans['PITT','LAN'] + Trans['PITT','WIN'] +
        Trans['PITT','STL'] + Trans['PITT','FRE'] +
        Trans['PITT','LAF'] = 2900;

The expand command also works with variables and objectives, and has indexing options similar to those of display or print for specifying which components are expanded.

The built-in parameter _nvars contains the number of variables; a symbolic parameter _varname[j] contains the AMPL name of the jth variable; and _var[j] is a “synonym” that refers to the jth variable. Thus to show the names of all variables that are below their upper bounds at the optimum, for example, you can write:

ampl: display {j in 1.._nvars:
ampl?   _var[j] < _var[j].ub - 0.00001} _varname[j];
_varname[j] [*] :=
2  "PD_Ship['SE']"
5  "DW_Ship['NE','BWI']"
6  "DW_Ship['SE','EWR']"
7  "DW_Ship['SE','BWI']"
;

This is only one of the uses for AMPL’s generic synonyms for variables, constraints and objectives. See the discussion of AMPL’s generic names in Section A.19.4 of the reference appendix to the AMPL book.

AMPL’s suffix feature can be used by solvers to return ranging information. See in particular the discussion of solver-defined suffixes in Section 14.3 of the AMPL book for an example of range output from CPLEX. The option listings for the CPLEX and Gurobi solvers contain more information about how to request and retrieve this information.

AMPL does not currently support “parametric” algorithms for postoptimal analysis of objective functions or constraint right-hand sides.

In practice, ranging often provides sensitivity information of limited usefulness, and parametric algorithms are limited to certain kinds of linear changes to the data. You may do better to write a simple AMPL script that solves the same model for a series of different parameter values. For an example, see the simple sensitivity analysis script developed in Section 13.1 of the AMPL book.

AMPL cannot determine by itself the values from the linear programming tableau, because the sparse linear algebra routines capable of computing these values are found only in the solvers. Efficient solver implementations avoid computing or storing more than a few tableau columns at a time, however. Hence AMPL also has no solver directives that can cause tableau values to be returned.

For a specific AMPL model, it is possible to set up an auxiliary model that finds the tableau values, at least in the nondegenerate case; see our diet tableau example based on the diet model from Chapter 2 of the AMPL book. This approach is highly inefficient, but should be adequate for small linear programs — up to a few dozen variables, say.

The display command does not currently recognize {k in SS} POW[k] where POW is an indexed (subscripted) set. For this elementary case, you can exhibit the whole indexed collection of subsets by giving just the name of the collection:

display POW;

You can also exhibit each of the indexed sets separately, by using the iterated form of the display command; it resembles the ordinary form, but with a colon after the index set:

display {k in SS}: POW[k];

This gives the same output as if you had typed a separate statement display POW[k] for each member k of SS. Any set expression (or list of expressions) may follow the colon.

Add the keyword ordered to the set’s declaration, after the set name. It can be useful to declare a set ordered for purposes of ordering the output, even when the ordering plays no role in the model.

AMPL provides the following built-in timing parameters such as _ampl_time and _total_solve_time. You’ll find the complete list in Table A-14 in the reference appendix of the AMPL book. These timing parameters can be used in all the same ways as ordinary AMPL parameters defined through param statements. Thus in particular you can show their values (or the values of expressions involving them) by use of display or printf commands.

Integer Programming

For each variable whose integrality you want to relax, assign a positive value to variable-name.relax. For example, if your model has

var X {1..n} integer >= 0;
var Y {1..m} integer >= 0;

then to relax the integrality of only the Y variables, you can use the command

let {i in 1..m} Y[i].relax := 1;

To remove a relaxation specified in this way, assign a nonpositive value to variable-name.relax.

These two ways of relaxing integrality can give different results because they interact differently with AMPL’s presolve phase.

Under option relax_integrality 1, all integer variables are changed to continuous before AMPL’s presolve. Thus presolve works on the “true” relaxation, and the reduced LP that comes out of presolve has the same objective value as the true relaxation.

Under option relax_integrality 0, all integer variables remain integer through AMPL’s presolve phase. Presolve may take advantage of this integrality to further tighten bounds and reduce the problem size. If the solver’s relax directive is subsequently set, then it will solve the relaxation of the presolved integer program, which may not have the same objective value as the true relaxation. (Specifically, its value may be higher for a minimization, or lower for a maximization.)

As a simple example, imagine a minimization model in which there are integer variables X[t] constrained by sum {t in 1..T} X[t] <= 7.5. If relax_integrality is set to 1, then the variables are made continuous, but otherwise the constraint remains the same. If relax_integrality is instead left at 0, then presolve will tighten 7.5 to 7 before sending the integer program to the solver. This has no effect on the integer optimum, but by tightening the constraint it may cause the solver’s relaxation to have a higher value than AMPL’s relaxation.

For purposes of solving the problem, the solver’s higher relaxation value is normally to be preferred. In some iterative schemes that solve a series of relaxations, however, only the lower true relaxation value makes sense. To ensure that you get the optimum of the true relaxation, either set option relax_integrality 1 or set option presolve 0 and turn on the solver’s relax directive.

The “break” signal (Ctrl-C for many computers) stops the solver and returns control to AMPL, but does not necessarily return any solution from the solver. Some solvers may make provision to “catch” the break signal and return the best solution found so far; consult the solver directive instructions to see if this is the case for the solver that you are using.

Various solver directives can stop a run and return the current incumbent optimal solution, but they must be given before the run begins. Available stopping criteria may include number of branch-and-bound nodes, number of integer solutions, and total time. Directives are different for each solver, so consult AMPL’s solver directive instructions for details.

There are two kinds of formulations that cause special ordered sets to be set up by AMPL and used by your solver automatically. For other situations you can explicitly identify special ordered sets of types 1 and 2 using certain suffixes on variables. The first kind of formulation involves variables that are restricted to a finite but not regularly-spaced series of values. For example, each variable Cap[j] standing for the capacity of warehouse j might be required to take one of the values in a given set S by use of a declaration like this:

var Cap {i in WHSE} in S;

(For example, S might contain only 0, 15, 25, and 30.) Special ordered sets of type 1 are automatically generated to help the solver branch efficiently on the possibilities in this case. The second kind of formulation is used to handle separable piecewise-linear functions written as described in Chapter 17 of the AMPL book. As an example, a 3-part shipment cost to be minimized may be written as

minimize Total_Cost:
   sum {i in ORIG, j in DEST}
      <<limit1[i,j],limit2[i,j];
        rate1[i,j], rate2[i,j], rate3[i,j] >> Trans[i,j];

using AMPL’s <<>> notation for writing piecewise-linear functions in terms of their breakpoints and slopes. In special cases there is an equivalent linear formulation; but in all other cases, special ordered sets of type 2 are automatically generated to help the solver search the possibilities. To identify special ordered sets explicitly, the following statements must first be used to create two suffixes for providing extra information on each variable:

suffix sosno integer IN;
suffix ref integer IN;

For our type 1 example above, auxiliary binary variables Lambda[i,j] could be defined to express the Cap[i] variables as sums that can only take on the possible values in S:

param nWhse integer > 0;
set WHSE = 1..nWhse;
var Lambda {i in WHSE, j in S} binary;
var Cap {i in WHSE} = sum {j in S} j * Lambda[i,j];
subject to ChooseOne {i in WHSE}:
  sum {j in S} Lambda[i,j] = 1;

The special ordered sets for this formulation could be defined by assigning values to the suffixes as follows:

let {i in WHSE, j in S} Lambda[i,j].sosno := i;
let {i in WHSE, j in S} Lambda[i,j].ref := j;

The variables in each special ordered set must be given the same unique .sosno value, positive for sets of type 1 or negative for sets of type 2. The .ref values guide branching within the solver’s branch-and-bound scheme, and are generally the coefficients in a sum like sum {j in S} j * Lambda[i,j] that appears in the definition of some variable. The details vary with the situation being modeled, and some advanced study of relevant papers may be necessary to get them right.

Versions and Platforms

The AMPL Student Edition consists of the AMPL software together with several solvers. It is limited to 300 variables and 300 constraints and objectives (500 for linear problems), but is otherwise identical to the full professional edition.

Courses using AMPL can get time-limited but size-unlimited versions of AMPL and solvers through the AMPL for Courses program. Academic research projects can buy highly discounted versions of the full-featured AMPL and solver packages.

The professional edition is the full-featured, unrestricted version. Numbers of variables and constraints are limited only by available computer resources.

Professional edition users may choose among numerous solvers. Solvers may be added at any time, at only the cost of the solver software; you do not need to buy additional AMPL software in order to add a solver.

Yes, access to AMPL IDE is included with all purchases and trials of AMPL. Optirisk Systems offers extended versions of the IDE with support of stochastic and robust optimization. There are also several experimental AMPL graphical interfaces for Windows that are available for downloading at no charge.

If you are using Linux or a Unix-based system, AMPL’s command-line environment should run nicely within a command or text window. The computer’s windowing software lets you scroll back through an AMPL session and copy previous AMPL commands. You can set up other windows to simultaneously view the model and data. A similar, free scrolling-window utility (sw.exe) is supplied for use with AMPL’s command-line environment under Windows. You might also want to look into editors can be configured with syntax highlighting specific to AMPL.

AMPL is available on all of the most popular platforms, including all recent versions of Windows, Linux, and MacOS, as well as several Unix systems. Solver availability varies, but generally also covers the popular platforms.

See our Platforms page for detailed availability of AMPL and of solvers sold by AMPL Optimization.

You can look at the current version message by invoking AMPL with the -v command-line switch:

% ampl -v
AMPL Version 20120619 (Intel icl EMT64 10.1.029, 64-bit)
Licensed to John von Neumann (JOHNNIAC).
Trial license expires 20130930.

Or, at any point in an AMPL session, you can see the version message by looking at the contents of AMPL’s version option, by typing option version or print $version.

The 8-digit number represents a date in the form yyyymmdd – 30 September 2013 in this example. You can compare this date against the dates of the entries in the AMPL change logs and in the write-ups of new AMPL features.

Updates are announced on our website, newsgroup, and Twitter stream (@amplopt). Additionally, period email reports of changes and fixes are sent to users who have registered download accounts; addresses for this information can be added and updated by logging in and clicking on “Notify list”.

Our maintenance & support service includes unlimited updates for AMPL and solvers purchased from us. One year of maintenance & support is included with all purchases. For more information about bringing existing licenses under maintenance, contact licensing@ampl.com.

Solvers

AMPL works with a variety of large variety of solvers for optimization problems of many kinds. See our solver pages for details.

Yes. Once you have AMPL running, you add a solver by simply installing the AMPL-interfaced version of that solver on your system..

The procedure is equally simple if you already have a solver running, and want to later add AMPL to your system. You will need to obtain the AMPL-interfaced version of the solver, however, if your distribution did not come with it.

Generally there is no extra charge for the “link” between AMPL and solvers. In many cases the linking code is freely available.

Many solver options can be specified directly from AMPL. You don’t need to prepare separate “spec files” or run solvers outside the AMPL environment in order to get the benefits of these options.

Separate guides to solver directives are available online, from the solver developers, or both. See our solver listing for more information.

AMPL has a publicly documented interface that you can use to make your own link to any solver you are developing. The interface works through files; AMPL writes a problem file to be read by your solver, and your solver writes a solution file to be read back by AMPL. The file format has equivalent binary and ASCII versions. It offers the flexibility needed to support a wide variety of solvers, yet is designed to afford very efficient reading and writing.

The AMPL interface is described and illustrated in Hooking Your Solver to AMPL. You can also download standard interface routines (in C) for reading and writing the AMPL file formats, along with a variety of examples of complete AMPL/solver links; see our hooking page for details.

Further Information

The AMPL book is the definitive introduction, tutorial and reference for AMPL. Chapters can be freely downloaded, and hardbound printed copies can be purchased.

Articles that describe and analyze many aspects of the design of AMPL are cited on our references page.

Post questions to our newsgroup by emailing them to ampl@googlegroups.com. Also you can write or call as detailed on our contact page.