Differences between revisions 13 and 17 (spanning 4 versions)
Revision 13 as of 2010-02-23 17:25:30
Size: 8211
Comment:
Revision 17 as of 2010-03-01 14:31:31
Size: 4660
Comment:
Deletions are marked like this. Additions are marked like this.
Line 1: Line 1:
This page documents the implementation of external functions in Dynare 4. We call ''external functions'' those functions that are not part of the small set of functions natively supported by Dynare (e.g., log, exp, etc). This page documents the implementation of external functions in Dynare 4. We call ''external functions'' those functions that are not part of the small set of functions natively supported by Dynare (e.g., log, exp, etc). Essentially, this feature allows Dynare 4 to operate on any user-defined (or built-in Matlab) function. For the remainder of this wiki, we assume that the user wants to use a function called {{{funcname}}} in her model.
Line 3: Line 3:
Essentially, this feature allows Dynare 4 to operate on any user-defined (or built-in Matlab) function.

= User Syntax =

We assume that t
he user wants to use a function called {{{funcname}}} in his model. The function is supposed to be implemented through a M-file or a MEX file, located in MATLAB path (this definition includes built-in MATLAB functions).
= The External Function =
The function can be implemented through a M-file or a MEX file (hereafter, M-/MEX file) located on the MATLAB path. '''NB:''' This includes built-in MATLAB functions.
Line 11: Line 8:
For giving the derivatives of this function to Dynare, there are three possibilities for the user:
 * the user does not provide any derivative: then it is up to Dynare to call a numerical derivator
 * the user provides the first (and possibly second) derivatives in the same M-file than the function itself: the first derivatives will be the second return argument (the jacobian, in a vector), the second derivatives will be the third return argument (the hessian, in a matrix)
 * the user provides the first (and possibly second) derivatives in separate M-files
There are two ways in which the user can provide the derivative of {{{funcname}}} to Dynare:
Line 16: Line 10:
The keyword {{{external_function}}} will be used for the declaration of external functions. It accepts the following options:
 * {{{name = STRING}}}: the name of the function, which must also be the name of the M-file (or MEX file) implementing it
 * {{{nargs = INTEGER}}}: the number of arguments of the function. Defaults to 1
 * {{{first_deriv_provided}}}: tells Dynare that the M-file also returns the first derivatives, as the second output
 * {{{first_deriv_provided = STRING}}}: tells Dynare that the first derivatives of the function are provided by the M-file given as option argument
 * {{{second_deriv_provided}}}: tells Dynare that the M-file also returns the second derivatives, as the second output
 * {{{second_deriv_provided = STRING}}}: tells Dynare that the second derivatives of the function are provided by the M-file given as option argument
 1. '''Provide the first (and possibly second) derivatives in the same M-/MEX file as the function itself:''' The first derivative will be the second return argument (the Jacobian, in a vector) and the second derivative will be the third return argument (the Hessian, in a matrix).
 1. '''Provide the first (and possibly second) derivatives in separate M-/MEX files:''' In this case, the only return argument to these functions will be a vector in the case of the Jacobian and a matrix in the case of the Hessian.
Line 24: Line 13:
This keyword would be used in the first part of the MOD file (where variable declarations are), possibly several times if several external functions are used. '''Further, the user has the option to provide neither the first nor the second derivative of {{{funcname}}}.''' In this case (and in the case when the first derivative is provided but the second is not), Dynare will calculate the missing derivatives numerically using finite differences methods (central differences for the Jacobian and eqs. 25.3.24 and 25.3.27 from [[http://mintaka.sdsu.edu/faculty/wfw/ABRAMOWITZ-STEGUN/page_884.htm|Abramowitz and Stegun (1965), pg 884]] for the Hessian);
Line 26: Line 15:
Since external functions are already accepted outside the MOD file without any specific declaration, we should keep the following convention: if during the parsing (inside or outside the model block), the parser encounters an unknown function name, then it should behave as if a external function declaration had been made for that function, with the number of arguments used in the construct, and without any provided derivative. Put otherwise, if the parser encounters something like {{{funcname(x, y, z)}}} and there is no explicit external function declaration, then it should generate an implicit external function declaration like the following: === Example ===
1. The case when one M-/MEX file contains the function output, its Jacobian and Hessian:
Line 28: Line 18:
external_function(name = funcname, nargs = 3); function [y df d2f]=funcname(a,b)
y=a*(b^2);

da=b^2;
db=2*a*b;
df=[da db];

d2f=[0 2*b; 2*b 2*a];
end
Line 31: Line 29:
== Syntax examples == 2. The case when there is a separate M-/MEX file for {{{funcname}}} and its derivative, {{{funcname_deriv}}}:
{{{
function y=funcname(a,b)
y=a*(b^2);
end
Line 33: Line 35:
 * Declare an external function with name {{{funcname}}}, accepting only one argument, and whose derivatives must be computed numerically by Dynare: function df=funcname_deriv(a,b)
da=b^2;
db=2*a*b;
df=[da db];
end
}}}

= User Syntax in the .mod file =

The keyword {{{external_function}}} is reserved for the declaration of external functions. It accepts the following options:
 * {{{name = STRING}}}: the name of the function, which must also be the name of the M-/MEX file implementing it.
 * {{{nargs = INTEGER}}}: the number of arguments of the function. If this option is not provided, Dynare assumes {{{nargs = 1}}}.
 * {{{first_deriv_provided}}}: tells Dynare that the M-/MEX file specified by the argument passed to {{{name}}} returns the Jacobian as its second output argument.
 * {{{first_deriv_provided = STRING}}}: tells Dynare that the Jacobian is provided as the only output of the M-/MEX file given as the option argument.
 * {{{second_deriv_provided}}}: tells Dynare that the M-/MEX file specified by the argument passed to {{{name}}} returns the Hessian as its third output argument.
 * {{{second_deriv_provided = STRING}}}: tells Dynare that the Hessian is provided as the only output of the M-/MEX file given as the option argument.

An {{{external_function}}} statement must be used once for every external function referenced in the {{{model_block}}}. Further, the statement must be issued somewhere before the {{{model_block}}}. The user does not need to include an {{{external_function}}} statement for external functions referenced outside of the {{{model_block}}}.

=== Examples ===

1. Declare an external function with name {{{funcname}}}, accepting only one argument, whose derivatives must be computed numerically by Dynare:
Line 37: Line 60:
 * Declare an external function with two arguments, whose derivatives are returned as second and third output argument of the implementation: 2. Declare an external function with name {{{funcname}}}, accepting two arguments, whose derivatives are returned as the second and third output arguments of the M-/MEX file {{{funcname}}}:
Line 41: Line 64:
 * Declare an external function with three arguments, whose first derivative is provided by M-file funcname_deriv, and whose second derivative must be computed numerically by Dynare: 3. Declare an external function with three arguments, whose first derivative is provided by the M-/MEX file {{{funcname_deriv}}}, and whose second derivative must be computed numerically by Dynare:
Line 45: Line 68:

= Sketch of the implementation in the preprocessor =

 * Currently, the preprocessor already contains implementation of external functions outside the model block, but these functions are currently called "unknown functions", which is not a very good name. Therefore, the following elements should be renamed:
   * in Code``Interpreter.hh, in the enum Symbol``Type, eUnknown``Function should be renamed to eExternal``Function
   * in Expr``Node.{cc,hh}, the class Unknown``Function``Node should be renamed External``Function``Node
   * some methods of class Parsing``Driver and Data``Tree should also be renamed
 * A new class External``Functions``Table should be created for storing the information about external functions. This class would store the list of external functions (explictly or implictly declared), with all the informations conveyed through the {{{external_function}}} keyword. An instance of this class would be stored inside the Mod``File class. Since each external function is also given a symbol ID (in the Symbol``Table) of type eExternal``Function, this class should provide methods to access information through that symbol ID
 * The class External``Function``Node (formerly Unknown``Function``Node) is used to store a call to an external function. Currently many methods of this class are not implemented. At least, the methods for derivation and substitutions should be implemented. The eval() method will never be implemented, for obvious reasons. The methods for computing temporary terms, the compile() method, and the normalizeEquation() method will be left aside for the moment
 * We also need to create node classes for storing derivatives of external functions:
   * A class First``Deriv``External``Function``Node, which would be a subclass of External``Function``Node. The only extra information needed is the integer index (between 1 and nargs) of the argument with respect to which the derivative is computed
   * Similarly, a class Second``Deriv``External``Function``Node, which would be also be a subclass of External``Function``Node. This time two integer indices are needed.
   * For these two classes, the derivation method needs to be overloaded (it produces a 2nd deriv node in case of a 1st deriv node, and it fails for a 2nd deriv node)
   * The writeOutput() method also needs to be overloaded. For the moment, we will adopt a very simple way which is clearly not efficient since it does not take into account expression sharing. Suppose we have the first derivative of {{{funcname}}} (function of 3 args) with respect to its 2nd arg, taken at the point (expr1, expr2, expr3), and that the user has provided the derivative in a separate M-file called {{{funcname_deriv}}}, then the M-file should contain: {{{
subscript_get(funcname_deriv(expr1, expr2, expr3), 2)
   }}} where the {{{subscript_get}}} function is only a helper to select the ''n''-th element of a vector (i.e. {{{subscript_get = @(x,i) x(i)}}}) (Note: is there a simpler way to do that??)
   * For 2nd derivatives, we need a similar construct with a {{{subscript_get2 = @(x,i,j) x(i,j)}}} function
   * The case where a single M-file gives both the value of the function and its derivative is dealt with in a similar way, using one more call to {{{subscript_get}}}
   * For numerical derivation, we use a similar construct, using a numerical derivator. A candidate is {{{jacob.m}}} located in the distribution of Dynare 3.
 * A check should be added in ModFile::checkPass(): the preprocessor should exit with an error if the model block contains an external function and if either use_dll or bytecode option is used
 * When a first prototype is working as described above, we will add the following optimizations:
   * in Data``Tree class, add "maps" for external function nodes (plain ones and 1st and 2nd derivatives), in order to implement maximum expression sharing like we do for unary ops and binary ops
   * use of temporary terms for avoiding recomputing this many times. For example, if we use the derivatives of a given function at a given point with respect to several variables, then only one call to the derivative M-file is necessary, instead of many, and its result should be stored in a temporary (vector) variable, which will be used at different places

This page documents the implementation of external functions in Dynare 4. We call external functions those functions that are not part of the small set of functions natively supported by Dynare (e.g., log, exp, etc). Essentially, this feature allows Dynare 4 to operate on any user-defined (or built-in Matlab) function. For the remainder of this wiki, we assume that the user wants to use a function called funcname in her model.

The External Function

The function can be implemented through a M-file or a MEX file (hereafter, M-/MEX file) located on the MATLAB path. NB: This includes built-in MATLAB functions.

From the mathematical point of view, this function is supposed to be of type  $\mathbb{R}^n \rightarrow \mathbb{R}$. In other words, it can accept any number of real arguments, but returns only one real argument.

There are two ways in which the user can provide the derivative of funcname to Dynare:

  1. Provide the first (and possibly second) derivatives in the same M-/MEX file as the function itself: The first derivative will be the second return argument (the Jacobian, in a vector) and the second derivative will be the third return argument (the Hessian, in a matrix).

  2. Provide the first (and possibly second) derivatives in separate M-/MEX files: In this case, the only return argument to these functions will be a vector in the case of the Jacobian and a matrix in the case of the Hessian.

Further, the user has the option to provide neither the first nor the second derivative of funcname. In this case (and in the case when the first derivative is provided but the second is not), Dynare will calculate the missing derivatives numerically using finite differences methods (central differences for the Jacobian and eqs. 25.3.24 and 25.3.27 from Abramowitz and Stegun (1965), pg 884 for the Hessian);

Example

1. The case when one M-/MEX file contains the function output, its Jacobian and Hessian:

function [y df d2f]=funcname(a,b)
y=a*(b^2);

da=b^2;
db=2*a*b;
df=[da db];

d2f=[0 2*b; 2*b 2*a];
end

2. The case when there is a separate M-/MEX file for funcname and its derivative, funcname_deriv:

function y=funcname(a,b)
y=a*(b^2);
end

function df=funcname_deriv(a,b)
da=b^2;
db=2*a*b;
df=[da db];
end

User Syntax in the .mod file

The keyword external_function is reserved for the declaration of external functions. It accepts the following options:

  • name = STRING: the name of the function, which must also be the name of the M-/MEX file implementing it.

  • nargs = INTEGER: the number of arguments of the function. If this option is not provided, Dynare assumes nargs = 1.

  • first_deriv_provided: tells Dynare that the M-/MEX file specified by the argument passed to name returns the Jacobian as its second output argument.

  • first_deriv_provided = STRING: tells Dynare that the Jacobian is provided as the only output of the M-/MEX file given as the option argument.

  • second_deriv_provided: tells Dynare that the M-/MEX file specified by the argument passed to name returns the Hessian as its third output argument.

  • second_deriv_provided = STRING: tells Dynare that the Hessian is provided as the only output of the M-/MEX file given as the option argument.

An external_function statement must be used once for every external function referenced in the model_block. Further, the statement must be issued somewhere before the model_block. The user does not need to include an external_function statement for external functions referenced outside of the model_block.

Examples

1. Declare an external function with name funcname, accepting only one argument, whose derivatives must be computed numerically by Dynare:

external_function(name = funcname);

2. Declare an external function with name funcname, accepting two arguments, whose derivatives are returned as the second and third output arguments of the M-/MEX file funcname:

external_function(name = funcname, nargs = 2, first_deriv_provided, second_deriv_provided);

3. Declare an external function with three arguments, whose first derivative is provided by the M-/MEX file funcname_deriv, and whose second derivative must be computed numerically by Dynare:

external_function(name = funcname, nargs = 3, first_deriv_provided = funcname_deriv);

DynareWiki: ExternalFunctions (last edited 2010-03-01 14:31:31 by HoutanBastani)