= 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