Friday, October 28, 2016

GAMS Modeling: Inventing Set Element Labels

I often see models with set elements 1,2,…. Here is an example:

image

Of course if we have real data often we can use real names. But in some cases we need to use numbers as IDs. This is also typically the case when we don’t have real data and have to invent things. BTW, inventing data for a model is actually a very useful exercise: it will force you to think about the model in a more structured way.

If I have to invent labels I tend not to use 1,2,3,… but rather prefix the number by a string indicating what it is, e.g.:

set
  i
/i1*i10/
  j
/j1*j10/
  w
'warehouse' /wh1*wh12/

;

The reason to use these longer labels is that the output of the model becomes easier to interpret. Below is an example:

Bare numbers Prefixed numbers
set
  p
'product' /1*10/
  f
'factory' /1*4/
  w
'warehouse' /1*3/
  t
'time period' /1*5/

;

positive variables x(p,f,w,t) 'product flow from factory to warehouse';

Declarations
 

set
  p
'product' /pr1*pr10/
  f
'factory' /fact1*fact4/
  w
'warehouse' /wh1*wh3/
  t
'time period' /t1*t5/

;

positive variables x(p,f,w,t) 'product flow from factory to warehouse';

---- VAR x  product flow from factory to warehouse

              LOWER        LEVEL        UPPER       MARGINAL

1 .1.1.1        .          20.0000      +INF           .         
1 .1.1.2        .          85.0000      +INF           .         
1 .1.1.3        .          60.0000      +INF           .         
1 .1.1.4        .          35.0000      +INF           .         
1 .1.1.5        .          30.0000      +INF           .         
1 .1.2.1        .          25.0000      +INF           .         
1 .1.2.2        .          35.0000      +INF           .         
1 .1.2.3        .          90.0000      +INF           .         
1 .1.2.4        .          10.0000      +INF           .         
1 .1.2.5        .          55.0000      +INF           .         
1 .1.3.1        .         100.0000      +INF           .         
...

First part of the solution listing. Without recalling the declaration of variable x this is difficult to understand.

 

Using the prefixes we do not have to remember the ordering of the indices for this variable

---- VAR x  product flow from factory to warehouse

                      LOWER       LEVEL       UPPER      MARGINAL

pr1 .fact1.wh1.t1       .         20.0000     +INF          .         
pr1 .fact1.wh1.t2       .         85.0000     +INF          .         
pr1 .fact1.wh1.t3       .         60.0000     +INF          .         
pr1 .fact1.wh1.t4       .         35.0000     +INF          .         
pr1 .fact1.wh1.t5       .         30.0000     +INF          .         
pr1 .fact1.wh2.t1       .         25.0000     +INF          .         
pr1 .fact1.wh2.t2       .         35.0000     +INF          .         
pr1 .fact1.wh2.t3       .         90.0000     +INF          .         
pr1 .fact1.wh2.t4       .         10.0000     +INF          .         
pr1 .fact1.wh2.t5       .         55.0000     +INF          .         
pr1 .fact1.wh3.t1       .        100.0000     +INF          .
         
...

----     26 VARIABLE x.L  product flow from factory to warehouse

INDEX 1 = 1

              1           2           3           4           5

1.1      20.000      85.000      60.000      35.000      30.000
1.2      25.000      35.000      90.000      10.000      55.000
1.3     100.000      60.000     100.000      80.000      15.000
2.1      65.000      20.000      30.000      70.000      45.000
2.2      40.000      40.000      15.000      20.000      60.000
2.3      85.000      25.000      70.000      80.000      35.000
3.1      15.000      55.000      20.000      90.000      30.000
3.2      30.000      60.000      75.000      65.000      50.000
3.3      45.000      15.000      35.000       5.000      35.000
4.1      20.000      65.000      60.000      80.000      30.000
4.2      70.000      80.000      65.000      30.000      10.000
4.3      15.000      65.000      55.000       5.000      80.000

INDEX 1 = 2

              1           2           3           4           5

1.1      10.000      20.000      55.000      80.000      20.000
1.2       5.000      60.000      65.000      40.000      40.000
1.3      25.000      25.000      15.000      95.000      40.000
2.1      80.000      35.000      15.000      75.000      10.000

...

Same with a default display output. Again only partially reproduced here.
 

----     24 VARIABLE x.L  product flow from factory to warehouse

INDEX 1 = pr1

                   t1          t2          t3          t4          t5

fact1.wh1      20.000      85.000      60.000      35.000      30.000
fact1.wh2      25.000      35.000      90.000      10.000      55.000
fact1.wh3     100.000      60.000     100.000      80.000      15.000
fact2.wh1      65.000      20.000      30.000      70.000      45.000
fact2.wh2      40.000      40.000      15.000      20.000      60.000
fact2.wh3      85.000      25.000      70.000      80.000      35.000
fact3.wh1      15.000      55.000      20.000      90.000      30.000
fact3.wh2      30.000      60.000      75.000      65.000      50.000
fact3.wh3      45.000      15.000      35.000       5.000      35.000
fact4.wh1      20.000      65.000      60.000      80.000      30.000
fact4.wh2      70.000      80.000      65.000      30.000      10.000
fact4.wh3      15.000      65.000      55.000       5.000      80.000

INDEX 1 = pr2

                   t1          t2          t3          t4          t5

fact1.wh1      10.000      20.000      55.000      80.000      20.000
fact1.wh2       5.000      60.000      65.000      40.000      40.000
fact1.wh3      25.000      25.000      15.000      95.000      40.000
fact2.wh1      80.000      35.000      15.000      75.000      10.000
...

image With the GDX viewer things can become even more mysterious and difficult to decipher as we can flip dimensions around. You get a little bit of help with this:image
The 5th dimension is LO,L(evel),M,UP. We see we flipped dimensions 4 and 3 (t and w).
Here we see immediately what is going on. image

Notes

  1. It makes sense to use this approach even with real (not invented) numeric IDs. Once the data arrives in GAMS it is often not so easy to “repair” set element labels. If the data is sitting in a database it can be helpful to prefix numeric IDs in the SQL query:

    SELECT 'pr' || productid
    FROM
    table


    For a spreadsheet, often the easiest way is to insert a column or create a copy of the table with the new prefixed names.
  2. The construct i.val does not work with numbers that are prefixed. I usually work around this to create a parameter val(i) that contains the value of element i. If needed I create a superset of i that has an easy way to populate val(s). E.g.

    set
       s
    /i3*i10/
       i(s)
    /i3*i5,i8*i10/
    ;
    parameter val(s);
    val(s) =
    ord
    (s)+2;
    * now we can use val(i)

  3. It is not always possible to distinguish dimensions by their element names. E.g.

    set i 'nodes' /node1*node10/;
    alias
    (i,j);
    positive variable
    flow(i,j);


    Here, by nature, the two indices i and j have the same names because they have the same domain.