Wednesday, July 30, 2008

How to Import User-Defined Functions to AMPL?

AMPL is a mathematical programming language for describing optimization problems. There are quite a number of solvers that accept AMPL model as input. It's a powerful language but sometimes you may need to have your own function to include in your AMPL model. Just to note, if your model includes a user-defined function, it's not possible to use NEOS solvers.

NETLIB provides some explanation on how to import user-defined functions to AMPL but I found the related information rather scattered. Here, I'll try to walk through the process of importing a very simple user-defined function.

First of all, user-defined functions are imported to AMPL through "amplfunc.dll" shared library. If this file exists then AMPL loads the defined functions. AMPL looks for this dll file in the execution directory and the directory (or file) given as a command-line option using "-i".

The same shared library makes user defined functions availbale both to AMPL and to solvers. If AMPL finds a shared-library, it sets the $AMPLFUNC environment variable to inform the linked solvers.

A function can be in one of the 3 categories: deterministic, symbolic (string) and random. Since solvers directly igonore functions returnming strings and non-deterministic functions I'll write a deterministic function.

Below is a very simple function which sums up the given two arguments and sends back the result:

  1. You need to download the header file from Netlib which inlcudes necessary definitions; for example, the defition of the structure "struct arglist" is found in this header file. You'll include it in your c program.

  2. Then, you'll begin to code your function; note that you may have more than 1 function to import. In Visual C/C++, you need to explicilty export the function you want to use in AMPL:

    http://msdn.microsoft.com/en-us/library/ms235636%28VS.80%29.aspx

    __declspec(dllexport) real foo(arglist *al){

    /* the return type is real, which is nothing but a double: see the definition in header file you downloaded */

    /* Now, you'll get the arguments passed by AMPL */

    real arg1 = al->ra[0];
    real arg2 = al->ra[1];

    /* return the result */
    return (arg1+arg2);

    }

  3. Another function you have to export is "funcadd". It'll be used by AMPL to get the information about the user-defined function(s), in this case it's just foo.

    __declspec(dllexport) void funcadd(AmplExports *ae){

    /* 1st param: name of the function to import to AMPL.
    2nd param: pointer to the function "foo"
    3rd param: function type...you can get more info from the header file by checking enumarated type FUNCADD_TYPE.
    4th param: Number of arguments passed to function "foo".
    5th param: funcinfo
    */
    addfunc("foo", (rfunc)foo, 0, 2, 0);
    }

    Again, NETLIB includes a more complicated sample: funcadd.c .

  4. After creating dll, you can name it "amplfunc.dll" and copy it under the execution directory of AMPL.

  5. Lastly, you have to declare the function in your model before using it:

    ....
    function foo;
    ...
    subject to X: ... foo(param1, var1)...;