Differences between revisions 10 and 11
Revision 10 as of 2010-01-19 14:38:17
Size: 8454
Comment:
Revision 11 as of 2010-01-19 15:48:43
Size: 8648
Comment:
Deletions are marked like this. Additions are marked like this.
Line 70: Line 70:
 * 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

Currently, Dynare 4 only operates on a small set of mathematical functions in the model description (though it accepts any MATLAB function outside the model, for example in parameter initializations).

This is a regression since Dynare 3 used to be able to operate on any function.

This change was made necessary because Dynare 4 does analytical derivatives, so it needs to know the derivation formula for each function used in the model, while Dynare 3 did numerical derivatives, hence needing less knowledge about the functions.

The purpose of this page is to describe the way we can fix this regression.

We call external functions the functions which are not part of the small set natively supported by Dynare (log, exp, trigonometry...).

Proposed user syntax

We assume that the user wants to use a function called funcname in its 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).

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.

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

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

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.

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:

external_function(name = funcname, nargs = 3);

Syntax examples

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

external_function(name = funcname);
  • Declare an external function with two arguments, whose derivatives are returned as second and third output argument of the implementation:

external_function(name = funcname, nargs = 2, first_deriv_provided, second_deriv_provided);
  • 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:

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

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 CodeInterpreter.hh, in the enum SymbolType, eUnknownFunction should be renamed to eExternalFunction

    • in ExprNode.{cc,hh}, the class UnknownFunctionNode should be renamed ExternalFunctionNode

    • some methods of class ParsingDriver and DataTree should also be renamed

  • A new class ExternalFunctionsTable 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 ModFile class. Since each external function is also given a symbol ID (in the SymbolTable) of type eExternalFunction, this class should provide methods to access information through that symbol ID

  • The class ExternalFunctionNode (formerly UnknownFunctionNode) 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 FirstDerivExternalFunctionNode, which would be a subclass of ExternalFunctionNode. 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 SecondDerivExternalFunctionNode, which would be also be a subclass of ExternalFunctionNode. 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 DataTree 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

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