Colorado GAMS

A Preprocessor for Function Definitions in GAMS

Michael Ferris

Department of Computer Science
University of Wisconsin

Thomas F. Rutherford*

Collin Starkweather**

Department of Economics
University of Colorado

November, 1998

(updated August, 2005)

* This research supported by the GAMS Applied General Equilibrium Research Fund. The software described here is designed for use with GAMS 2.50A, but it can be used with earlier GAMS systems. The authors remain responsible for any bugs which exist in this software. This software is not officially supported by GAMS Corporation.

** Please feel free to contact Collin at collin.starkweather@colorado.edu with any comments/suggestions/bug reports.


A Brief Overview:

GAMS programs in economics typically involve lots of algebra. When you write down a system of nested CES demand functions, for example, it can become quite tedious to keep track of all the parentheses and avoid typographical errors. Most experienced modellers know that the model specification can be simplified at the expense of declaring additional variables and equations. As more variables are introduced to a model, the model equations become easier to read; but this strategy presents certain limitations. For one thing, the model gets larger, both in terms of the number of equation and variable names and in terms of the dimension of the equation system.

The GAMS-F tool is intended to reduce complexity and improve transparency of GAMS programs without additional overhead. This tool allows the programmer to define "functions" in a GAMS program which can subsequently be substituted into parameter or equation definitions. Thus, a function may be defined in a GAMS file as


F(I) == A * K(I)**alpha * L(I)**(1-alpha)

and the function can later be referenced in an equation as:

OUTPUT(I)..	Y(I) =E= F(I);

The preprocessor comments out the function definition and replaces the function reference as:

OUTPUT(I)..	Y(I) =E= A * K(I)**alpha * L(I)**(1-alpha)

This example is particularly simple. The function substitution system has some limited knowledge about GAMS syntax. For example, it knows that dummy sets referenced in a function declaration should be substituted to avoid collisions in the function reference. For example, the following would be valid:

	F(I) == SUM(J, A(I,J) * Y(J));

EQ(J)..		SUM(I, B(I,J) * F(I)) =E= C(J);

In this case the preprocessor makes the replacement:

ALIAS(FCN000,J);

EQ(J)..		SUM(I, B(I,J) * (SUM(FCN000, A(I,FCN000) * Y(FCN000)))) =E= C(J);

In addition to simplifying GAMS algebra, the GAMS function tool also provides some assistance for complementarity programmers.

A function may be used in place of an equation name in a mixed complementarity program model statement. For example, here is how the linear complementarity problem can be specified using the function syntax:


F(i) == q(i) + sum(j, M(i,j) * Z(j));

model lemke / F.Z /;

We hope that the use of functions in MCP model definitions can clear up some of the confusion which has resulted from the initial use of oriented equations.

Invocation:

Command line syntax:


	gams-f <input_file >output_file

This command translates a GAMS input file with function definitions to a standard GAMS program in which function definitions are omitted and function references are replaced. The input file comes from standard input and output is written to standard output. Passing a command-line argument to GAMS-F will result in this message.

Syntax in GAMS:

	$sysinclude gams-f
	[$exit]

These statements invokes the preprocessor on the current file. The file appears as translated in the listing, but the source file itself is unaltered. The $exit statement is required for versions of GAMS prior to release 2.50a.

Function Definitions:

<func id>(args) == expression(args); 
where the argument list may include sets, parameters or variables. Here are a few examples:

(i)     F(i) == SUM(j, alpha(i,j) * 
                        P(j)**(1-sigma))**(1/(1-sigma));

(ii)    G(i,j) == PROD(t(k), a(i,j,k) * X(i,j)**alpha);

(iii)   H(i) == SMIN(j$(alpha(i) gt alpha(j)), a(i,j) * F(j));

The preprocessor reads these function definitions and generates GAMS code which aliases any dummy sets and then makes the appropriate substitutions when the functions are referenced later in the code. Note that function definitions can reference previously defined functions.

NB: The preprocessor does not perform domain checking. Apart from counting dimensions in function references, all syntax checking is performed by the GAMS compiler on the translated code.

NB: GAMS-F uses set names FCN000, FCN001, etc. for sets and names EQN000, EQN001, etc. for MCP equations. Thus, GAMS identifiers of the form FCN??? or EQN??? are not permitted.


Function References:

A function may only be referenced following its definition. Functions may be referenced in parameter assignments, equations or in PUT statements. Functions may not appear in DISPLAY statements.


Debugging:

The preprocessor proceeds through the file in a linear fashion including any $include file which either

  1. Includes function definitions,
  2. Includes references to previously defined functions, or
  3. $includes a file which (may $include a file which . . .) meets criteria (1) or (2)

The output will indicate the line number of any actions taken, appending the file name of any $included file to the output.

Thus, the output for example 4 below would look like

GAMS-F Function Preprocessor Version 11/11/98
---------------------------------------------

Reading input file . . .
Parsing input file . . .

        [21]    Function object F compiled
        [26]    Checking MODEL statement . . .
        [26]    MCP conditions formulated for F . . .

Writing output . . .

GAMS-F terminating normally.
If, for example, line 5 of the above example was
$include incfile
and incfile.gms contained a function definition, this would be reflected in output as
        [5]    Examining file incfile.gms . . .
        [5]    Including file incfile.gms . . .
        [1]    Function object FCN1 compiled (INCFILE.GMS)

If you would like to examine the file produced by GAMS-F prior to GAMS compilation, you may use the syntax

gams-f.pl <input.gms >output.gms
in Unix or
gams-f <input.gms >output.gms
in Windows where input.gms is the name of your GAMS file and output.gms is the name of a file to which the GAMS-F output will be sent.

Restrictions:


Installation:

Download gams-f.unix.pck or gams-f.windows.pck into your GAMS system directory, and run GAMSINST. This will unpack the following files into your GAMS system directory:


	gams-f.gms	GAMS sysinclude program file
	gams-f.pl	Perl source code
	gams-f.exe	Executable program for Windows (95/98/NT) (Windows only)
	perl.dll	Perl dynamic link library called by gams-f (Windows only)
	cw3220mt.dll	Perl dynamic link library called by gams-f (Windows only)

If you are running a version of GAMS earlier than 2.50A, you need to edit the gams-f.gms file. Change the line which reads:

$stop

to:

$hidden $stop

After this change has been made, you need to invoke $sysinclude gams-f followed by an $exit statement.

Unix users should note that the gams-f.pl shebang assumes Perl is in /usr/local/bin.


Examples:

Example 0: The Basics


$sysinclude gams-f
$exit

*	Define the function F(X) 
F(X) == 2 * SIN(X/4);

file kcon /con/;
putclose kcon // "F when X is 3.14159:    ", F(3.14159) //;
putclose kcon // "F when X is 2*3.14159:  ", F(2*3.14159) /;

The output:

--- Starting compilation
--- .GAMS-F.GMS(0) 143 Kb
--- Running the GAMS function preprocessor ...

GAMS-F Function Preprocessor 12/04/98 11:07
by Collin Starkweather and Thomas F. Rutherford
-----------------------------------------------

Reading input file . . .
Parsing input file . . .

        [5]     Function object F compiled
        [8]     Performing substitution of function F
        [9]     Performing substitution of function F

Writing output . . .

GAMS-F terminating normally.

--- ..GAMS-F.SCR(25) 147 Kb
--- .GAMS-F.GMS(19) 147 Kb
--- FUNCTION.GMS(2) 147 Kb
--- Starting execution
--- FUNCTION.GMS(21) 134 Kb

F when X is 3.14159:            1.41

--- FUNCTION.GMS(30) 134 Kb

F when X is 2*3.14159:          2.00


*** Status: Normal completion
--- Erasing scratch files

Example 1: Functions Cannot Appear in Display Statements


$sysinclude gams-f
$exit

set	i	/1*10/;

alias (i,j);

parameter	a(i,j)	random matrix
		x(i)	a vector function;

a(i,j) = uniform(0,1);

f(i) == sum(j, a(i,j));

*	A function cannot be displayed -- you must 
*	assign a parameter to do this:

x(i) = f(i);

display x;

Example 2: Functions in PUT Statements are OK


$sysinclude gams-f
$exit

set	i	/1*10/;

alias (i,j);

parameter	a(i,j)	random matrix;

a(i,j) = uniform(0,1);

f(i) == sum(j, a(i,j));

*	A function can be inserted into a PUT
*	statement:

file kout /example2.out/; put kout;

loop(i, put i.tl, f(i)/; );

Example 3: Set Aliasing and Substitution


$sysinclude gams-f
$exit

set	i	/1*10/;

alias (i,j);

parameter	a(i,j)	random matrix;

a(i,j) = uniform(0,1);

f(i) == sum(j, a(i,j));

*	GAMS-F substitutes dummy sets in the
*	definition so you don't need to worry
*	about which sets are used in the definition:


parameter	b(i,j)	A second matrix;

b(i,j) = a(i,j) + f(i);

display b;

Example 4: Using GAMS-F with MCP Models


$sysinclude gams-f
$exit

set	i /1*3/;
alias (i,j);

parameter	m(i,j)	LCP data matrix
		q(i)	LCP data vector;

*	Some random data (will this be a
*	P-matrix?)

m(i,j) = uniform(0,1);
q(i) = uniform(0,1);

*	Define the LCP function -- notice that we
*	can refer to z here even though it has not
*	yet been declared.  Z must, however, be 
*	declared prior to referencing f:

f(i) == q(i) + sum(j, m(i,j) * z(j));

positive
variable	z(i)	LCP unknonw;

model lcp /f.z/;

solve lcp using mcp;

Example 5: A Perverse Example


$sysinclude gams-f
$exit

set i /1*10/;

set j(i) /1*5/;

parameter a(i,j);

a(i,j) = 1;

parameter b(i);

*	Notice that the j appear as a qualifier on
*	the sum over i is uncontrolled:

x(j) == sum(i$j(i), a(i,j));

b(j) = x(j);

display b;

Example 6: An Example with Parameter Arguments


$sysinclude gams-f
$exit


set i goods /1*100/;

alias (i,j);

parameter	pi	Market prices
		t	Tax rates
		tunif	Uniform tax
		p0	Benchmark user price
		theta	Benchmark value share;

*	Dummy data:

pi(i) = uniform(0,2);
t(i) = uniform(0,0.2);
p0(i) = pi(i) * (1 + t(i));
theta(i) = p0(i) / sum(j, p0(j));

scalar	sigma	Elasticity of substitution /3/;

*	Define an expenditure function -- note that p is used as a 
*	dummy argument here, even though it is in the GAMS symbol
*	table:

e(pi,t) == sum(i, theta(i) * (pi(i)*(1+t(i))/p0(i))**(1-sigma))**(1/(1-sigma));

parameter	expend functions at difference prices;


expend("p0") = e(pi,t);

tunif(i) = sum(j, t(j)) / card(j);

expend("uniform") = e(pi,tunif);

t(i) = 0;

expend("notax") = e(pi,t);

display expend;

Example 7: A Larger Example


$title Simple 2 x 2 x 2 General Equilibrium Model (TWO3MCP,SEQ=131)

*	Invoke the function preprocessor.  The $exit following this 
*	call is required for versions of GAMS earlier than 2.50A.

$sysinclude gams-f
$exit

*  Reference:  Shoven and Whalley: "Applied G.E. Models"
*              Journal of Economic Literature, XXII (1984)

sets
        f  factors    /labor, capital/
        s  sectors    /mfrs,  nonmfrs/
        h  households /rich,  poor/;

alias (h,k), (s,ss), (f,ff);
*
*       demand function parameters.
*
parameter sigmac(h)
       / rich    1.5 ,  poor    0.75/;

table alpha(s,h)
                rich    poor
        mfrs    0.5     0.3
        nonmfrs 0.5     0.7;

table e(f,h)
                rich    poor
        labor             60
        capital   25
*
*       production function parameters.
*
parameter phi(s)
        / mfrs 1.5,  nonmfrs 2.0 /;

table delta(f,s)
                        mfrs    nonmfrs
        labor           0.6     0.7
        capital         0.4     0.3;

parameter sigma(s)
      /  mfrs 2.0,   nonmfrs 0.5/;

parameter       tshr(h) share of tax revenue /rich 0.4, poor 0.6/,
                t(f,s)  ad-valorem tax rates;

t(f,s) = 0;

positive
variables
        w(f)            factor price,
        p(s)            commodity price,
        y(s)            production level;

pf(f,s) == w(f)*(1+t(f,s));

cost(s) == sum(f, delta(f,s)**sigma(s) * pf(f,s)**(1 - sigma(s)) )**(1/(1-sigma(s))) / phi(s);

fd(f,s) == (delta(f,s) * cost(s) / pf(f,s))**sigma(s);

fmkt(f) == sum(h, e(f,h)) - sum(s, fd(f,s) * y(s));

tax(s) == sum(f, t(f,s) * w(f) * y(s) * fd(f,s)); 

income(h) == sum(f, e(f,h) * w(f)) + tshr(h) * sum(s, tax(s));

d(s,h) == alpha(s,h) * income(h) * sum(ss, alpha(ss,h) * p(ss)**(sigmac(h)-1)) * p(s)**(-sigmac(h));

cmkt(s) == y(s) - sum(h, d(s,h));
                
profit(s) == cost(s) - p(s);

model jel / fmkt.w, cmkt.p, profit.y/;

*       compute solution for this dimension problem:

w.lo(f) = 0.0001;
p.lo(s) = 0.0001;

w.l(f) = 1;
p.l(s) = 1;
y.l(s) = 1;

*	Use labor as numeraire:

w.fx("labor") = 1;

*       solve the reference case:

solve jel using mcp;

*       apply tax in test problem:

t("capital","mfrs") = 0.5;
solve jel using mcp;