AMPL > >Faqs

Sets and Indexing

Why doesn’t AMPL interpret the expression T..1 as I expect?

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.

Why does “set S := 50 .. 70” give me a set of only 3 members?

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.

I have declared “set S” and “param b {S}”. How do I write an AMPL expression for the arg min of b[i] — that is, the s in S such that b[s] equals the minimum of b[i] over all i in S?

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.

How can I get AMPL to index over the “power set” consisting of all subsets of a set?

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.

How can I apply AMPL’s ordered-set functions to pairs and higher-dimensional objects?

AMPL does not define any ordering on pairs, triples, or objects of higher dimension. Thus next, prev, 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, first, last 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.

Why do I get an “invalid subscript discarded” message when I display an AMPL parameter?

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.