User functions
The most complicated formula in this model is the area calculation. With only 5 sides, it is still possible to write it out explicitly, but it becomes large (and perhaps inefficient) if the number of sides increases. The alternative is to calculate the formula in a function and then use the function within the model.
A user function is essentially a function which is not built in to Xpress NonLinear. It can be written in a language such as C or Fortran, and compiled into a DLL; it can be written as a set of formulae in an Excel spreadsheet (with or without a macro as well); it can be written entirely within an Excel macro. This example shows the area function written as a compiled C function.
A user function in C
This function calculates the area from an array of values, ordered as (RHO1, THETA1, RHO2, THETA2, ...). The number of items in the Values array is given as the first item in nArg.
double XPRS_CC MyFunc(double *Values, int *nArg) { int i; double Area; Area = 0; for(i=3; i<nArg[0]; i=i+2) { Area = Area + Values[i-3]*Values[i-1]*sin(Values[i]-Values[i-2]); } return Area*0.5; }
This is the standard interface for a user function in Xpress NonLinear. The first argument is an array of double precision values holding the values of the arguments for the Xpress NonLinear function in order; the second argument is an array of integers, the first of which contains the size of the first array.
The function must be declared using XPRS_CC as shown, to ensure that the correct function linkage is created.
This function can be compiled into a DLL. To make use of it, we also need to be able to access the formula from outside, so you may need to add suitable externalization definitions. In Visual C++ under Microsoft Windows, you can use a Definition File, containing an EXPORTS section, such as:
EXPORTS MyFunc=_MyFunc@8
Extending the polygon model
We can now declare this function in the model and use it instead of the explicit area formula.
nToken = 0; XSLPsetstring(sprob, &i, "MyFunc"); Type[nToken] = XSLP_STRING; Value[nToken++] = (double) i; Type[nToken] = XSLP_UFEXETYPE; Value[nToken++] = (double) 0x01; Type[nToken] = XSLP_UFARGTYPE; Value[nToken++] = (double) 023; XSLPsetstring(sprob, &i, "MyDLL.DLL"); Type[nToken] = XSLP_STRING; Value[nToken++] = (double) i; Type[nToken] = XSLP_EOF; XSLPloaduserfuncs(sprob, 1, Type, Value); XSLPaddnames(sprob, XSLP_USERFUNCNAMES, "MyArea", 1, 1);
User functions are declared using XSLPloaduserfuncs. The definition of the function is stored in parsed arrays similar to the ones used for defining formulae. There are two special token types used here; see the Xpress NonLinear Reference Manual for full details about the corresponding values.
We must also define the name of the function. This is a character string, and it is the first item in the array of tokens. To pass a character string to Xpress NonLinear, use the XSLPsetstring function to store the string and return an index to the string. Then use the index with the XSLP_STRING token type.
Because this is a DLL function, we must also define the name of the DLL. This is the first string after the tokens defining the function and argument types. For other types of function (for example, Excel spreadsheets or macros), other string parameters may be needed as well.
The XSLPaddnames function creates a name for the function to be used inside Xpress NonLinear when the function is referenced. It is what you will see if you write the problem out using XSLPwriteprob. It can be the same name as the function name in the DLL, but it does not have to be. If you are not writing the problem out, then you do not need to set a name at all.
Type[nToken] = XSLP_RB; Value[nToken++] = 0; for (i=nSide-1; i>0; i--) { Type[nToken] = XSLP_COL; Value[nToken++] = i; Type[nToken] = XSLP_COL; Value[nToken++] = nSide+i; } Type[nToken] = XSLP_FUN; Value[nToken++] = 1; Type[nToken] = XSLP_EOF; Value[nToken++] = 0;
In reverse Polish, the arguments to the function must appear in reverse order, so the items start with THETA4 and work down to RHO1. The arguments are preceded by a right bracket token and followed by the user function token for function number 1.
Internal user functions
The example above used a function written in a DLL. If the function is compiled into something else – for example, the main executable program – or is not externalized, then you will need to define its address explicitly.
void *Func; Func = MyFunc; XSLPchguserfuncaddress(sprob, 1, &Func);
XSLPchguserfuncaddress takes as its arguments the number of the function, and a pointer to its address. As usual, if the pointer is NULL, the data is left unaltered. The main use of the routine is to define the address of a user function directly, without relying on Xpress NonLinear to find it.
© 2001-2019 Fair Isaac Corporation. All rights reserved. This documentation is the property of Fair Isaac Corporation (“FICO”). Receipt or possession of this documentation does not convey rights to disclose, reproduce, make derivative works, use, or allow others to use it except solely for internal evaluation purposes to determine whether to purchase a license to the software described in this documentation, or as otherwise set forth in a written software license agreement between you and FICO (or a FICO affiliate). Use of this documentation and the software described in it must conform strictly to the foregoing permitted uses, and no other use is permitted.