$title  A Successive Recalibration Algorithm for GE Models with Many Households

$ontext

This program constitutes explicit documentation of the solution
algorithm used to solve a general equilibrium model of the economic
effects of Russia's accession to the WTO based on Goskomstat's
consumer expenditure survey.  That model has 55094 households.  For
further details, see the working paper:

Poverty Effects of Russia’s WTO Accession: modeling “real” households
and endogenous productivity effects
 
Thomas Rutherford, University of Colorado
David Tarr, The World Bank
Oleksandr Shepotylo, University of Maryland

September, 2004

Contact: dtarr@worldbank.org

This is a small GAMS program which formulates a simple exchange model
with multiple households and shows how the model can be solved either
in "bottom-up" mode (with an explicit representation of the consumer
demands in the model) or through successive computation of a
"top-down" model.

The default configuration of this model has 1000 households.  The
program output is the reporting parameter itrlog which should be
displayed as follows:

----    459 PARAMETER itrlog  Equilibrium price levels

         bottomup       iter0       iter1       iter2       iter3       iter4       iter5
i1        0.96239     0.95735     0.96309     0.96230     0.96241     0.96239     0.96239
i2        0.99602     0.99546     0.99607     0.99601     0.99602     0.99602     0.99602
i3        1.00449     1.00511     1.00439     1.00451     1.00449     1.00450     1.00449
i4        1.05347     1.05983     1.05275     1.05355     1.05346     1.05347     1.05347
i5        0.98992     0.98869     0.99005     0.98990     0.98992     0.98992     0.98992
i6        1.01483     1.01677     1.01456     1.01487     1.01483     1.01483     1.01483
i7        1.00364     1.00420     1.00353     1.00365     1.00363     1.00364     1.00364
i8        0.93811     0.93088     0.93903     0.93800     0.93813     0.93811     0.93812
i9        1.02511     1.02800     1.02476     1.02515     1.02510     1.02511     1.02511
i10       1.01202     1.01371     1.01176     1.01206     1.01201     1.01202     1.01202

CPU       0.36100     0.03000     0.04000     0.03000     0.03000     0.03000     0.02000
delta                 0.25639     0.03186     0.00407     0.00052     0.00007 8.740106E-6


Rows labelled i1 to i10 report equilibrium prices from various models.
"bottomup" presents equilibrium prices for the integrated model in
which each of the households is explicitly modelled.  The columns
labelled "iter0", "iter1", etc. report equilibrium prices returned for
successive steps in the approximation procedure.  Notice that the
bottom up model agrees to five decimals the last three iterations of
the decomposition procedure.

The row labelled "CPU" reports elapsed time (calculated using the GAMS
internal function "system.timeexec") required to process each of the
models.  (Notice that even with as few as 1000 households, the
decomposition procedure is much faster than the integrated bottom-up
model.)

The row labelled "delta" reports the computed deviation at each step
in the decomposition procedure.  This deviation is the 1-norm of
changes in computed equilibrium prices from one iteration to the next.
The decomposition algorithm is terminated when delta falls below 1e-5 

$offtext

*       Define the dimensions of the model here:

$if not set nhh $set nhh 1000

set     h       Households /h1*h%nhh%/,
        i       Commodities /i1*i10/;

alias (i,j);

*       Use randomly generated input data:

parameter       c0(i,h)         Reference consumption levels,
                e0(i,h)         Commodity endowments,
                timestart       Run time
                sigma(h)        Elasticities of substitution in demand;

c0(i,h) = uniform(0,1);
e0(i,h) = uniform(0,1);
sigma(h) = uniform(0.25, 2);

*       Avoid the tedium of coding both Cobb-Douglas and CES
*       demand functions:

sigma(h)$(abs(sigma(h)-1) < 0.01) = 0.99;

display c0, e0, sigma;

*       Declare and solve a model with a fully disaggregate
*       set of households:

$ontext
$model:bottomup

$commodities:
        P(i)

$consumers:
        HH(h)

$demand:HH(h)  s:sigma(h)
        e:P(i)  q:e0(i,h)
        d:P(i)  q:c0(i,h)

$offtext
$sysinclude mpsgeset bottomup

equations       mkt(i)  Market clearance equation for p(i)
                inc(h)  Income balance equation;

parameter       theta(i,h)      Budget share,
                hh0(h)          Benchmark income level;

*       Compute the benchmark budget shares:

theta(i,h) = c0(i,h)/sum(j,c0(j,h));
hh0(h) = sum(i, c0(i,h));

alias (i,i_);
$macro P_C(h)   (sum(i_, theta(i_,h)*P(i_)**(1-sigma(h)))**(1/(1-sigma(h))))

mkt(i)..        sum(h,e0(i,h)) =e= sum(h, c0(i,h)*(P_C(h)/P(i))**sigma(h) * HH(h)/(P_C(h)*hh0(h)));

inc(h)..        HH(h)  =e= sum(i, P(i)*e0(i,h));

model bottomup_mcp /mkt.P, inc.HH/;

*       When an MPSGE model is formulated with high dimensionality, it is 
*       often necessary to manually increase the allocated workspace.  Here 
*       I am allocating 50 megabytes to the workspace array, a value which
*       is adequate for more than 10,000 households:

bottomup.workspace = 50;

*       Solve the bottom-up model in a single shot:

*       Normalize the price system by fixing one income level:

HH.FX("h1") = sum(i,e0(i,"h1"));

*       Install a lower bound to avoid bad function calls:

P.LO(i) = 1e-5;

timestart = system.timeexec;

$if not set mpsge $set mpsge no
$ifthen.bottomup "%mpsge%"=="yes"

$include bottomup.gen
solve bottomup using mcp;

$else.bottomup

solve bottomup_mcp using mcp;

$endif.bottomup

parameter       itrlog(*,*)     Equilibrium price levels;

itrlog("CPU","bottomup") = system.timeexec - timestart;
itrlog(i,"bottomup") = P.L(i) * card(i) / sum(j, P.L(j));


*       Next, solve the same model recursively using the 
*       successive recalibration algorithm:

*       The top-down model is based on a single representative
*       agent whose preferences are successively adjusted to 
*       locally portray the "community indifference curve" which
*       describes the underlying household endowments and preferences:

parameter
                pc(h)           Household consumption price index,
                u(h)            Household utility index (relative to c0),
                pref(i)         Reference price
                cref(i)         Reference demand quantity;

*       Initially calibrate the community indifference curve
*       based on a price point at the center of the simplex:

u(h)    = sum(i, e0(i,h))/sum(i,c0(i,h));
cref(i) = sum(h, c0(i,h) * u(h));
pref(i) = 1;

*       Here is the top-down model.  Note that the h set does 
*       not appear in this model:

$ontext
$model:topdown

$commodities:
        P(i)    ! Commodity prices

$consumers:
        RA      ! Reprsentative agent:

*       Preferences are Cobb-Douglas:

$demand:RA  s:1
        e:P(i)  q:(sum(h,e0(i,h)))
        d:P(i)  q:cref(i)  p:pref(i)

$offtext
$sysinclude mpsgeset topdown

equations       inc_topdown, mkt_topdown;

inc_topdown..           RA =e= sum((i,h), P(i)*e0(i,h));

$macro alpha(i) (cref(i)*pref(i)/sum(i_,cref(i_)*pref(i_)))

mkt_topdown(i)..        sum(h, e0(i,h)) =e=  alpha(i) * RA/P(i);

model topdown_mcp /inc_topdown.RA, mkt_topdown.P/;

*       Fix aggregate income to normalize the price system:

RA.FX = sum(h, HH.L(h));

set     iter    Iterations in the projection algorithm /iter0*iter10/;

parameter       delta           Convergence metric /1/;

*       Loop until we have drive the sum of absolute price changes
*       to a small level:

loop(iter$(delta > 1e-5),

*       Within each iteration we solve the top-down model.  Note that
*       each solution is very cheap because the model is small and the
*       previous iteration's solution is already in place:

        timestart = system.timeexec;
$ifthen.iter "%mpsge%"=="yes"
$include topdown.gen
        solve topdown using mcp;
$else.iter
        solve topdown_mcp using mcp;
$endif.iter

        itrlog("CPU",iter) = system.timeexec - timestart;

*       Record the current deviation:

        delta = sum(i, abs(P.L(i)-pref(i)));

        itrlog("delta",iter) = delta;

*       Update the iteration log:

        itrlog(i,iter) = P.L(i) * card(i) / sum(j, P.L(j));

*       Recalibrate preferences of the representative agent based on
*       demands of the individual households:

        pc(h) = sum(i, theta(i,h) * P.L(i)**(1-sigma(h)))**(1/(1-sigma(h)));

*       Utility index for household h (relative to c0):

        u(h) = sum(i, e0(i,h)*P.L(i))/(pc(h)*sum(i,c0(i,h)));

*       Reference consumption level for the representative agent:

        cref(i) = sum(h, c0(i,h) * u(h) * (pc(h)/P.L(i))**sigma(h));
        pref(i) = P.L(i);
);

option itrlog:5;
display itrlog;