/*
ASCEND modelling environment
Copyright (C) 1990, 1993, 1994 Thomas Guthrie Epperly, Kirk Andre Abbott
Copyright (C) 2006 Benjamin Allan
Copyright (C) 2006 Carnegie Mellon University
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*//**
@file
External Functions Module.
This module implements the ExternalFunc structure referenced by
black and glass box structures and external methods.
The ExternalFunc structure stores the number of input and
output parameters (formal)
as well as 'help' string and 'name' string' for each of these 'calls'.
This header also provides functions for ExternalFunc library maintenance.
This allows ASCEND to maintain a list of the ExternalFunc requests derived
from statements in the model(s). When compilation completes, I suppose
it should be possible to alert the user about any external functions
that were not able to be resolved.
@todo Complete documentation of compiler/extfunc.h.
Requires:
#include "utilities/ascConfig.h"
#include "compiler/instance_enum.h"
#include "general/list.h"
#include "compiler/compiler.h"
*//*
by Kirk Andre Abbott and Ben Allan
Created: July 4, 1994.
Version: $Revision: 1.5 $
Version control file: $RCSfile: extfunc.h,v $
Date last modified: $Date: 1997/07/18 12:29:30 $
Last modified by: $Author: mthomas $
*/
#ifndef ASC_EXTFUNC_H
#define ASC_EXTFUNC_H
/** @addtogroup compiler_rel Compiler Relations
@{
*/
#include
#include "relation_util.h"
/*------------------------------------------------------------------------------
type definitions and forward decls
*/
/**
ExtEvalFunc type is a function pointer.
@see rootfind.c:51
@param mode 'to pass to the eval function' (?)
@param m relation index
@param n variable index
@param x 'x' vector (?)
@param u 'u' vector (?)
@param f vector of residuals
@param g vector of gradients
*/
typedef int ExtEvalFunc(int *mode, int *m, int *n,
double *x, double *u, double *f, double *g);
/**
This is an enum to clarify and make type-safer the
the variation of external functions circa 1995.
Blackboxes might be callable from methods as well (@TODO), but
this is dependent on their code registration to setup.
*/
enum ExternalFuncType {
efunc_ERR = 0, /**< err value (Traps old mode errors too) */
efunc_BlackBox = 10, /**< remainder of struct is blackbox */
#if 0
efunc_GlassBox = 20, /**< remainder of struct is glassbox */
#endif
efunc_Method = 30 /**< remainder of struct is method */
};
struct GlassBoxExternalFunc {
ExtEvalFunc *initial;
ExtEvalFunc **value; /**< array of relation residual functions. */
ExtEvalFunc **deriv; /**< array of relation gradient functions. */
ExtEvalFunc **deriv2; /**< array of relation hessian functions. */
ExtEvalFunc *final; /**< cleanup function. */
};
/** values a blackbox (or ?) can report when returning. */
enum Calc_status {
calc_converged,
calc_diverged,
calc_fp_error,
calc_incorrect_args,
calc_error,
calc_all_ok
};
/**
Things that a blackbox can be asked to do.
@NOTE Rhetorical question: Why do we want this? See comments in Slv_Interp.
*/
enum Request_type {
bb_none, /**< do nothing. should never be sent. */
bb_first_call, /**< will be given when the initial function pointer is called. */
bb_last_call, /**< will be given when the final function pointer is called. */
bb_check_args, /**< do any argument checking of the variables, data */
bb_recalculate, /**< the caller thinks the input may have changed: recalc if reqd */
bb_func_eval, /**< the caller needs the residual function pointer. */
bb_deriv_eval, /**< the caller needs the deriv function pointer. */
bb_hess_eval, /**< the caller needs the hessian function pointer. */
bb_single_step /**< the caller would like one step toward the solution;
usually this is meaningless and should be answered with calc_diverged. */
};
/**
Each blackbox equation may show up more than once in a simulation
if models repeat in the structure. For each occurence of the
blackbox, a unique BBoxInterp object is given when the set of
corresponding relations is created.
It is used for the blackbox to communicate to the rest of the system.
If the blackbox retains internal state between evaluation calls,
it should store this state in the user_data pointer.
*/
struct BBoxInterp {
/** status is set by blackbox calls before returning. */
enum Calc_status status;
/** user_data is set by the blackbox if it has any persistent state
during calls to initial and final functions given in
CreateUserFunctionBlackBox.
*/
void *user_data;
/** What the caller wants done on a given call.
As black boxes are represented with 5 function pointers,
one might think this is not needed. Providing the 'task' here allows
one to implement only one function and have it handle all types of
calls. It also facilitates cases where there is checking rather than
evaluation.
@NOTE Problem? Don't init functions and evaluation functions have
different signatures?
*/
enum Request_type task;
/* if someone still needs a nodestamp, we could go back to
defining one, but it should really be inside user_data as
ascend doesn't need it. */
};
typedef int ExtBBoxInitFunc(struct BBoxInterp *interp
, struct Instance *data
, struct gl_list_t *arglist
);
/**<
Note Well: References to ASCEND instances must not be
cached in the user_data of interp, as instances may move
in the dynamically typed ASCEND world!
@param interp is the structure that all subsequent calls to
evaluation functions will include.
@param data is the DATA instance from the ascend model.
@param arglist is the input and output list of instance lists,
where each element corresponds to a formal argument.
Elements 1:n_formal_inputs are the inputs and the next
n_formal_outputs elements are the outputs.
@return 0 if ok, nonzero if some error makes the blackbox impossible.
*/
typedef void ExtBBoxFinalFunc(struct BBoxInterp *interp);
/**< @param interp same as called in init and evaluate.
This is the opportunity to deallocate user_data as the
instance is being destroyed.
*/
/**
External black box equations are of the block form
y_out = f(x_in). This block expands to N_outputs equations
of the form y_out[i] = f_i(x_in), where the functional details
of f are assumed to be smooth enough but otherwise totally hidden
and x_in, y_out are non-overlapping sets of variables.
The compiler will warn if overlap is detected.
Normal D.O.F. solver analysis applies in most cases.
Note that solvers are not psychic; if this blackbox is embedded
in a larger model such that some of y_out are inconsistently
fixed variables, the odds of convergence are small.
Cleverer solvers may issue a warning.
@param interp the control information is exchanged in interp;
interp->task should be consulted.
@param ninputs the length of the inputs, xi_in.
@param noutputs, the length of the outputs, y_out.
@param jacobian, the partial derivative df/dx, where
each row is df[i]/dx[j] over each j for the y_out[i] of
matching index. The jacobian array is 1-D, row major, i.e.
df[i]/dx[j] -> jacobian[i*ninputs+j].
@TODO this one may need splitting/rework for hessian.
*/
typedef int ExtBBoxFunc(struct BBoxInterp *interp,
int ninputs, int noutputs,
double *inputs, double *outputs, double *jacobian);
struct BlackBoxExternalFunc {
ExtBBoxInitFunc *initial; /**< called after instance construction */
ExtBBoxFunc *value; /**< relation residual function. */
ExtBBoxFunc *deriv; /**< relation gradient function (see jacobian)*/
ExtBBoxFunc *deriv2; /**< relation hessian function. */
ExtBBoxFinalFunc *final; /**< cleanup function called at instance destruction. */
double inputTolerance; /**< largest change in an input variable
that is allowable without recalculating. */
};
/**
Function pointer (type) to implement an external method on a particular
instance
@param context the instance on which the method is run.
context may also appear explicitly in the arg list as SELF.
@param args Each element of args is a list of instances; each
name in the ascend-language argument list is expanded to a list
(which may contain 0 or more Instances) and appended to args.
@return ???
*/
typedef int ExtMethodRun(struct Instance *context, struct gl_list_t *args, void *user_data);
/**
Setup/teardown, if any needed, for a particular instance.
We don't actually support this method anywhere right now, as
we're not sure what it can logically be used for that the
init function in dlopening shouldn't be doing.
In principal, we could add and cache a client-data pointer
in each instance so that the external method may be stateful.
Presently, the external methods must be clever to do that
on their own or must use ascend instances for state instead.
@param context the instance on which the method may be run.
*/
typedef int ExtMethodInit( struct Instance *context);
/**
Destroy function (note comments for ExtMethodInit).
This function deallocated user_data for a particular external method.
In the case of external python script methods, for example, this will perform
Py_DECREF on the external script, so that python can unload it.
Couldn't see a way to do this without adding back this function here. -- JP
@return 0 on success, else error code.
*/
typedef int ExtMethodDestroyFn( void *user_data);
struct MethodExternalFunc {
ExtMethodRun *run; /**< the method invoked. */
void *user_data; /**< I'd anticipate that this would be a function pointer
implemented in an external scripting language. Should only be accessed
from inside the 'run' function! -- JP */
ExtMethodDestroyFn *destroyfn;
};
struct ExternalFunc {
enum ExternalFuncType etype;
CONST char *name; /**< a string we own. */
CONST char *help; /**< a string we own. */
unsigned long n_inputs; /**< expected # of formal inputs. */
unsigned long n_outputs; /**< expected # of formal outputs. */
union {
#if 0
struct GlassBoxExternalFunc glass;
#endif
struct BlackBoxExternalFunc black;
struct MethodExternalFunc method;
} u;
};
/*------------------------------------------------------------------------------
REGISTRATION / LOOKUP FUNCTIONS
*/
extern void InitExternalFuncLibrary(void);
/**<
The main external functions library initialization routine. This
function must be called before all others.
*/
extern void DestroyExtFuncLibrary(void);
/**<
Destroys the external function library and deallocates all the
information associated with it.
*/
extern int AddExternalFunc(struct ExternalFunc *efunc, int force);
/**<
Adds an external function node to the external function library.
We look up the external function before adding it to the
library. If force is zero and the function exists then nothing
is done and 0 is returned. If force is true, then the old entry is
removed and the new one is added; 1 is returned. If the name is not
found then the information is added to the library.
@return 1 if an element is added to ExternalFunctionLibrary Table,
or 0 if no addition is made.
*/
ASC_DLLSPEC struct ExternalFunc *LookupExtFunc(CONST char *funcname);
/**<
Returns the external function having the given name, or NULL if
not found.
*/
extern struct ExternalFunc *RemoveExternalFunc(char *name);
/**<
Removes the external function having the given name from the
External function library.
*/
extern void DestroyExternalFunc(struct ExternalFunc *name);
/**<
Destroys an external function, but does *not* remove it from the
library. Use the RemoveExternalFunc library first to retrieve the
information, then call this function.
*/
extern void PrintExtFuncLibrary(FILE *f);
/**<
Prints the contents of the external function library to the given
file. The file must be opened for writing.
*/
ASC_DLLSPEC char *WriteExtFuncLibraryString(void);
/**<
Returns a string of formatted information about the external functions
defined. the string looks like "{{name1} {help1}} {{name2} {help2}} "
The string may be empty/NULL if there are no external functions loaded.
*/
/**
This provides a way for other code to visit the external function list
*/
ASC_DLLSPEC void TraverseExtFuncLibrary(void (*)(void *,void *),void *secondparam);
/** fetch the required formal input count for glass, black, or method. */
ASC_DLLSPEC unsigned long NumberInputArgs(CONST struct ExternalFunc *efunc);
/** fetch the required formal output count for glass, black, or method. */
ASC_DLLSPEC unsigned long NumberOutputArgs(CONST struct ExternalFunc *efunc);
ASC_DLLSPEC CONST char*ExternalFuncName(CONST struct ExternalFunc *efunc);
/**<
Returns the name of an external function.
*/
/*------------------------------------------------------------------------------
EXTERNAL METHOD STUFF
*/
ASC_DLLSPEC int CreateUserFunctionMethod(CONST char *name
,ExtMethodRun *run
,CONST long n_args
,CONST char *help
,void *user_data
,ExtMethodDestroyFn *destroyfn
);
/**<
* Adds an external method call to the ASCEND system.
* The name of the function is looked up. If it already exists, the
* information will be updated. If the name was not found in the
* external function library, then an external function node will be
* created and added to the external function library. We make a
* *copy* of the help string if it is provided. We also make a copy
* of the name. Anyone desirous of ASCEND knowing about their
* external methods must use this protocol.
*
*
* Note on blackbox integration with nonlinear solvers:
* The basic idea is that the blackbox has inputs x and outputs y
* that are all variables to the ascend model. Some of these may
* be fixed variables, but this is of no consequence to the blackbox
* routine unless one of the fixed variables is fixed outside the
* bounds of feasible input to the box. In the newton solution
* process there are three sets of values involved, x (inputs),
* yhat (the outputs as computed by the box), and y (the proposed
* values of the outputs in the model which may not match yhat
* until the entire model is converged. The many equations of the
* blackbox are hidden and represented by the reduced set of
* equations. In mathematical form, an array of equations:
* y = yhat(x); (ascend form bbrel: bboxname(inputs, outputs, data);)
* where yhat(x) is computed as bboxname(x,yhat).
* The bbox may produce the reduced gradient or be finite differenced
* to get dyhat/dx partial derivatives.
* The residual then, obviously, is yhat - y and the gradient is
* (in matrix form) I-dyhat/dx for the reduced equations.
*
* @param name Name of the function being added (or updated).
* @param run Pointer to the method.
* @param n_args number of arguments expected as input, or -1 if any number is allowed.
* @param help a string for human consumption.
* @return Returns 0 if the function was successfully added,
* non-zero otherwise.
*/
/** Fetch method run function. */
extern ExtMethodRun *GetExtMethodRun(struct ExternalFunc *efunc);
extern void *GetExtMethodUserData(struct ExternalFunc *efunc);
/*------------------------------------------------------------------------------
BLACK BOX STUFF
*/
/** Fetch black initialization function. */
extern ExtBBoxInitFunc *GetInitFunc(struct ExternalFunc *efunc);
/** Fetch black residual function. */
extern ExtBBoxFunc *GetValueFunc(struct ExternalFunc *efunc);
/** Fetch black sensitivity gradient function. */
extern ExtBBoxFunc *GetDerivFunc(struct ExternalFunc *efunc);
/** Fetch black hessian function. */
extern ExtBBoxFunc *GetDeriv2Func(struct ExternalFunc *efunc);
/** Fetch black cleanup function. */
extern ExtBBoxFinalFunc *GetFinalFunc(struct ExternalFunc *efunc);
/** Fetch black inputTolerance. */
extern double GetValueFuncTolerance(struct ExternalFunc *efunc);
ASC_DLLSPEC int CreateUserFunctionBlackBox(CONST char *name,
ExtBBoxInitFunc *init,
ExtBBoxFunc *value,
ExtBBoxFunc *deriv,
ExtBBoxFunc *deriv2,
ExtBBoxFinalFunc *final,
CONST unsigned long n_inputs,
CONST unsigned long n_outputs,
CONST char *help,
double inputTolerance
);
/**<
Adds an external function to the ASCEND system.
The name of the function is looked up. If it already exists, the
information will be updated. If the name was not found in the
external function library, then an external function node will be
created and added to the external function library. We make a
*copy* of the help string if it is provided. We also make a copy
of the name. Anyone desirous of ASCEND knowing about their
functions must use this protocol.
Note on blackbox integration with nonlinear solvers:
The basic idea is that the blackbox has inputs x and outputs y
that are all variables to the ascend model. Some of these may
be fixed variables, but this is of no consequence to the blackbox
routine unless one of the fixed variables is fixed outside the
bounds of feasible input to the box. In the newton solution
process there are three sets of values involved, x (inputs),
yhat (the outputs as computed by the box), and y (the proposed
values of the outputs in the model which may not match yhat
until the entire model is converged. The many equations of the
blackbox are hidden and represented by the reduced set of
equations. In mathematical form, an array of equations:
y = yhat(x); (ascend form bbrel: bboxname(inputs, outputs, data);)
where yhat(x) is computed as bboxname(x,yhat).
The bbox may produce the reduced gradient or be finite differenced
to get dyhat/dx partial derivatives.
The residual then, obviously, is yhat - y and the gradient is
(in matrix form) I-dyhat/dx for the reduced equations.
@param name Name of the function being added (or updated).
@param init Pointer to initialisation function, or DefaultExtBBoxInitFunc if none.
@param final Pointer to shutdown function, or DefaultExtBBoxFinalFunc if none.
@param value evaluation function pointers, or NULL if none.
@param deriv first partial derivative functions, or NULL if none.
@param deriv2 second derivative functions, or NULL if none.
@param inputTolerance maximum change in any of the inputs that is
allowable without recomputing the outputs.
0.0 is conservative, or specify a larger number if the
outputs are only mildly sensitive to small changes in inputs.
@return Returns 0 if the function was successfully added,
non-zero otherwise.
*/
ASC_DLLSPEC int DefaultExtBBoxInitFunc(struct BBoxInterp *interp
, struct Instance *data
, struct gl_list_t *arglist
);
/**<
Default init code for black boxes.
If the user does not supply an init function (possibly because they have
no per-instance data to manage), they should pass this function (or NULL)
instead to CreateUserFunctionBlackBox. If NULL is supplied, this will be
used.
*/
ASC_DLLSPEC int ErrorExtBBoxValueFunc(struct BBoxInterp *interp
, int ninputs, int noutputs
, double *inputs, double *outputs
, double *jacobian
);
/**<
Default residual code for black boxes. NOT VERY INTERESTING.
If the user does not supply a value function (possibly because they have
no brain) they will get this. It whines. always returns -1.
*/
ASC_DLLSPEC int DefaultExtBBoxFuncDerivFD(struct BBoxInterp *interp
, int ninputs, int noutputs
, double *inputs, double *outputs
, double *jacobian
);
/**<
Default finite-differencing code for blackboxes. If the user does not
supply a derivative function they wrote, they must supply this derivative
function (or NULL) instead. If NULL is supplied, this will be used.
John Pye claims to have filled this in. This has yet to be tested in
the courts, however. He is confident that his client will prevail.
*/
ASC_DLLSPEC int DefaultExtBBoxFuncDeriv2FD(struct BBoxInterp *interp
, int ninputs, int noutputs
, double *inputs, double *outputs
, double *jacobian
);
/**<
Currently a pipe dream. returns an error. Although the API permits this
function to be used, there is as yet no solver that utilises it. CONOPT
appears to be able to make use of it but we haven't wired it up yet.
If the user does not supply a hessian function they wrote, they should
supply this derivative function (or NULL) instead. If NULL is supplied,
this will be used. If NULL is supplied, this will be used.
*/
ASC_DLLSPEC void DefaultExtBBoxFinalFunc(struct BBoxInterp *interp);
/**<
Default finalize code for black boxes.
If the user does not supply a final function (possibly because they have
no per-instance data to manage), they should pass this function
instead to CreateUserFunctionBlackBox. If NULL is supplied, this will be
used.
*/
/*-----------------------------------------------------------------------------
GLASS BOX STUFF
*/
#if 0
ASC_DLLSPEC int CreateUserFunctionGlassBox(CONST char *name,
ExtEvalFunc *init,
ExtEvalFunc **value,
ExtEvalFunc **deriv,
ExtEvalFunc **deriv2,
ExtEvalFunc *final,
CONST unsigned long n_inputs, CONST unsigned long n_outputs,
CONST char *help
);
/**<
Adds an external function to the ASCEND system.
The name of the function is looked up. If it already exists, the
information will be updated. If the name was not found in the
external function library, then an external function node will be
created and added to the external function library. We make a
*copy* of the help string if it is provided. We also make a copy
of the name. Anyone desirous of ASCEND knowing about their
functions must use this protocol.
@param name Name of the function being added (or updated).
@param init Pointer to initialisation function, or NULL if none.
@param value array of evaluation function pointers,
or NULL if none.
@param deriv array of first partial
derivative functions, or NULL if none.
@param deriv2 array of second derivative
functions, or NULL if none.
@return Returns 0 if the function was successfully added,
non-zero otherwise.
*/
/** Fetch glass initialization function. */
extern ExtEvalFunc *GetGlassBoxInit(struct ExternalFunc *efunc);
/** Get glass box residual function array. */
extern ExtEvalFunc **GetValueJumpTable(struct ExternalFunc *efunc);
/** Get glass box gradient function array. */
extern ExtEvalFunc **GetDerivJumpTable(struct ExternalFunc *efunc);
/** Get glass box hessian function array. */
extern ExtEvalFunc **GetDeriv2JumpTable(struct ExternalFunc *efunc);
/** Fetch black initialization function. */
extern ExtEvalFunc *GetGlassBoxFinal(struct ExternalFunc *efunc);
#endif
/* @} */
#endif /* ASC_EXTFUNC_H */