DRY stands for “Don’t repeat yourself” and is one of the main principles of efficient programming. In Gams, I use some checks over and over again. Instead of rewriting the code or searching for a file with the existing code and copying it, I use macros in Gams. Macros aren’t difficult to write. You can find more on them here in the documentation.
Here is a simple example from the Gams documentation defining and using a macro that calculates the reciprocal of a number:
1 2 3 |
$macro reciprocal(y) 1/y scalar z, x1 /2/, x2 /3/; z = reciprocal(x1) + reciprocal(x2); |
More complex macros can go over several lines as long as every line, with the exception of the last line, ends with a backslash.
Here is one of my favorites: Whenever I want to check if allrow sums are equal to the the corresponding column sums (a typical check if you work with Input-Output tables), I use the following macro:
1 2 3 4 5 6 7 8 9 10 |
$macro checkmatrix(qset, aqset, matrix) \ * Define an alias for the set parameter check_cons, check_count; \ * Calculate the difference between column and row totals check_cons(qset) = sum(aqset, matrix(qset,aqset)) - sum(aqset, matrix(aqset,qset)); \ * Count the number of inconsistencies check_count = sum(qset, 1$(abs(check_cons(qset)) gt 1e-5)); \ check_cons(qset)$(abs(check_cons(qset)) < 1e-5) = 0; \ * Abort if there are inconsistencies abort$check_count "consistency error", check_cons, check_count; \ |
As you can see, every code line ends with a backslash. You can add comments, but be sure not to use empty lines. In my code, I can now use this matrix as follows:
1 |
checkmatrix(iot, s, as) |
where s is the row index and is aliased with as. If there is any inconsistency between the sums, the code aborts and Gams reports the number of inconsistencies and the differences themselves.
Another macro I use often is the checkmapping macro. I described how this macro functions in a previous post. It checks if I did not make a mistake in my mapping from one set to another. The macro looks like this:
1 2 3 4 5 6 7 |
$macro checkmapping(mapping, fset, aset) \ set error1, error2; \ * Check first index error1(fset) = YES$(sum(aset, 1$mapping(fset,aset)) ne 1); \ abort$CARD(error1) "Inconsistent first index for mapping! Missing or multiple elements: ", error1; \ error2(aset) = YES$(sum(fset, 1$mapping(fset,aset)) = 0); \ abort$CARD(error2) "Inconsistent second index for mapping! Missing or multiple elements: ", error2; \ |
The macro aborts the code if I made a mistake and shows the set elements that cause problems.
Another example is checking if the absolute value of some parameter is zero (for example, if I am checking if there are differences in a zero-profit condition) and if not aborts the code:
Another example is checking if the absolute value of some parameter is zero (for example, if I am checking if there are differences in a zero-profit condition) and if not aborts the code:
1 |
$macro checkdiff(check) abort$(abs(check) gt 1E-7) "Difference greater than 1E-7", check |
In Gams this could be used like this:
1 2 3 4 |
lhs = a + b*Y; rhs = c - d*Y; diff = lhs - rhs; checkdiff(diff) |
which would produce an abort if there would be a difference between lhs and rhs:
—- 481 Difference greater than 1E-7
PARAMETER diff = 0.010 Difference
**** Exec Error at line 481: Execution halted: abort$1 ‘Difference greater than 1E-7’
The macros are stored in a file “macros.gms” that I include in my code like this
1 |
$include path_to_macros\macros.gms |