Monday, May 12, 2008

$ifthen does not nest (GAMS release 22.7 bug)

My first blog starts with a down note. I was working last night on this very large GAMS model with many model settings implemented as $set/$setglobal macros. In this context I want to nest some $ifthen constructs:

$set stochastic 0
$set erslivestockmodel 1

$ifthen %stochastic%==1
$ ifthen %erslivestockmodel%==1
b(i,"Exports",k) = tempbx(k);
b(i,"Season Average Price",k) = tempbp(k);
b(i,"Feed Use",k) = tempbdf(k);
b(i,"Food",k) = tempbdi(k);
cfixed(i,"Exports",k) = 0;
cfixed(i,"Season Average Price",k) = 0;
cfixed(i,"Feed Use",k) = 0;
cfixed(i,"Food",k) = 0;
$ else
$ include grains.inc
$ endif
$endif


This does not work as nesting is not allowed. You don't get an error message either indicating nesting is not allowed. In some cases you may get a possibly misleading error message, in the worst case it just behaves incorrectly. The documentation in the McCarl User Guide does not mention this limitation. Looks like $ifthen will be less useful in practice than I had hoped.

The workaround is to use a run-time if statement:

$set stochastic 0
$set erslivestockmodel 1

scalars
stochastic /%stochastic%/
erslivestockmodel /%erslivestockmodel%/
;
if(stochastic=1,
if(erslivestockmodel=1,
b(i,"Exports",k) = tempbx(k);
b(i,"Season Average Price",k) = tempbp(k);
b(i,"Feed Use",k) = tempbdf(k);
b(i,"Food",k) = tempbdi(k);
cfixed(i,"Exports",k) = 0;
cfixed(i,"Season Average Price",k) = 0;
cfixed(i,"Feed Use",k) = 0;
cfixed(i,"Food",k) = 0;
else
$ include grains.inc
);
);


This has additional advantages: all branches are syntax-checked even if they are not executed, and we check the type of the parameters using the scalar initializations. Of course we lose some of the compile time features using this approach.

Another workaround would be to place the inner $ifthen in an include file:

$set stochastic 0
$set erslivestockmodel 1

$ifthen %stochastic%==1
$include gamsbugworkaround.inc
$endif
Conclusion: GAMS users be aware when using nested $ifthen constructs. They are not implemented (correctly) and no error message is produced to warn you about this.

4 comments:

  1. Hi Erwin,

    Your first "workaround" is the only way I would think to do it. Can you share what are the "compile time features" you mention that would make your first attempt more interesting to you? Thanks!

    ReplyDelete
  2. When using if(condition,) compared to $ifthen you need to realize if(cond,) is executed at execution time.

    E.g.
    if(condition,
    $include xxx.inc
    );
    will always include the file xxx.inc, even if condition=false.

    Another example:

    if(condition,
    parameter p;
    p = 1;
    );

    is not allowed, as the declaration is compile time.

    Erwin

    ReplyDelete
  3. One more example. Suppose you want to $set another thing inside, then this will not work:

    if(1,
    $set name "hello"
    else
    $set name "world"
    );

    display "%name%";

    This will display world instead of the expected hello.

    ReplyDelete
  4. I see your point.
    I've never used these compile time conditionals but I can imagine situations in which they might be useful. Specially when used with $include

    Thanks.

    ReplyDelete