Declaration of External Procedures and Functions

External procedures and functions

External procedures and functions are special types of nodes in the model tree. They have the same attributes as internal procedures and functions with the exception of the Body and Derivative attributes, which are replaced by the attributes in this table.

Table 20 Additional attributes of external procedures and functions

Attribute

Value-type

See also page

DllName

string, file-identifier

ReturnType

integer, double

Property

FortranConventions, UndoSafe

The Property attribute

BodyCall

external-call

DerivativeCall

external-call

The DllName attribute

With the mandatory DllName attribute you can specify the name of the DLL which contains the external procedure or function to which you want to make a link in your AIMMS application. The value of the attribute must be a string, a string parameter, or a File identifier, representing the path to the external DLL.

Search path

If you only specify a DLL name, AIMMS will search for the DLL in all directories in the AIMMSUSERDLL environment variable, and the PATH environment variable on Windows, or the LD_LIBRARY_PATH environment variables on Linux, respectively. In addition, on Windows, AIMMS will also search for the DLL in the project folder. If you specify a relative path including a folder (possibly ./), AIMMS will take this path relative to the project folder. If you specify an absolute path, AIMMS will try to open the DLL at the specified location.

File identifier and unit conventions

When you use a File identifier to specify an external DLL name, AIMMS will use the Convention attribute of that File identifier (if specified) to pass numeric values to any procedure or function in that DLL according to the specified unit convention (see also Globally Overriding Units Through Conventions). When the DLL name has not been specified through a File identifier, or when its Convention attribute is left empty, AIMMS will use the unit convention specified for the main model.

Default argument scaling

Without any such convention, AIMMS will use the default convention, i.e. arguments will be scaled according to the unit specified for each argument, and AIMMS will assume that the result of an external function is scaled according to the unit specified in its Unit attribute. Unit analysis for functions and procedures is discussed in full detail in Unit Analysis of Procedures and Functions.

The ReturnType attribute

The ReturnType indicates the type of any scalar numerical value returned by the DLL function. The possible values are integer and double. AIMMS will use the value returned by the DLL function either as the return value of the ExternalProcedure, or as the (numerical) function value of the ExternalFunction, whichever is applicable. If you do not specify the ReturnType attribute, AIMMS will discard any value returned by the function.

Restricted use

You cannot directly use the returned value of a DLL function as the function value of an ExternalFunction when its return value is either an indexed parameter, a set, a set element or a string. In such cases you must pass the function name as an additional external argument to the DLL function, and specify how the function value must be dealt with.

Example

Consider a C function Cobb_Douglas_Arg with prototype

void Cobb_Douglas_Arg( int n, double *a, double *c, double *CDValue );

which passes the Cobb-Douglas function value through the argument CDValue instead of as the return value. In this example CDValue is a scalar, which could have been passed as the result of the DLL function as well. The following ExternalFunction declaration provides a link with Cobb_Douglas_Arg and obtains its function value via the argument list.

ExternalFunction CobbDouglasArgument {
    Arguments     : (a,c);
    Range         : nonnegative;
    DllName       : "Userfunc.dll";
    BodyCall      : {
        Cobb_Douglas_Arg( card : InputFactors, array: a, array: c,
            scalar: CobbDouglasArgument );
    }
}

The Property attribute

With the Property attribute you can specify through the FortranConventions property whether the external function is based on FORTRAN calling conventions. By default, AIMMS will assume that the DLL function is written in a C-like languages such as C, ++ or PASCAL. The precise differences between both calling conventions are explained in full detail in C Versus FORTRAN conventions. In addition, for external procedures, you can specify the UndoSafe property. The semantics of the UndoSafe property is discussed in Internal Procedures.

Formal argument types

As with internal procedures and functions, all formal arguments of an external procedure or function must be declared as local identifiers. AIMMS supports the following identifier types for formal arguments of external procedures and functions:

  • simple sets and relations,

  • scalar and indexed Parameters,

  • scalar and indexed Variables (external functions only), and

  • Handles (external procedures only).

Argument handling

Many details regarding the handling of arguments of internal procedures and functions also apply to external procedures and functions. Thus, arguments of external procedures and functions can be defined over global and local sets, and their associated units of measurement can be specified in terms of either global units or locally defined unit parameters, completely similar to internal procedures and functions (see Internal Procedures).

Handle arguments

The Handle identifier type is only supported for formal arguments of external procedures, i.e. it is not possible to declare global identifiers of type Handle. The following rules apply:

  • Handle arguments are always declared as scalar local identifiers,

  • Handle arguments can only be passed to the DLL function as an integer Handle (see below), and

  • the actual argument in a call to the external procedure corresponding to a formal Handle argument can be a (sliced) reference to an identifier in your model of any type and of any dimension.

Handle arguments allow you to completely circumvent any type checking on actual arguments with respect to the dimension and the respective index domains of the corresponding formal arguments in the call to an external procedure. As a result of this, however, the actual data transfer of Handle arguments to the DLL function must completely take place via the AIMMS API (see also The AIMMS Programming Interface).

Note

Unfortunately, in AIMMS versions lower than 4.90, in terms of compilation it did not matter whether you specified an argument as a Handle or as a real typed argument, like, for example, a two-dimensional string parameter. In other words: because of the ‘handle’ in the actual body call, the argument itself implicitly acted as being of type Handle. So, you did not get any type errors during compilation.

In AIMMS 4.90 you now get a warning when the actual passed-in argument to a procedure does not match the type or dimension of the argument declaration. However, compilation and execution will continue as it did in earlier versions.

It is recommended to have a look at these warnings and try to fix them. The easiest way is to make the argument a Handle as well but perhaps you can make the argument really match and thus make the call more type safe. In a future version of AIMMS these warnings will be treated as errors and then you are forced to modify the code.

If you encounter these warnings in libraries that you cannot change yourself, for example in a repository library of AIMMS itself, then please have a look whether there exists a more recent and upgraded version of that library. If not, and it is a library provided by AIMMS, please let us know.

The BodyCall attribute

In the mandatory BodyCall attribute you must specify the call to the DLL procedure or function, to which the execution of the ExternalProcedure or Function must be relayed. Such an external call specifies:

  • the name of the DLL function or procedure that must be called, and

  • how the actual AIMMS arguments must be translated into arguments suitable for the DLL function or procedure.

Any external call must be specified according to the syntax below. In the Model Explorer, you can specify all components of the BodyCall attribute using a wizard which will guide you through most of the necessary detail.

Syntax

external-call:

image/svg+xmlDLL-function ( external-argument , )

external-argument:

Mandatory translation type

The mandatory translation type indicates the type of the external argument into which the actual argument must be translated before being passed to the external procedure. The following translation types are supported.

  • scalar: the actual scalar AIMMS argument is passed on as a scalar of the indicated external data type.

  • literal: the literal specified in the external call is passed on as a scalar of the indicated external data type, i.e. a literal argument does never correspond to an actual AIMMS argument, but is specified directly in the BodyCall attribute.

  • array: the AIMMS argument is passed on as an array of values according to the indicated translation type and external data type. The precise manner in which the translation takes place is discussed below.

  • card: the cardinality of a set argument is passed on as an integer value. The set argument can be either a set passed as an actual AIMMS argument or the domain set of a multi-dimensional parameter passed as an actual argument.

  • handle: an integer handle to a (sliced) set or parameter argument is passed on. Within the external procedure you must use functions from the AIMMS API (see also The AIMMS Programming Interface) to obtain the dimension, domain and range associated with the handle, or to retrieve or change its data values.

  • work: an array of the indicated type is passed as a temporary workspace to the external procedure. The actual argument must be an integer expression and is interpreted as the size of the array to be passed on. This translation type is useful for programmers of languages such as standard F77 FORTRAN which lack facilities for dynamic memory allocation.

Actual external argument

The actual external argument specified in an external argument of the BodyCall attribute can be

  • a reference to a formal argument of the ExternalProcedure at hand (for the scalar, array, card, handle and work translation types),

  • a reference to a domain set of a formal multi-dimensional argument of the ExternalProcedure at hand (for the card translation type), or

  • an integer, double or string literal (such as 12345, 123.45 or "This is a string") directly specified within the BodyCall attribute (for the literal translation type).

Input-output type

For every formal argument of an ExternalProcedure, you can specify its associated input-output type through the Input, InOut (default) or Output properties in the Propert attribute of the local argument declaration. With it, you indicate whether or not AIMMS should consider any changes made to the argument by the DLL function. For each input-output type, AIMMS performs the following actions:

  • Input: AIMMS initializes the external argument, but discards all changes made to it by the DLL function,

  • InOut: AIMMS initializes the external argument, and passes back to the model the values returned by the DLL function, or

  • Output: AIMMS allocates memory for the external argument, but does not initialize it; the values returned by the DLL function are passed back to the model.

As with internal functions, all ExternalFunction arguments are Input by definition. The return value of an ExternalProcedure and the function value of an ExternalFunction are considered as an (implicit) Output argument when passed to the DLL function as an external argument.

External data type

In translating AIMMS arguments into values (or arrays of values) suitable as arguments for an external procedure or function, AIMMS supports the external data types listed in this table.

Table 21 External data types

External data type

Passed as

integer

4-byte (signed) integer

double

8-byte double precision floating number

string

C-style string

integer8

1-byte (signed) integer

integer16

2-byte (signed) integer

integer32

4-byte (signed) integer

Allowed combinations

Not all combinations of input-output types, translation types and external data types are supported (or even useful). this table describes all allowed combinations, as well as the resulting argument type that is passed on to the external procedure. The external data types printed in bold are the default, and can be omitted if appropriate. Throughout the table, the data type integer can be replaced by any of the other integer types integer8, integer16 or integer32.

Table 22 Allowed combinations of translation, input-output and data types

Allowed types

AIMMS argument

Passed as

Translation

Input-Output

Data

scalar

input

integer

scalar expression

integer

double

double

string

string

inout, output

integer

scalar reference

integer pointer

double

double pointer

string

string

literal

n/a

integer

n/a

integer

double

double

string

string

card

n/a

n/a

set, parameter

integer

array

input, inout, output

integer

parameter

integer array

double

double array

integer

element parameter

integer array

string

set

string array

string

string/unit parameter

string array

handle

input, inout, output

n/a

set, parameter, handle

integer

work

n/a

integer

integer expression

integer array

double

double array

Passing array arguments

When you are passing a multidimensional AIMMS identifier to an external procedure or function as a array argument, AIMMS passes a one-dimensional buffer in which all values are stored in a manner that is compatible with the storage of multidimensional arrays in the language which you have specified through the Property attribute. The precise array numbering conventions for both C-like and FORTRAN arrays are explained in C Versus FORTRAN conventions.

Encoding of string arguments

The strings communicated with your DLL have an encoding. This encoding is set by the option external_string_character_encoding, which has a default of UTF8. This option can be overridden by using the Encoding attribute of string parameters, similar to the Encoding attribute of a File, see The Encoding attribute. On Windows, using the encoding UTF-16LE and on Linux, using the encoding UTF-32LE, the strings are passed as a wchar_t* array, otherwise the strings are passed as a char * array.

Output string arguments

When you pass a scalar or multidimensional output string argument, AIMMS will pass a single char buffer of fixed length, or an array of such buffers. The length is determined by the option external function string buf size. The default of this option is 2048. You must use the C function strcpy or a similar function to copy the string data in your DLL to the appropriate char buffer associated with the output string argument.

Full versus sparse data transfer

When considering your options on how to pass a high-dimensional parameter to an external procedure, you will find that passing it as an array is often not the best solution. Not only will the memory requirements grow rapidly for increasing dimension, but also running over all elements in the array inside your DLL function may turn out to be a very time-consuming process. In such a case, it is much better practice to pass the argument as an integer handle, and use the AIMMS API functions discussed in Communicating Individual Identifier Values to retrieve only the nondefault values associated with the handle. You can then set up your own sparse data structures to deal with high-dimensional parameters efficiently.

Translation modifiers …

In addition to the translation types, input-output types and external data types you can specify one or more translation modifiers for each external argument. Translation modifiers allow you to slightly modify the manner in which AIMMS will pass the arguments to the DLL function. AIMMS supports translation modifiers for specifying the precise manner in which

  • special values,

  • the data associated with handles, and

  • set elements,

are passed.

… for special values

When a parameter or variable that you want to pass to an external DLL contains special values like ZERO or INF, AIMMS will, by default, pass ZERO as 0.0, INF and -INF as \(\pm\)1.0e150, and will not pass any of the values NA and UNDF. When you specify the translation modifier retainspecials, AIMMS will pass all special numbers by their internal representation as a double precision floating point number. You can use the AIMMS API functions discussed in Communicating Individual Identifier Values to obtain the MapVal value (see also this table) associated with each number. The translation modifier retainspecials can be specified for numeric arguments that are passed either as a full array or as an integer handle.

… for handles

When passing a multidimensional identifier handle to an external DLL, AIMMS can provide several methods of access to the data associated with the handle by specifying one of the following translation modifiers:

  • ordered: the data retrieval functions will pass the data values according to the particular ordering imposed any of the domain sets of the identifier associated with the handle. By default, AIMMS will use the natural ordering determined by the data entry order of all domain sets.

  • raw: the data retrieval functions will also pass inactive data (see also Data Control). By default, AIMMS will not pass inactive data.

The details of ordered versus unordered and raw data transfer are discussed in full detail in Communicating Individual Identifier Values.

… for set elements

AIMMS can pass set elements (in the context of element parameters and sets) to external procedures in various manners. More specifically, set elements can be translated into:

  • an integer external data type, or

  • a string external data type.

When the external data type is string, AIMMS will pass the element name for each set element. Transfer of element names is always input only. In general, when the external data type is integer, AIMMS can pass either

  • the ordinal number with respect to its associated subset domain (ordinalnumber modifier), or

  • the element number with respect to its associated root set (elementnumber modifier).

Alternatively, when set elements are passed in the context of a set you can specify the indicator modifier in combination with the integer external data type. This will result in the transfer of a multidimensional binary parameter which indicates whether a particular tuple is or is not contained in the set.

Passing element parameters

When you pass an element parameter as an integer scalar or array argument, AIMMS will assume the ordinalnumber modifier by default. When passed as integer, element parameters can be input, output or inout arguments. When element parameters are passed as string arguments, they can be input only.

When to use

Element numbers and ordinal numbers each can have their use within an DLL function. Element numbers remain identical throughout a modeling session using a single data set, regardless of addition and deletion of set elements, or any change in set ordering. For this reason, it is best to use element numbers when the set elements need to be used in multiple calls of the DLL function. Ordinal numbers, on the other hand, are the most convenient means for passing permutations that are used within the current external call only. With it, you can directly access a permuted reference in other array arguments.

Passing set arguments

Sets can be passed as array arguments to an external DLL function. When passing set arguments, you have to make a distinction between one-dimensional root sets, one-dimensional subsets (both either simple or relation), and multidimensional subsets and indexed sets. The following rules apply.

Pass as onedimensional array

One-dimensional root sets and subsets can be passed as a one-dimensional array of length equal to the cardinality of the set. To accomplish this, you can must pass such a set as

  • an array of integer numbers, representing either the ordinal or element numbers of each element in the set (using the ordinalnumber or elementnumber modifier), or

  • a string array, representing the names of all elements in the set.

One-dimensional set arguments passed in this manner can only be input arguments. As a specific consequence, you cannot modify the contents of root sets passed as array arguments.

Pass as indicator parameter

You can pass any subset (whether it is simple, relation or indexed) as a multidimensional integer indicator array defined over its respective domain sets, indicating whether a particular tuple of domain set elements is contained in the subset (value equals 1) or not (value equals 0). The dimension of such indicator parameters is given by the following set of rules:

  • the dimension for a simple subset is 1,

  • the dimension for a multidimensional relation is the dimension of the Cartesian product of which the set is a subset,

  • the dimension of an indexed set is the dimension of the index domain of the set plus 1.

Set arguments passed as an indicator argument can be of input, output, or inout type. In the latter two cases modifications to the 0-1 values of the indicator parameter are translated back into the corresponding element memberships of the subset.

Set argument defaults

When you pass set arguments to an external DLL, AIMMS will assume no default translation methods when the set is passed as an integer array, as each type of set does not allow every translation method. For integer set arguments you should therefore always specify one of the translation modifiers ordinalnumber, elementnumber or indicator.

Passing set handles

Sets can also be passed by an integer handle. AIMMS offers various API functions (see also Obtaining Identifier Attributes) to obtain information about the domain of the set, its cardinality and elements, and to add or remove elements to the set.