We begin by describing a convenient and precise new way of testing the overall result of a solver run, and the result of the most recent solver run for a specified objective or named problem. Then we describe the solver status for variables and constraints and its use to specify an advanced start. We conclude by describing a complementary AMPL status that characterizes model components that have not been sent to the solver for various reasons. For most purposes, a variable or constraint's status of interest can be denoted by adding the suffix .status to its name.
The brief result message that the solver returns to AMPL is
also accessible via a built-in symbolic parameter, solve_message:
Solve results
A solver finishes its work because it has identified an optimal
solution or has encountered some other terminating condition. In
addition to the values of the variables, the solver may set two
built-in AMPL parameters and an AMPL option that provide information
about the outcome of the optimization process:
At the beginning of an AMPL session, solve_result_num is
-1 and solve_result is '?'. Any
solve command resets these parameters, however, so that they
describe the solver's status at the end of its run, solve_result_num
by a number and solve_result by a character string. The
solve_result_table lists the possible combinations, which
may be interpreted as follows:
ampl: model diet.mod;
ampl: data diet2.dat;
ampl: display solve_result_num, solve_result;
solve_result_num = -1
solve_result = '?'
ampl: solve;
MINOS 5.5: infeasible problem.
9 iterations
ampl: display solve_result_num, solve_result;
solve_result_num = 200
solve_result = infeasible
ampl: option solve_result_table;
option solve_result_table '\
0 solved\
100 solved?\
200 infeasible\
300 unbounded\
400 limit\
500 failure\
';
number string interpretation
0 - 99 solved optimal solution found
100 - 199 solved? optimal solution indicated, but error likely
200 - 299 infeasible constraints cannot be satisfied
300 - 399 unbounded objective can be improved without limit
400 - 499 limit stopped by a limit that you set (such as on iterations)
500 - 599 failure stopped by an error condition in the solver routines
Normally this status information is used in scripts, where it
can be tested to distinguish among cases that must be handled in
different ways. As an example, the following script for the diet
problem progressively decrements the sodium limit,
n_max["NA"], until it finds a value that makes the problem
infeasible:
The if condition solve_result =
"infeasible" could equivalently be written 200
<= solve_result_num < 300.
Normally you will want to avoid the latter alternative, since it only
makes the script more cryptic. It can occasionally be useful, however,
in making fine distinctions among different solver termination
conditions. For example, here are some of the values that the CPLEX
solver sets for different optimality conditions:
model diet.mod;
data diet2a.dat;
repeat {
solve;
if solve_result = "infeasible" then break;
let n_max["NA"] := n_max["NA"] - 1000;
};
solve_result_num message at termination
0 optimal solution
1 primal has unbounded optimal face
2 optimal integer solution
3 optimal integer solution within mipgap or absmipgap
The value of solve_result is "solved" in all of these
cases, but you can test solve_result_num if you need to
distinguish among them. The interpretations of
solve_result_num are entirely solver-specific; you'll have to
look at a particular solver's instructions to see which values it
returns and what they mean.
You can use AMPL's string functions to test
the contents of solve_message, but for most situations a test
of solve_result will be simpler and less solver-dependent.
ampl: display solve_message;
solve_message = 'MINOS 5.5: infeasible problem.\
9 iterations'
Solve results can be returned as described above only if AMPL's invocation of the solver has been successful. Invocation can fail because the operating system is unable to find or execute the specified solver, or because some low-level error prevents the solver from attempting or completing the optimization. Typical causes include a misspelled solver name, improper installation of the solver, missing or corrupt input files, insufficient memory or disk space, and termination of the solver process by an execution fault ("core dump") or a "break" from the keyboard.
The built-in parameter solve_exitcode records the success or failure of the most recent solver invocation. Initially -1, it is reset to 0 whenever there has been a successful invocation, and to some nonzero value otherwise:
Here the failed invocation, due to the misspelled solver name xplex, is reflected by a positive solve_exitcode value. The solve status parameters solve_result and solve_result_num are also reset to their initial values '?' and -1.ampl: reset; ampl: display solve_exitcode; solve_exitcode = -1 ampl: model diet.mod; ampl: data diet2.dat; ampl: option solver xplex; ampl: solve; Cannot invoke xplex: No such file or directory exit code 4 <BREAK> ampl: display solve_exitcode; solve_exitcode = 1024 ampl: display solve_result, solve_result_num; solve_result = '?' solve_result_num = -1
If solve_exitcode exceeds the value in option solve_exitcode_max, then AMPL aborts any currently executing compound commands (include, commands, repeat, for, if). The default value of solve_exitcode_max is 0, so that AMPL normally aborts compound commands whenever a solver's invocation fails. A script that sets solve_exitcode_max to a higher value may test the value of solve_exitcode, but in general its interpretation is not consistent across operating systems or solvers.
Appended to an objective or problem name, this suffix indicates the value of the corresponding built-in parameter at the most recent solve in which the objective or problem was current. Thus our sample script trnloc1p.run for Benders decomposition suffixes SubPoint with .result to refer to the most recent subproblem solution:built-in parameter suffix solve_result .result solve_result_num .result_num solve_message .message solve_exitcode .exitcode
As another example, consider the small McDonald's diet model. Its objective functions are defined as:if SubPoint.result = "infeasible" then { ...
After minimizing three of these objectives, we can view the solve status values for all of them:minimize Total_Cost: sum {j in FOOD} cost[j] * Buy[j]; minimize Nutr_Amt {i in NUTR}: sum {j in FOOD} amt[i,j] * Buy[j];
For the objectives that have not yet been used, the .result suffix is unchanged (at its initial value of '?' in this case).ampl: model diet1.mod; ampl: data diet1.dat; ampl: solve; CPLEX 5.0: optimal integer solution; objective 15.05 74 simplex iterations 69 branch-and-bound nodes Objective = Total_Cost ampl: objective Nutr_Amt['Cal']; ampl: solve; CPLEX 5.0: optimal integer solution; objective 2500 11 simplex iterations 3 branch-and-bound nodes ampl: objective Nutr_Amt['VitC']; ampl: solve; CPLEX 5.0: optimal integer solution; objective 100 34 simplex iterations 26 branch-and-bound nodes ampl: display Total_Cost.result, Nutr_Amt.result; Total_Cost.result = solved Nutr_Amt.result [*] := Cal solved Carbo '?' Protein '?' VitA '?' VitC solved Calc '?' Iron '?' ;
Solver statuses of variables
In addition to providing for return of the overall status of
the optimization process as described above, AMPL lets a solver return
an individual status for each variable. This feature is intended
mainly for reporting the basis status of variables after a linear
program is solved either by the simplex method, or by an interior-point
(barrier) method followed by a "crossover" routine. The basis status
is also relevant to solutions returned by certain nonlinear solvers,
notably MINOS,
that employ an extension of the concept of a basic solution.
In addition to the variables declared by var statements in an AMPL model, solvers also define "slack" or "artificial" variables that are associated with constraints. Solver statuses for these latter variables are defined in a similar way, as explained in the next section. Both variables and constraints also have an "AMPL status" that distinguishes those in the current problem from those that have been removed from the problem by presolve or by commands such as drop. The interpretation of AMPL statuses and their relationship to solver statuses are considered in a subsequent section.
The major use of solver status values from an optimal basic solution is to provide a good starting point for the next optimization run. The new option send_statuses, when left at its default value of 1, instructs AMPL to include statuses with the information about variables sent to the solver at each solve. You can see the effect of this feature in almost any sensitivity analysis that re-solves after making some small change to the problem. As an example, consider what happens when the multi-period production example from the AMPL book is solved repeatedly after increases of 5% in the availability of labor. With the send_statuses option set to 0, the solver reports about 30 iterations of the simplex method each time it is run:
With send_statuses left at its default value of 1, however, only the first solve takes 30 iterations. Subsequent runs take a few iterations at most:ampl: model steelT3.mod; ampl: data steelT3.dat; ampl: option send_statuses 0; ampl: solve; CPLEX 5.0: optimal solution; objective 514521.7143 30 iterations (1 in phase I) ampl: let {t in 1..T} avail[t] := 1.05 * avail[t]; ampl: solve; CPLEX 5.0: optimal solution; objective 537104 31 iterations (1 in phase I) ampl: let {t in 1..T} avail[t] := 1.05 * avail[t]; ampl: solve; CPLEX 5.0: optimal solution; objective 560800.4 31 iterations (1 in phase I) ampl: let {t in 1..T} avail[t] := 1.05 * avail[t]; ampl: solve; CPLEX 5.0: optimal solution; objective 585116.22 30 iterations (1 in phase I)
Each solve after the first automatically uses the variables' basis statuses from the previous solve to construct a starting point that turns out to be only a few iterations from the optimum. In the case of the third solve, the previous basis remains optimal; the solver thus confirms optimality immediately and reports taking 0 iterations.ampl: model steelT3.mod; ampl: data steelT3.dat; ampl: solve; CPLEX 5.0: optimal solution; objective 514521.7143 30 iterations (1 in phase I) ampl: let {t in 1..T} avail[t] := 1.05 * avail[t]; ampl: solve; CPLEX 5.0: optimal solution; objective 537104 3 iterations (2 in phase I) ampl: let {t in 1..T} avail[t] := 1.05 * avail[t]; ampl: solve; CPLEX 5.0: optimal solution; objective 560800.4 0 iterations ampl: let {t in 1..T} avail[t] := 1.05 * avail[t]; ampl: solve; CPLEX 5.0: optimal solution; objective 585116.22 2 iterations (1 in phase I)
The following discussion explains how you can view, interpret, and change variables' status values in the AMPL environment. You don't need to know any of this to use optimal bases as starting points as shown above, but these features can be useful in certain advanced circumstances.
AMPL refers to a variable's solver status by appending .sstatus to its name. Thus you can exhibit the statuses of variables by use of the display command. At the beginning of a session (or after a reset), when no problem has yet been solved, all variables have the status none:
After an invocation of a simplex method solver, the same display lists the statuses of the variables at the optimal basic solution:ampl: model diet.mod; ampl: data diet2a.dat; ampl: display Buy.sstatus; Buy.sstatus [*] := BEEF none CHK none FISH none HAM none MCH none MTL none SPG none TUR none ;
Two of the variables -- Buy['BEEF'] and Buy['SPG'] -- have status bas, which means they are in the optimal basis. Three have status low and three upp, indicating that they are nonbasic at lower and upper bound, respectively. A table of the recognized solver status values is stored in the AMPL option sstatus_table:ampl: solve; MINOS 5.5: optimal solution found. 13 iterations, objective 118.0594032 ampl: display Buy.sstatus; Buy.sstatus [*] := BEEF bas CHK low FISH low HAM upp MCH upp MTL upp SPG bas TUR low ;
Numbers and short strings representing status values are given in the first two columns. (The numbers are mainly for communication between AMPL and solvers, though you can access them by using the suffix .sstatus_num in place of .sstatus.) The entries in the third column are comments. For nonbasic variables as defined in many textbook simplex methods, only the low status is applicable; other nonbasic statuses are required for the more general bounded-variable simplex methods in large-scale implementations. The sup status is used by solvers like MINOS to accommodate nonlinear problems. This is AMPL's standard sstatus_table; a solver may substitute its own table, in which case its instructions will indicate the interpretation of the table entries.ampl: option sstatus_table; option sstatus_table '\ 0 none no status assigned\ 1 bas basic\ 2 sup superbasic\ 3 low nonbasic <= (normally =) lower bound\ 4 upp nonbasic >= (normally =) upper bound\ 5 equ nonbasic at equal lower and upper bounds\ 6 btw nonbasic between bounds\ ';
You can change a variable's status by use of AMPL's let command. This facility is sometimes useful when you want to re-solve a problem after a small, well-defined change. For example, consider the variables defined in our cutting-stock model, cut1.mod:
At each pass through the Gilmore-Gomory procedure, as implemented in cut1.run, nPAT is stepped by one, causing a new variable Cut[nPAT] to be created. It has an initial solver status of "none", like all new variables, but it is guaranteed -- by the way that the pattern generation procedure is constructed -- to enter the basis as soon as the expanded cutting problem is re-solved. Thus we give it a status of "bas" instead:param nPAT integer >= 0; # number of patterns set PATTERNS := 1..nPAT; # set of patterns var Cut {PATTERNS} integer >= 0; # rolls cut using each pattern
It turns out that this change tends to reduce the number of iterations in each re-optimization of the cutting problem, at least with some simplex solvers. Setting a few statuses in this way is not guaranteed to reduce the number of iterations, however. Its success depends on the particular problem and solver, and on their interaction with a number of complicating factors:let Cut[nPAT].sstatus := "bas";
For models that have several var declarations, AMPL's generic synonyms for variables provide a convenient way of getting information about the statuses of all variables. Using expressions such as _var, _varname and _var.sstatus in a display statement, you can produce a quick summary table of the results. You can also easily list the names of all variables that satisfy some condition, by using the statuses to define an appropriate subset of the variables. Here, for example, are the names of all the basic variables:
To display the values of the variables along with their names, you could use the same statement with _varname[j] replaced by (_varname[j],_var[j]).ampl: display {j in 1.._nvars: _var[j].sstatus = "bas"} _varname[j]; _varname[j] [*] := 1 "Make['bands',1]" 2 "Make['bands',2]" 3 "Make['bands',3]" 4 "Make['bands',4]" 5 "Make['coils',1]" 6 "Make['coils',2]" 7 "Make['coils',3]" 8 "Make['coils',4]" 15 "Inv['coils',1]" 22 "Sell['bands','east',4]" 32 "Sell['coils','west',2]" 33 "Sell['coils','west',3]" ;
A simplex solver gains two advantages from having these "logical" variables added to the "structural" ones its gets from AMPL:
To accommodate statuses of logical variables, AMPL permits a solver to return status values corresponding to the constraints as well as the variables. The solver status of a constraint -- written as the constraint name suffixed by .sstatus -- is interpreted as the status of the logical variable associated with that constraint. For example, in our diet model, where the constraints are all inequalities,
the logical variables are slacks that have the same variety of statuses as the structural variables:subject to diet {i in NUTR}: n_min[i] <= sum {j in FOOD} amt[i,j] * Buy[j] <= n_max[i];
There are a total of 6 basic variables, equal in number to the 6 constraints (one for each member of set NUTR) as is always the case at a basic solution. In our transportation model, where the constraints are equalities,ampl: model diet.mod; ampl: data diet2a.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.5: optimal solution found. 13 iterations, objective 118.0594032 ampl: display Buy.sstatus; Buy.sstatus [*] := BEEF bas CHK low FISH low HAM upp MCH upp MTL upp SPG bas TUR low ; ampl: display diet.sstatus; diet.sstatus [*] := A bas B1 bas B2 low C bas CAL bas NA upp ;
the logical variables are artificials that receive the status "equ" when nonbasic. Here's how the statuses for all constraints might be displayed using AMPL's constraint generic synonyms (analogous to the variable synonyms previously shown):subject to Supply {i in ORIG}: sum {j in DEST} Trans[i,j] = supply[i]; subject to Demand {j in DEST}: sum {i in ORIG} Trans[i,j] = demand[j];
One artificial variable, on the constraint Demand['FRA'], is in the optimal basis, though at a value of zero like all artificial variables in any feasible solution. (In fact there must be some artificial variable in every basis for this problem, due to a linear dependency among the equations in the model.)ampl: model transp.mod; ampl: data transp.dat; ampl: solve; MINOS 5.5: optimal solution found. 13 iterations, objective 196200 ampl: display _conname,_con.sstatus; : _conname _con.sstatus := 1 "Supply['GARY']" equ 2 "Supply['CLEV']" equ 3 "Supply['PITT']" equ 4 "Demand['FRA']" bas 5 "Demand['DET']" equ 6 "Demand['LAN']" equ 7 "Demand['WIN']" equ 8 "Demand['STL']" equ 9 "Demand['FRE']" equ 10 "Demand['LAF']" equ ;
Here's an example of the most common cases, using one of our diet models:ampl: option astatus_table; option astatus_table '\ 0 in normal state (in problem)\ 1 drop removed by drop command\ 2 pre eliminated by presolve\ 3 fix fixed by fix command\ 4 sub defined variable, substituted out\ 5 unused not used in current problem\ ';
An AMPL status of "in" indicates components that are in the problem sent to the solver, such as variable Buy['BEEF'] and constraint diet_min['A']. Three other statuses indicate components left out of the problem:ampl: model dietu.mod; ampl: data dietu.dat; ampl: drop diet_min['CAL']; ampl: fix Buy['SPG'] := 5; ampl: fix Buy['CHK'] := 3; ampl: solve; MINOS 5.5: optimal solution found. 3 iterations, objective 54.76 ampl: display Buy.astatus; Buy.astatus [*] := BEEF in CHK fix FISH in HAM in MCH in MTL in SPG fix TUR in ; ampl: display diet_min.astatus; diet_min.astatus [*] := A in B1 pre B2 pre C in CAL drop ;
Not shown here are the AMPL status "unused" for a variable that does not appear in any objective or constraint, and "sub" for variables and constraints eliminated by substitution (as explained on pages 247-248 of the AMPL book).ampl: model dietobj.mod; ampl: data dietobj.dat; ampl: objective total_cost['JEWEL']; ampl: solve; MINOS 5.5: optimal solution found. 8 iterations, objective 74.3532967 ampl: display total_cost.astatus; total_cost.astatus [*] := 'A&P' drop JEWEL in VONS drop ;
For a variable or constraint, you will normally be interested in only one of the statuses at a time: the solver status if the variable or constraint was included in the problem sent most recently to the solver, or the AMPL status otherwise. Thus AMPL provides the suffix .status to denote the one status of interest:
In general, name.status is equal to name.sstatus if name.astatus is "in", and is equal to name.astatus otherwise.ampl: display Buy.status, Buy.astatus, Buy.sstatus; : Buy.status Buy.astatus Buy.sstatus := BEEF low in low CHK fix fix none FISH low in low HAM low in low MCH bas in bas MTL low in low SPG fix fix none TUR low in low ; ampl: display diet_min.status, diet_min.astatus, diet_min.sstatus; : diet_min.status diet_min.astatus diet_min.sstatus := A bas in bas B1 pre pre none B2 pre pre none C low in low CAL drop drop none ;
Return to the AMPL update page.