Draft Writeup of May 12, 1995

LOOPING AND TESTING 1:
writing "scripts" in the AMPL command language

New for, repeat, if and related commands let you write small programs, or scripts, in the AMPL command language. Another collection of new commands let you step through a script, for purposes of observation or debugging.

This section introduces these commands, using formatted printing and sensitivity analysis as examples. For those who want to get started faster, a syntax summary for all these commands appears at the end of this section.

The following section describes additional commands and features that permit you to express new algorithmic schemes by means of AMPL scripts.


Running scripts

Just as the model command reads files of model declarations, and the data command reads files of data statements, the AMPL commands command reads files containing AMPL scripts -- lists of commands -- that are to be executed. Simple scripts can save you the trouble of typing the same sequences of commands again and again. More sophisticated scripts can carry out repetitive actions that would be impractical to type one at a time, as we will show shortly.

As an example, consider how we might perform a simple sensitivity analysis on the multi-period production problem of Section 4.2. Only 32 hours of production time are available in week 3, compared to 40 hours in the other weeks. Suppose that we want to see how much extra profit could be gained for each extra hour in week 3. We can accomplish this by repeatedly solving, displaying the solution values, and increasing avail[3]:

	ampl: model steelT.mod;
	ampl: data steelT.dat;

	ampl: solve;
	MINOS 5.4: optimal solution found.
	15 iterations, objective 515033

	ampl: display total_profit >steelT.sens;
	ampl: option display_1col 0;
	ampl: option omit_zero_rows 0; 
	ampl: display Make >steelT.sens;
	ampl: display Sell >steelT.sens;
	ampl: option display_1col 20;
	ampl: option omit_zero_rows 1;
	ampl: display Inv >steelT.sens;

	ampl: let avail[3] := avail[3] + 5;

	ampl: solve;
	MINOS 5.4: optimal solution found.
	6 iterations, objective 532033

	ampl: display total_profit >steelT.sens;
	ampl: option display_1col 0;
	ampl: option omit_zero_rows 0; 
	ampl: display Make >steelT.sens;
	ampl: display Sell >steelT.sens;
	ampl: option display_1col 20;
	ampl: option omit_zero_rows 1;
	ampl: display Inv >steelT.sens;

	ampl: let avail[3] := avail[3] + 5;

	ampl: solve;
	MINOS 5.4: optimal solution found.
	6 iterations, objective 549033

	ampl:
To try all integer values of avail[3] from 32 to 77, we would complete another seven cycles in the same way. All of the solution displays are saved in the file steelT.sens, although we could just as well have made them appear on the screen.

To avoid having to type the same commands again and again, we can create a new file listing the commands to be repeated:

	solve;

	display total_profit >steelT.sens;

	option display_1col 0;
	option omit_zero_rows 0;
	display Make >steelT.sens;
	display Sell >steelT.sens;

	option display_1col 20;
	option omit_zero_rows 1;
	display Inv >steelT.sens;

	let avail[3] := avail[3] + 5;
If we call this file steelT.sa1, then we can execute all the commands in it by typing just the one line commands steelT.sa1:
	ampl: model steelT.mod;
	ampl: data steelT.dat;

	ampl: commands steelT.sa1;
	MINOS 5.4: optimal solution found.
	15 iterations, objective 515033

	ampl: commands steelT.sa1;
	MINOS 5.4: optimal solution found.
	6 iterations, objective 532033

	ampl: commands steelT.sa1;
	MINOS 5.4: optimal solution found.
	6 iterations, objective 549033

	ampl: commands steelT.sa1;
	MINOS 5.4: optimal solution found.
	7 iterations, objective 565193

	ampl:
We refer to the contents of a file like steelT.sa1 as a script. The looping and testing commands to be described in this section are most valuable when they are used within scripts.

A script can itself contain a commands command that refers to another script. Scripts can be nested in this way to a depth of up to 10.

[[Can alternatively use the include command -- makes no difference here, but does inside a loop as in the next section]]


Iterating over a set

The above example still requires that some command be typed over and over again. AMPL provides looping commands that can do this work automatically, with various options to determine how long the looping should continue.

We begin with the for command, which executes a statement or collection of statements once for each member of some set. To execute our multi-period production problem sensitivity analysis script four times, for example, we need only type a single for command followed by the command that we want to repeat:

	ampl: model steelT.mod;
	ampl: data steelT.dat;

	ampl: for {1..4} commands steelT.sa1;

	MINOS 5.4: optimal solution found.
	15 iterations, objective 515033
	MINOS 5.4: optimal solution found.
	6 iterations, objective 532033
	MINOS 5.4: optimal solution found.
	6 iterations, objective 549033
	MINOS 5.4: optimal solution found.
	7 iterations, objective 565193

	ampl: 
The expression between for and the command can be any AMPL indexing expression, as we will see in later examples.

As an alternative, we can add the for command to the script. We then want it to loop over a whole series of commands; this is accomplished by enclosing the commands in braces:

	model steelT.mod;
	data steelT.dat;

	for {1..4} {
	   solve;

	   display total_profit >steelT.sens;

	   option display_1col 0;
	   option omit_zero_rows 0;
	   display Make >steelT.sens;
	   display Sell >steelT.sens;

	   option display_1col 20;
	   option omit_zero_rows 1;
	   display Inv >steelT.sens;

	   let avail[3] := avail[3] + 5;
	}
If this script is stored in steelT.sa2, then the whole iterated sensitivity analysis is carried out by simply typing commands steelT.sa2.

This latter approach tends to be clearer and easier to work with, particularly as we make the loop more sophisticated. As a first example, consider how we would go about compiling a table of the objective and the dual value on constraint time[3], for successive values of avail[3]. A script for this purpose is shown in Figure 1. After the model and data are read, the script provides additional declarations for the table of values:

	set AVAIL3;
	param avail3_obj {AVAIL3};
	param avail3_dual {AVAIL3};
The set AVAIL3 will contain all the different values for avail[3] that we want to try; for each such value a, avail3_obj[a] and avail3_dual[a] will be the associated objective and dual values. Once these are set up, we assign the set value to AVAIL3,

	let AVAIL3 := avail[3] .. avail[3] + 15 by 5;
and then use a for loop to iterate over this set:

	for {a in AVAIL3} {

	   let avail[3] := a;

	   solve;

	   let avail3_obj[a] := total_profit;
	   let avail3_dual[a] := time[3].dual;
	}
We see here that a for loop can be over an arbitrary set, and that the index running over the set (a in this case) can be used in statements within the loop. After the loop is complete, the desired table is produced by displaying avail3_obj and avail3_dual, as shown at the end of the script in Figure 1. If this script is stored in steelT.sa3, then the desired results are produced with a single command:

	ampl: include steelT.sa3

	:  avail3_obj avail3_dual :=
	32    515033      3400
	37    532033      3400
	42    549033      3400
	47    565193      2980
	;
In this case we have suppressed the messages from the solver, by including the command option solver_msg 0 in our script.


--------------------------------------------------------------- model steelT.mod; data steelT.dat; set AVAIL3; param avail3_obj {AVAIL3}; param avail3_dual {AVAIL3}; let AVAIL3 := avail[3] .. avail[3] + 15 by 5; option solver_msg 0; for {a in AVAIL3} { let avail[3] := a; solve; let avail3_obj[a] := total_profit; let avail3_dual[a] := time[3].dual; } display avail3_obj, avail3_dual; ---------------------------------------------------------------
Figure 1. A script for recording sensitivity to the value of the parameter avail[3], using a solve within a for loop (steelT.sa3).


AMPL's for loops are also very convenient for generating formatted tables. Suppose that after solving the multi-period production problem, you want to display sales both in tons and as a percentage of the market limit. You could use a display command to produce a table like this:

	ampl: display {t in 1..T, p in PROD}
	ampl?   (Sell[p,t], 100*Sell[p,t]/market[p,t]);

	:       Sell[p,t] 100*Sell[p,t]/market[p,t]    :=
	1 bands    6000            100
	1 coils     307              7.675
	2 bands    6000            100
	2 coils    2500            100
	3 bands    1400             35
	3 coils    3500            100
	4 bands    2000             30.7692
	4 coils    4200            100
	;
By writing a script that uses AMPL's printf command (Section A.13.1), you can create a much more effective table:

	ampl: commands steelT.tab1

	SALES         bands            coils   
	week 1     6000  100.0%      307    7.7%
	week 2     6000  100.0%     2500  100.0%
	week 3     1399   35.0%     3500  100.0%
	week 4     1999   30.8%     4200  100.0%
The script to write this table can be as short as two printf commands:

	printf "\n%s%14s%17s\n", "SALES", "bands", "coils";

	printf {t in 1..T}: "week %d%9d%7.1f%%%9d%7.1f%%\n", t,
	   Sell["bands",t], 100*Sell["bands",t]/market["bands",t],
	   Sell["coils",t], 100*Sell["coils",t]/market["coils",t];
This approach is undesirably restrictive, however, because it assumes that there will always be two products and that they will always be named coils and bands. In fact the printf statement cannot write a table in which both the number of rows and the number of columns depend on the data, because the number of entries in its format string is always fixed.

A more generally applicable script for generating the above table, using a for loop over 1..T, is shown in Figure 2. Each pass through the loop generates one row of the table. There are more printf statements than in the previous example, but they are shorter and simpler. We use several statements to write the contents of each line; printf does not begin a new line except where \n appears in its format string.


--------------------------------------------------------------- printf "\nSALES"; printf {p in PROD}: "%14s ", p; printf "\n"; for {t in 1..T} { printf "week %d", t; for {p in PROD} { printf "%9d", Sell[p,t]; printf "%7.1f%%", 100 * Sell[p,t]/market[p,t]; } printf "\n"; } ---------------------------------------------------------------
Figure 2. A script for generating a formatted sales table, using nested for loops (steelT.tab).


Within the "outer" loop over 1..T we generate each line's product entries by use of an "inner" loop over PROD. Loops can in general be nested to any depth, and may be over any set that can be represented by an AMPL set expression. There is one pass through the loop for every member of the set, and if the set is ordered -- any set of numbers like 1..T, or a set declared ordered or circular -- then the order of the passes is determined by the ordering of the set. If the set is unordered (like PROD) then AMPL chooses the order of the passes, but the choice is the same every time; the Figure 2 script relies on this consistency to insure that all of the entries in one column of the table refer to the same product.

[[Note: alternatives not using for]]


Iterating subject to a condition

A second kind of AMPL looping construct, the repeat statement, continues iterating as long as some logical condition is satisfied.

Returning to the multi-period production example, we observe that the dual value on the constraint time[3] provides an upper limit on the additional profit that can be realized from each extra hour added to avail[3]. When avail[3] is made sufficiently large, so that there is more third-week capacity than can be used, the associated dual value falls to zero and further increases in avail[3] have no effect on the optimal solution. Either the expression time[3] alone, or time[3].dual, represents the dual value in AMPL expressions.

We can specify that looping should stop once the dual value falls to zero, by writing a repeat statement that has one of the following forms:

	repeat while time[3].dual > 0 { . . . };
	repeat until time[3].dual = 0 { . . . };

	repeat { . . . } while time[3].dual > 0;
	repeat { . . . } until time[3].dual = 0;
The loop body, here indicated by the placeholder { . . . }, must be enclosed within braces. Passes through the loop continue as long as the condition after while is true, or as long as the condition after until is false. A condition written before the loop body is tested before every pass; if a while condition is false or an until condition is true before the first pass, then the loop is never executed. A condition written after the loop body is tested after every pass, so that the loop is executed at least once in every case.

A complete script using repeat is shown in Figure 3. The until phrase is placed after the loop body, so that time[3].dual will not be tested until after a solve has been executed in the first pass. Two other features of this script are worth noting, as they are relevant to many AMPL scripts of this kind.


--------------------------------------------------------------- model steelT.mod; data steelT.dat; option solution_precision 10; option solver_msg 0; set AVAIL3 default {}; param avail3_obj {AVAIL3}; param avail3_dual {AVAIL3}; param avail3_step := 5; repeat { solve; let AVAIL3 := AVAIL3 union {avail[3]}; let avail3_obj[avail[3]] := total_profit; let avail3_dual[avail[3]] := time[3].dual; let avail[3] := avail[3] + avail3_step; } until time[3].dual = 0; display avail3_obj, avail3_dual; ---------------------------------------------------------------
Figure 3. A script for recording sensitivity to the value of avail[3], using a repeat statement to continue until the associated dual value is zero (steelT.sa4).


At the beginning of the script, we don't know how many passes the repeat statement will make through the loop. Thus we cannot determine AVAIL3 in advance as we did in Figure 1. Instead, we declare it initially to be the empty set,

	set AVAIL3 default {};
	param avail3_obj {AVAIL3};
	param avail3_dual {AVAIL3};
and add each new value of avail[3] to it after solving:

	let AVAIL3 := AVAIL3 union {avail[3]};
	let avail3_obj[avail[3]] := total_profit;
	let avail3_dual[avail[3]] := time[3].dual;
By adding a new member to AVAIL3, we also create new components of the parameters avail3_obj and avail3_dual that are indexed over AVAIL3, and so we can proceed to assign the appropriate values to these components. Any change to an AMPL set is propagated to all declarations that use the set, in the same way that any change to a parameter is propagated.

Because numbers in the computer are represented with a limited number of bits of precision, a solver may return values that differ very slightly from the solution that would be computed using exact arithmetic. Ordinarily you don't see this, because AMPL's display command is set by default to round values to six significant digits. Compare what is shown when rounding is dropped, by setting display_precision to 0:

	ampl: display Make;

	Make [*,*] (tr)
	:  bands  coils    :=
	1   5990   1407
	2   6000   1400
	3   1400   3500
	4   2000   4200
	;

	ampl: option display_precision 0;
	ampl: display Make;

	Make [*,*] (tr)
	:         bands                coils           :=
	1   5989.999999999999    1407.0000000000002
	2   6000                 1399.9999999999998
	3   1399.9999999999995   3500
	4   1999.9999999999993   4200
	;
These seemingly tiny differences can have undesirable effects whenever a script makes a comparison that uses values returned by the solver. The rounded table would lead you to believe that Make["coils",2] >= 1400 is true, for example, whereas from the second table you can see that really it is false.

You can avoid this kind of surprise by writing your tests more carefully; instead of until time[3].dual = 0, for instance, you might say until time[3].dual <= 0.0000001. Alternatively, you can instruct AMPL to round all solution values that are returned by the solver, so that number that are supposed to be equal really do come out equal. The statement

        option solution_precision 10; 
toward the beginning of Figure 3 has this effect; it states that solution values are to be rounded to 10 significant digits. These and related options are discussed and illustrated in Section 10.5 of the AMPL book.

Note finally that the script declares set AVAIL3 as default {} rather than := {}. The former allows AVAIL3 to be changed by let commands as the script proceeds, whereas the latter permanently defines AVAIL3 to be the empty set.


Testing a condition

AMPL's new if command takes a specified action conditionally. In the simplest case, the action is the execution of one AMPL command:

	if Make["coils",2] < 1500 then printf "under 1500\n";
The action may also be a series of AMPL commands grouped by braces as in the for and repeat commands:

	if Make["coils",2] < 1500 then {
	   printf "Fewer than 1500 coils in week 2.\n";
	   let market["coils",2] := market["coils",2] * 1.1;
	}
An optional else specifies an alternative action that also may be either a single command,

	if Make["coils",2] < 1500 then {
	   printf "Fewer than 1500 coils in week 2.\n";
	   let market["coils",2] := market["coils",2] * 1.1;
	}
	else printf "More than 1500 coils in week 2.\n";
or a group of commands:

	if Make["coils",2] < 1500 then printf "under 1500\n";
	else {
	   printf "over 1500\n";
	   let market["coils",2] := market["coils",2] * 0.9;
	};
AMPL executes these commands by first evaluating the logical expression following if. If the expression is true then the command or commands following then are executed. If the expression is false then the command or commands following else, if any, are executed.

The if command is most useful for regulating the flow of control in scripts. In Figure 2, we could suppress the many occurrences of 100% by placing the statement that prints Sell[p,t]/market[p,t] inside an if:

	if Sell[p,t] < market[p,t] then
	   printf "%7.1f%%", 100 * Sell[p,t]/market[p,t];
	else printf "    --- ";
In Figure 3, we can use an if command in the repeat loop to test whether the dual value has changed since the previous pass through the loop:

	let avail[3] := 1;
	param avail3_step := 1;
	param previous_dual default Infinity;

	repeat while previous_dual > 0 {

	   solve;

	   if time[3].dual < previous_dual then {
	      let AVAIL3 := AVAIL3 union {avail[3]};
	      let avail3_obj[avail[3]] := total_profit;
	      let avail3_dual[avail[3]] := time[3].dual;
	      let previous_dual := time[3].dual;
	      }

	   let avail[3] := avail[3] + avail3_step;
	   }
When used within the Figure 3 script, this loop creates a table that has exactly one entry for each different dual value discovered.

If you run the Figure 3 script with the above changes (steelT.sa5), you will find that the dual value changes when avail[3] is increased from 22 to 23 hours. Figure 4 exhibits a script (steelT.sa6) that carries out a binary search to determine more exactly (to 7 decimal places) the value of avail[3] where the dual value changes:

	ampl: commands steelT.sa6;

	Dual value 3620.000000 for avail[3] < 22.807142
	Dual value 3500.000000 for avail[3] > 22.807144
The if in this script,

	if time[3].dual = dual_lower 
	   then let avail3_lower := avail[3];
	   else let avail3_upper := avail[3];
is key to the binary search algorithm, since it determines which half of the current search interval is removed at each step. The equality at the beginning of this if statement is another example of a comparison involving numbers returned by the solver. Thus as before the option solution_precision is set to 10 at the start of the script; in this case the results would be wrong otherwise.


--------------------------------------------------------------- model steelT.mod; data steelT.dat; option solution_precision 10; option solver_msg 0; param avail3_lower default 22; param dual_lower; param avail3_upper default 23; param dual_upper; let avail[3] := avail3_lower; solve; let dual_lower := time[3].dual; let avail[3] := avail3_upper; solve; let dual_upper := time[3].dual; repeat { let avail[3] := (avail3_lower + avail3_upper) / 2; solve; if time[3].dual = dual_lower then let avail3_lower := avail[3]; else let avail3_upper := avail[3]; } until (avail3_upper - avail3_lower) / avail[3] < 0.0000001; printf "Dual value %11.6f for avail[3] < %9.6f\n", dual_lower, avail3_lower; printf "Dual value %11.6f for avail[3] > %9.6f\n", dual_upper, avail3_upper; ---------------------------------------------------------------
Figure 4. A script that performs a binary search to determine the level of avail[3] at which the associated dual value changes (steelT.sa6).


The Figure 4 script requires an initial interval, bounded by avail3_lower and avail3_upper, such that the dual value changes once as avail[3] moves through the interval. If the dual value might change more than once, then the following if ensures that the script will find the largest value of avail[3] at which there is a change:

	   if time[3].dual > dual_upper 
	      then {
	         let avail3_lower := avail[3];
	         let dual_lower := time[3].dual;
	      }
	      else let avail3_upper := avail[3];
A further refinement counts the number of times that the search detects an additional change in the dual value within the initial interval:

	   if time[3].dual = dual_lower 
	      then let avail3_lower := avail[3];
	      else if time[3].dual = dual_upper 
	         then let avail3_upper := avail[3];
	         else {
	            let other_bkpts := other_bkpts + 1;
	            let avail3_lower := avail[3];
	            let dual_lower := time[3].dual;
	         };
Here we see how the statement following else can be another if, giving a chain of tests. [[Each nested else paired with nearest available if.]]


Terminating a loop

Two other new AMPL commands work with looping statements to make some scripts easier to write. The continue command terminates the current pass through a for or while loop; all further statements in the current pass are skipped, and execution continues with the start of the next pass (if any). The break command completely terminates a for or while loop, sending control immediately to the statement following the end of the loop.

As an example of both these commands, Figure 6 exhibits another way of rewriting the loop from the Figure 3 script, so that a table entry is made only when there is a change in the dual value associated with avail[3]. After solving, we test to see if the new dual value is equal to the previous one:

   if time[3].dual = previous_dual then continue;
If yes, there is nothing to be done for this value of avail[3]. Thus we use the continue command to jump to the end of the current pass; execution resumes with the next pass, starting at the beginning of the loop.


--------------------------------------------------------------- model steelT.mod; data steelT.dat; option solution_precision 10; option solver_msg 0; set AVAIL3 default {}; param avail3_obj {AVAIL3}; param avail3_dual {AVAIL3}; let avail[3] := 0; param previous_dual default Infinity; repeat { let avail[3] := avail[3] + 1; solve; if time[3].dual = previous_dual then continue; let AVAIL3 := AVAIL3 union {avail[3]}; let avail3_obj[avail[3]] := total_profit; let avail3_dual[avail[3]] := time[3].dual; if time[3].dual = 0 then break; let previous_dual := time[3].dual; } display avail3_obj, avail3_dual; ---------------------------------------------------------------
Figure 5. An alternative to the sensitivity analysis script in Figure 3, using the continue and break commands (
steelT.sa7).


After adding an entry to the table, we test to see if the dual value has fallen to zero:

	   if time[3].dual = 0 then break;
If yes, we are done with the loop. We use the break command to jump out of the loop; execution passes to the display command that follows the loop in the script. Since the repeat statement in this example has no while or until conditions, it relies on the break command for termination.

When a break or continue lies within more than one loop, it applies only to the innermost loop. This convention generally has the effect desired. As an example, consider how we could expand Figure 5 to perform a separate sensitivity analysis on each avail[t]. The repeat loop would be nested in a for {t in 1..T} loop (steelT.sa7a), but the continue and break commands would apply to the inner repeat as before.

There do exist situations in which the logic of a script requires breaking out of a loop other than the inner loop. In the script of Figure 5, for instance, we can imagine that instead of stopping when the time[3].dual is zero,

	if time[3].dual = 0 then break;
we want to stop when time[t].dual falls below 2700 for any t. One way to implement this criterion is:

	for {t in 1..T} 
	   if time[t].dual < 2700 then break;
This statement does not have the desired effect, however, because break applies only to the inner for loop that contains it, rather than to the outer repeat loop as we desire. To deal with these kinds of situations, AMPL lets you give a name to any loop, so that any break or continue may specify by name the loop to which it should apply. Using this feature, the outer loop in our example could be named sens_loop:

	repeat sens_loop {
	   . . .
	}
and the inner loop would be
	for {t in 1..T} 
	   if time[t].dual < 2700 then break sens_loop;
The loop name always comes immediately after repeat or for, and after break or continue.


Stepping through a script

If you think that a script might not be doing what you want it to, you can direct AMPL to step through it one command at a time. This facility can be used to provide an elementary form of "symbolic debugger" for AMPL scripts.

To step through a script that does not execute any other scripts, simply reset the AMPL option single_step from its default value of zero to one. For example, here is how you might begin stepping through the script in Figure 4:

	ampl: option single_step 1;

	ampl: commands steelT.sa6;

	steelT.sa6:3(19)   data ...

	<2>ampl: 
The expression steelT.sa6:3(19) gives the file name, line number and character number where AMPL has stopped in its processing of the script. It is followed by the beginning of the next command (data) to be executed. One the next line you are returned to the ampl prompt; the <2> in front means [[level of prompt, to be explained]].

At this point you may use the step command to execute individual commands of the script. Type step by itself to execute one command,

	<2>ampl: step
	steelT.sa6:5(37)   option ...

	<2>ampl: step
	steelT.sa6:6(67)   option ...

	<2>ampl: step
	steelT.sa6:11(188)   let ...

	<2>ampl: 
or type step followed by a number to execute that number of commands:

	<2>ampl: step 3
	steelT.sa6:14(258)   let ...

	<2>ampl: 
Every command is counted except those having to do with model declarations, such as model and param in this example.

Each step returns you to an AMPL prompt as before. You may continue in this way, but at some point you will want to display some values to see if the script is working as you intended:

	<2>ampl: display avail[3], dual_lower;
	avail[3] = 22
	dual_lower = 3620

	<2>ampl: step 3
	steelT.sa6:17(328)   repeat ...

	<2>ampl: display avail[3], dual_upper;
	avail[3] = 23
	dual_upper = 3500

	<2>ampl: 
Any series of AMPL commands may be typed while single-stepping. After each command, the <2>ampl prompt returns to remind you that you are still in this mode and may use step to continue executing the script.

At this point, execution of the script has reached the beginning of a repeat loop. The step command takes you into the first pass through the loop,

	<2>ampl: step
	steelT.sa6:19(341)   let ...

	<2>ampl: step
	steelT.sa6:21(396)   solve ...

	<2>ampl: 
and into the branch of the enclosed if command that is executed:

	<2>ampl: step
	steelT.sa6:23(407)   if ...

	<2>ampl: step
	steelT.sa6:24(448)   let ...

	<2>ampl: 
Further use of step will take you to the next pass of the loop, back at line 19,
	<2>ampl: step
	steelT.sa6:19(341)   let ...

	<2>ampl: step
	steelT.sa6:21(396)   solve ...

	<2>ampl: 
and similarly through all of the subsequent passes, after which control passes to the command following repeat.

To help you step through lengthy compound commands (for, repeat, or if) AMPL provides several alternatives to step. The next command steps past a compound command rather than into it. In our example, next would cause the entire repeat command to be executed, stopping again only at the following printf command on line 29:

	<2>ampl: step
	steelT.sa6:17(328)   repeat ...

	<2>ampl: next
	steelT.sa6:29(586)   printf ...

	<2>ampl: 
Type next n to step past n commands in this way.

The commands skip and skip n work like step and step n, except that they skip the next 1 or n commands in the script rather than executing them.


Syntax summary [to come]



Comments or questions?
Write to info@ampl.com or use our comment form.

Return to the AMPL update page.

Return to the AMPL home page.


LAST MODIFIED 24 JANUARY 1996 BY 4er.