The Calc Section of a Model

In many instances, your model’s raw input data will need additional massaging to get it into the proper form.  As an example, suppose your raw data consists of daily observations of a number of securities’ closing prices.  Furthermore, let’s suppose that your model ultimately requires the covariance matrix for the securities to be computed from the raw closing price data.  You could certainly compute the covariance matrix as part of the constraint section in your model.  However, entering simple computations as constraints will make your model artificially large.  Another option, although inconvenient, would be to compute the covariance matrix outside of LINGO and pass it to LINGO as external data.  Actually, what you would really like is a section in LINGO to perform data manipulation in such a way that it doesn’t increase the size of the final optimization model passed through to the solver engine.  This is the function of the calc section.

A calc section begins with the keyword CALC: and ends with the keyword ENDCALC. You may input any expression in a calc section that you would in the constraint section of a model.  However, each expression must be in the form of an assignment statement.  In an assignment statement, a single variable appears on the left-hand side of an expression, followed by an equality sign, followed by an arbitrary mathematical expression on the right-hand side.  Furthermore, the right-hand side expression may only contain references to variables that are set as part of the model’s input data (i.e., set in a previous data section or calc expression.)

As an example, here’s a model with a calc section that computes the average of three variables:

MODEL:

 

DATA:

  X, Y, Z = 1, 2, 3;

ENDDATA

 

CALC:

  AVG = ( X + Y + Z) / 3;

ENDCALC

 

END

Example of a valid calc section

Now, suppose we did not know the value of Y beforehand.  The following model with Y dropped from the data section would trigger an error in LINGO.  The error occurs because the value of Y is an unknown, which violates the requirement that all right-hand side variables in a  calc expression must have already had their values established in a previous data or calc section:

MODEL:

 

DATA:

  X, Z = 1, 3;

ENDDATA

 

CALC:

  AVG = ( X + Y + Z) / 3;

ENDCALC

 

END

Example of an invalid calc section

You may perform running calculations in a calc section, which means that you may break complex calc expressions down into a series of smaller expressions.  Here we break the computation from above into two steps:

MODEL:

 

DATA:

  X, Y, Z = 1, 2, 3;

ENDDATA

 

CALC:

  AVG = X + Y + Z;

  AVG = AVG / 3;

ENDCALC

 

END

Example of a running calc expression

There is no limit to the number of times that a variable may appear on the left-hand side of a calc expression.  However, the final calc expression for the variable will determine its value in the final solution report.

Calc expressions are computed sequentially in the order in which they appear in the model.  So, if one calc expression feeds its value into a subsequent expression, then it must appear before its dependent expression.  For example, the following calc section is valid:

CALC:

  X = 1;

  Y = X + 1;

ENDCALC

while this variation is not valid:

CALC:

  Y = X + 1;

  X = 1;

ENDCALC

In the second example, Y depends on X, but X is not defined until after Y.

Of course, Set looping functions may also be used in calc expressions.  For example, consider the following portfolio optimization model.  In this model, we take the annual returns for three stocks and in a calc section compute the following three pieces of information for the stocks: average return, the covariance matrix, and the correlation matrix.  This information is then used in a standard Markowitz model to determine an optimal portfolio that meets a desired level of return while minimizing overall risk.

MODEL:

SETS:

  STOCKS: AVG_RET, WEIGHT;

  DAYS;

  SXD( DAYS, STOCKS): RETURN;

  SXS( STOCKS, STOCKS): COVR, CORR;

ENDSETS

 

DATA:

  DAYS   = 1..12;

  TARGET = .15;

  STOCKS =   ATT     GMC      USX;

  RETURN = 0.300   0.225    0.149

           0.103   0.290    0.260

           0.216   0.216    0.419

          -0.046  -0.272   -0.078

          -0.071   0.144    0.169

           0.056   0.107   -0.035

           0.038   0.321    0.133

           0.089   0.305    0.732

           0.090   0.195    0.021

           0.083   0.390    0.131

           0.035  -0.072    0.006

           0.176   0.715    0.908;

ENDDATA

 

CALC:

  !Average annual return for each stock;

  @FOR( STOCKS( S):

   AVG_RET( S) =

    ( @SUM( SXD( D, S): RETURN( D, S)) /

     @SIZE( DAYS))

  );

 

  !Covariance matrix;

  @FOR( SXS( S1, S2):

   COVR( S1, S2) =

    @SUM( DAYS( D):( RETURN( D, S1) - AVG_RET( S1)) *

     ( RETURN( D, S2) - AVG_RET( S2))) / @SIZE( DAYS)

  );

 

  !Although not required, compute the correlation matrix;

  @FOR( SXS( S1, S2):

   CORR( S1, S2) = COVR( S1, S2) /

   ( COVR( S1, S1) * COVR( S2, S2))^.5;

  );

ENDCALC

 

!Minimize the risk of the portfolio

(i.e., its variance);

[R_OBJ] MIN = @SUM( SXS( S1, S2):

WEIGHT( S1) * WEIGHT( S2) * COVR( S1, S2));

 

!Must be fully invested;

[R_BUDGET] @SUM( STOCKS: WEIGHT) = 1;

 

!Must exceed target return;

[R_TARGET] @SUM( STOCKS: AVG_RET * WEIGHT) >= TARGET;

 

END

Model: MARKOW

We demonstrated how to initialize variable values in the previous topic on init sections.  Variables may also be initialized in a calc section.  The basic idea is to set the variable's value using one or more statements in a calc section. Once the variable's value has been set in a calc section, the variable will be marked as fixed in value, and will not be allowed to be changed the next time you issue a solve command.  Obviously, we don't want an optimizeable to be a fixed variable.  To correct this, we use the @RELEASE function to release the fixed variable so that it is once again optimizable.  The following example illustrates:

MODEL:

 

SETS:

  S1: X;

ENDSETS

 

DATA:

  S1 = 1..5;

ENDDATA

 

CALC:

! Initialize the odd members of X to .5;

  @FOR( S1( I) | @MOD( I, 2) #NE# 0:

     X( I) = .5;

! The previous statement fixed X(I) to .5.

 We must now release it so that it remains

 optimizable;

     @RELEASE( X( I));

  );

ENDCALC

 

END