The $if statement has been enhanced to allow constant expression evaluation. To match the flavor of the basic $if statement, we have introduced two forms:
$ife expr1 == expr2 true if (expr1-expr2)/(1+abs(expr2)) < 1e-12 $ife expr true if expr1 <> 0
The expressions follow the standard GAMS syntax and are explained in more detail below. The .==. form is convenient when expecting rounding errors. For example, we can write
$ife log2(16)^2 == 16 (true statement)
which is more convenient than having to write
$ife NOT round(log2(16)^2-16,10) (true statement) $ife round(log2(16)^2,10)=16 (true statement)
A new variant on the $if statement has been introduced. It follows the usual structures and allows appropriate nesting. The syntax for the condition are the same as for the $if statement. The $ifthen and $elseif have the same modifiers as the $if statement, namely I for case insensitive compare and E for constant expression evaluation. In the example below we will execute all blocks of such a statement.
$maxgoto 10 $set x a $label two $ifthen %x% == a $set x 'c' $log $ifthen with x=%x% $elseif %x% == b $set x 'k' $log $elseif 1 with x=%x% $elseif %x% == c $set x 'b' $log $elseif 2 with x=%x% $else $set x 'e' $log $else with x=%x% $endif $if NOT %x% == e $goto two $eval x 1 $label three display 'x=%x%'; $ifthen %x% == 1 $eval x %x%+1 $elseif %x% == 2 $eval x %x%+1 $elseif %x% == 3 $eval x %x%+1 $elseif %x% == 4 $eval x %x%+1 $else $set x done $endif $if NOT %x% == done $goto three
This is a bit contrived but illustrates some of more subtle features. Anytime we use a looping construct via a $goto statement we have to protect ourselves against the potential of an infinite loop. The number of times we jump back to a label is counted and when a limit is reached, GAMS will issue an error. It is important to note that the %string% references are substituted only once.
Lengthy and nested ithen/else structures can become difficult to debug. Tagging of the begin, the $ifthen and the end, the $endif can be helpful. For example, the next line will fail because the tags do not match:
$ifthen.one x == x $endif.one
As with the $if statement, the statement on the line with the $ifthen style statements is optional. The following two statements give the same results:
$iftheni %type% == low $include abc $elseifi %type% == med $include efg $else $include xyz $endif $iftheni %type% == low $include abc $elseifi %type% == med $include efg $else $include xyz $endif
The statements following directly a $ifthen, $elseif, or $else on the same line can be a sequence of other dollar control statements or contain proper GAMS syntax. The statements following directly a $endif can only contain another dollar control statements.
$ifthen.two c==c display 'true for tag two'; $ifthen.three a==a $log true for tag three display ' then clause for tag three'; $ifthen.four x==x display 'true for tag four'; $log true for tag four $else display ' else clause for tag four'; $endif.four $log endif four $endif.three $log endif three $endif.two $log endif two
This will produce a GAMS program like
1 display 'true for tag two'; 3 display ' then clause for tag three'; 4 display 'true for tag four';
with the following log output
--- Starting compilation true for tag three true for tag four endif four endif three endif two
Those three new dollar control options are similar to $set, $setlocal and $setglobal. Those statements are used to assign values to ‘environment variables’ which are named string to be substituted by the %var% reference. The $eval options interpret the argument as a constant expression (see for more details below) and encode the result as a string. For example:
$if NOT set d $eval d 5 $eval h '24*%d%' $eval he '0' $eval dd '0' Sets d days / day1*day%d%/ h hours / hour1*hour%h% / dh(d,h) / $label more $eval dd '%dd%+1' $eval hs '%he%+1' $eval he %he%+24 day%dd%.hour%hs%*hour%he% $ife %dd%<%d% $goto more /;
will produce the expanded input source code below:
3 Sets d days / day1*day5/ 4 h hours / hour1*hour120 / 5 dh(d,h) / 7 day1.hour1*hour24 10 day2.hour25*hour48 13 day3.hour49*hour72 16 day4.hour73*hour96 19 day5.hour97*hour120 21 /;
The syntax of constant expression use in data statements and $conditions follows the GAMS syntax, but restricted to scalar values and a subset of functions, as summarized below:
OR XOR EQV IMP AND NOT < <= = <> >= > LE LE EQ NE GE GT + - binary and unary * / ^ **
abs ceil cos exp floor frac IfThen log log2 log10 max min mod PI power round sign sin sleep sqr sqrt tan trunk
When used in data statement, the constant expressions have to be enclosed in a pair of Square brackets [ ] or curly brackets or braces { }. Spaces can be used freely inside those brackets. When used in dollar control options, like $eval or $if statements brackets are not required, however, we may have to enclose the expression within quotes (single or double) if we want to embed spaces or continue with additional $options on the same line. For example, when using $eval followed by another $statement:
$eval x 3 / 5 * pi $eval y "3/5*pi" $eval z pi / 2 $eval a ' exp(1) / pi ' $set b anything goes here $show . Level SetVal Type Text -------------------------------------------- 0 x SCOPED 1.88495559215388 0 y SCOPED 1.88495559215388 0 z SCOPED 1.5707963267949 0 a SCOPED 0.865255979432265 0 b SCOPED anything goes here
As with other dollar control statements, without the quotes, the entire remaining line will be interpreted as one argument.
The $ife and $ifthene/$elseife statements have a related problem which has been inherited by mimicking the Windows bat and cmd scripting conventions. When a constant expression contains spaces it has to be enclosed in quotes, as shown below.
$ife (2+3)>=(2+2) display 'OK, no spaces'; $ife ' (2 + 3) >= (2 + 2) ' display 'now we need quotes';
Finally, here are some data examples:
Scalars x PI half / {pi/2} /, e famous number / [ exp( 1 ) ] /; Parameter y demo / USA.(high,low) [1/3], USA.medium {1/4} /;
Information about the licensing process is now available at compile and execution time.
Two new System environment variables LicenseStatus and LicenseStatusText complement the other license related variables. In addition, two functions have been added to retrieve the licensing level and status. The use of those variables is demonstrated in the updated library model .licememo.. Here is an extract:
$set filename %gams.license% $if '%filename' == '' $set filename %gams.sysdir%gamslice.txt if(%system.licensestatus%, put '**** Error Message: %system.licensestatustext%' / '**** License file : %filename%' / '**** System downgraded to demo mode'// );
If called with an incorrect license, the report may contain:
**** Error Message: could not open specified license file **** License file : garbage **** System downgraded to demo mode
The variable system.licensestaus returns zero if no error has been encountered by the licensing process. The variable system.licensestatustext contains the respective explanation about a licensing failure. The above example uses compile time string substitutions and are not updated when execution a pre compiled work file.
Two new functions, LicenseLevel and LicenseStatus, provide this information at runtime:
Some special features for option files or other information files require writing complete expanded references of indexed identifiers like variables or equations. For example, the options for CPLEX indicator variables can now be written more compactly. For example, we no can write:
loop(lt(j,jj), put / 'indic ' seq.tn(j,jj) '$' y.tn(j,jj) yes / 'indic ' seq.tn(jj,j) '$' y.tn(j,jj) NO );
This will produce
indic seq('A','B')$y('A','B') YES indic seq('B','A')$y('A','B') NO indic seq('A','C')$y('A','C') YES indic seq('C','A')$y('A','C') NO indic seq('B','C')$y('B','C') YES indic seq('C','B')$y('B','C') NO
Besides the more compact GAMS code, it provides complete syntax checking at compile time.
New syntax has been added to extract more information about a controlling set. It is similar to the ord(i) operator but uses the dot notation. The new functions are:
The following example illustrates some of those new features:
set i / '-inf',1,12,24,'13.14',inf /; parameter report; report(i,'value') = i.val; report(i,'length') = i.len; report(i,'offset') = i.off; report(i,'position') = i.pos; display report;
The display shows
---- 6 PARAMETER report value length offset position -inf -INF 4.000 1.000 1 1.000 1.000 1.000 2.000 12 12.000 2.000 2.000 3.000 24 24.000 2.000 3.000 4.000 13.14 13.140 5.000 4.000 5.000 inf +INF 3.000 5.000 6.000