|
<< Click to Display Table of Contents >> Solve, quick example |
![]() ![]()
|
A lot of the core Gekko functionality regarding model solving resides in Solve.cs, in the Gekko project (i.e., the Gekko folder).
The most complicated methods in Solve.cs deal with the simulation of models, especially the code regarding the Newton algorithm. This algorithm has an entry point in the SolveNewton() method, calling later on the SolveNewtonAlgorithm() method, which among other things uses an ordering algorithm, and linear algebra (Numerics and alglib projects). The Newton algorithm will not be described at this time, instead we will focus on the Gauss-Seidel algorithm, i.e. the SolveGauss() method.
The Gauss solver
This method solves the model using the Gauss-Seidel algorithm. The method solves the model for one period, and the input is an array b[] containing data, a list isDampedPointers containing variables to be damped, and a link to the .dll file containing the compiled equations (modelType).
Output from the method is the array b[], where non-lagged endogenous variables will have changed value. In addition, the array simulateResults contains some meta-information (iteration count, problematic equation if an error occurs etc.). The variable culprit will contain the name of the last variable to converge.
The indices in the b[] array are arranged in order regarding the model .frm file. Variables are considered different if lags are different. If the model file starts with the equation FRML _i Y = 0.1*X + 0.2*X[-1] + Y[-1] + 0.2*X; there will be the following indices:
•0: Y
•1: X
•2: X[-1]
•3: Y[-1]
The counting is regarding the first occurrence of the variable, so the term 0.2*X will not impact any indices. The equation will translate into b[0] = 0.1*b[1] + 0.2*b[2] + b[3] + 0.2*b[1], corresponding quite closely to machine-code. The Gauss algorithm is really quite simple: translate all equations into machine-code of this kind, load the relevant data from the in-memory Work databank into the b array, and run the machine-code successively, until the left-side b's -- the endogenous, for instance b[0] -- do not change anymore. Then put the left-side b's back into the Work databank variables, run the next period(s), and finally return to the user prompt.
Since Gekko automatically orders equations, it identifies prologue, simultaneous, and epilogue blocks. So to save time, the prologue is only run once, the simultaneous equations are run until convergence, and finally the epilogue is run once (together with, among other things, reverted equations regarding add-factors etc.).
Regarding the simultaneous block of equations, the iterative procedure is this (in pseudo-code)
Program listing: Gekko iterative loop (Gauss):
bOld = b; //copy b array into bOld array (element by element)
probe = true; //starts in probe-mode
for iterCounter = 0 to infinity;
run machine-code (b[]-equations);
damp variables to be damped, using b and bOld;
if(iterCounter > itermax) STOP;
if(iterCounter < itermin)
bOld = b;
continue from start of loop;
end;
convergence check on endogenous one by one --> return if all are converged
bOld = b;
end;
So the idea is as follows. Start out by making a copy of the b array (called bOld). First, the b-equations (C#-code) are run, and after that any damped variables get damped (a weighted average of their new and old values).
If the iteration count is > itermax, the loop will abort now, and if the iteration count is < itermin, the program will copy the b-array and jump to the start of the loop and continue iterating (no convergence check is done)
Next is convergence check. Gekko begins to check all variables. This is done one by one, and as soon as a variable is not converged, the rest are not checked.
So the typical picture is that there are first a number of "free" (nonchecked) iterations, defined by the itermin option. Then convergence checks kick in. For the last iterations thousands of variables typically get checked, until all are ok.
The b[]-equations are close to raw machine-code, and run pretty fast. The copying of the b array is done as a block (System.Array.Copy() method), and is very convenient regarding convergence check. On modern cpu's such array copying is very fast, and in general does not slow anything down.
Intercepting the SolveGauss() method
The Gekko representation of the simultaneous equation problem is quite general, since it is represented as a number of equations of this form: b[0] = 0.1*b[1] + 0.2*b[2] + b[3] + 0.2*b[1]. Given the b[] array, the question is how to choose the elements of b[] so that all the equations are ok (within some convergence criteria, that is).
It would be quite simple to link an external solver to Gekko (while still letting Gekko deal with accessing data from its databanks, and parsing the model file). The method would be the following:
Access the .cs code containing the simultaneous b[] equations, transform these equations to some other language, and compile them. This could be done by adding some code in the EmitCsCodeAndCompileModel() method in Parser.cs, which gets called when a MODEL statement is issued. Inside this method, adding the following code would produce the b[] equations in the simultaneous block and put them into a StringBuilder object (sb):
StringBuilder sb = new StringBuilder();
foreach (EquationHelper eh in gaussEquations)
{
sb.Append(eh.csCodeLhsGauss);
sb.Append(" = ");
sb.AppendLine(eh.csCodeRhs);
sb.AppendLine(";");
}
To get the text string of equations, simply use sb.ToString().
Next, intercept Gekko where the SolveGauss() method is called in Solve.cs. Instead of the call to this method, call the external binary .dll solver instead. Preferably, this external .dll should have direct access to the b[] array structure in RAM, otherwise it must be copied somehow. Solve the equations by means of the .dll (and put the results back into the b[] array if the array was copied in the step before).
An alternative to this is to use the a[] array, containing all the data (i.e. observations x variables). See the FromDatabankToA() method.