External Language Interfaces

To facilitate the development of other model language interfaces, a quite simple "eXternal Language Interface" (XLI) has been defined, which allows lp_solve to be dynamically configured (at run-time) to use alternative means to read and write the MIP model. lp_solve has several build-in interfaces to models: mps, lp, CPLEX lp. XLI allows implementing a custom reader and writer for other model layouts.
At present, a MathProg (AMPL subset) interface is in place, and the template is provided for other platforms. An XML-based interface is likely to come fairly soon also. Obviously, all existing interface methods will remain unchanged.
This feature again gives a unique flexibility and openness to the user and developer community that we have quite some expectations for.
If you write your own XLI, we would be glad if you would share your code with the lp_solve community. We are very interested in providing as many different XLIs as possible to the community.

Under Windows, an XLI is provided as DLL (xli_*.dll), under UNIX/LINUX as a dynamic linked library (libxli_*.so).
The lp_solve program provides a way to specify an XLI for reading a model and an XLI for writing the model. These can be different so that a model in one format can be converted to another format. It is still possible to both read and write the models with the build-in formats (mps, lp, CPLEX lp) and you can combine with the XLI.
With the lp_solve program, to set the XLI to read the model, use the option -rxli xliname filename [datafilename]. Via the API, use read_XLI. xliname is the name of the dynamic linked library. It is possible to provide a path to this name to be sure that the XLI from the specified location is used. If no path is given, then it depends on the OS where it will be searched.

Under Windows, the following search order is used:

  1. Current directory.
  2. A semi-colon-separated (;) list of directories in the user's PATH environment variable.

Under Unix/Linux, following search order is used:

  1. A colon-separated (:) list of directories in the user's LD_LIBRARY_PATH environment variable.
  2. The list of libraries specified in /etc/ld.so.cache (which is generated from /etc/ld.so.conf).
  3. /lib, followed by /usr/lib. Note the order here; this is the reverse of the order used by the old a.out loader. The old a.out loader, when loading a program, first searched /usr/lib, then /lib (see the man page ld.so(8)). This shouldn't normally matter, since a library should only be in one or the other directory (never both), and different libraries with the same name are a disaster waiting to happen.
filename is the name (with optional path) of the modelname. datafilename is optional and depends on the XLI implementation if needed/required/possible. For example for the MathProg XLI, this is a possible optional datafilename containing the data. Note that this name may not start with a minus sign (-)

Under Unix/Linux it is standard that a library has the lib prefix and a .so postfix. Under Windows there is no prefix and a .dll postfix.
To make the calling structure for XLIs uniform across different types of OS, lp_solve automatically adds the prefix and postfix if not provided. So the following commands are valid for both Windows and Unix/Linux:

lp_solve -rxli xli_MathProg input.mod
lp_solve -rxli ./xli_MathProg input.mod

The latter makes sure that the XLI is searched in the current directory, especially for Unix/Linux.

With the lp_solve program, to set the XLI to write the model, use the option -wxli xliname filename. Via the API, use set_XLI to set the XLI library and write_XLI to write the model. xliname is the name of the dynamic linked library. It is possible to provide a path to this name to be sure that the XLI from the specified location is used. If no path is given, then it depends on the OS where it will be searched.

Under Windows, the following search order is used:

  1. Current directory.
  2. A semi-colon-separated (;) list of directories in the user's PATH environment variable.

Under Unix/Linux, following search order is used:

  1. A colon-separated (:) list of directories in the user's LD_LIBRARY_PATH environment variable.
  2. The list of libraries specified in /etc/ld.so.cache (which is generated from /etc/ld.so.conf).
  3. /lib, followed by /usr/lib. Note the order here; this is the reverse of the order used by the old a.out loader. The old a.out loader, when loading a program, first searched /usr/lib, then /lib (see the man page ld.so(8)). This shouldn't normally matter, since a library should only be in one or the other directory (never both), and different libraries with the same name are a disaster waiting to happen.
filename is the name (with optional path) of the modelname.

Under Unix/Linux it is standard that a library has the lib prefix and a .so postfix. Under Windows there is no prefix and a .dll postfix.
To make the calling structure for XLIs uniform across different types of OS, lp_solve automatically adds the prefix and postfix if not provided. So the following commands are valid for both Windows and Unix/Linux:

lp_solve -wxli xli_MathProg output.mod
lp_solve -wxli ./xli_MathProg output.mod

The latter makes sure that the XLI is searched in the current directory, especially for Unix/Linux.

Creating an XLI

To create your own XLI, you have to create a DLL/shared library that implements the following routines:

MYBOOL XLI_CALLMODEL xli_name(void)

Return Value

xli_name must return a string describing the XLI library.

Parameters

None

MYBOOL XLI_CALLMODEL xli_readmodel(lprec *lp, char *model, char *data, char *options, int verbose);

Return Value

Must return TRUE or FALSE to indicate if successfull (TRUE) or not (FALSE).

Parameters

lp

An initial lp structure created via make_lp(0, 0).

model

Filename to read the model from.

data

Optional data to read data from. For example used by the MathProg XLI.

options

Extra options that can be used by the reader.

verbose

The verbose level. Can be used to output more or less information while creating the model.

Remarks

This routine must be used to create the model. Via the provided lp variable all lpsolve API routines can be accessed to create the model.
Note that xli_readmodel already provides an lp structure. You must use this one to build the model. The lp structure is created via make_lp(0, 0). So it contains 0 rows and 0 columns.

MYBOOL XLI_CALLMODEL xli_writemodel(lprec *lp, char *filename, char *options, MYBOOL results);

Return Value

Must return TRUE or FALSE to indicate if successfull (TRUE) or not (FALSE).

Parameters

lp

Pointer to previously created lp model. See return value of make_lp, read_lp, read_LP, read_lpt, read_LPT, read_mps, read_freemps, read_MPS, read_freeMPS, read_XLI

filename

Filename to write the model to.

options

Extra options that can be used by the writer.

results

...

Remarks

This routine must be used to write the model. Via the provided lp variable all lpsolve API routines can be accessed to write the model.

lp_XLI1.c must be included at the beginning of the source file. This to include an extra routine xli_compatible needed by the lpsolve library to check the compatibility.

If you want to create XLIs yourself, make sure that under Windows, you use 8 byte alignments. This is needed for the XLIs to work correctly with the general distribution of lp_solve and also to make sharing XLIs as uncomplicated as possible. If not, it will likely crash. It doesn't matter which calling convention is used to compile the library. The XLI_CALLMODEL directive makes sure that the calling convention of the needed routines is always ok independent of the calling convention specified in the project.
Also under windows, a definition file must be added. If this is not done, it will not work. The definition file should have extention .def and contain the following:

EXPORTS
  xli_compatible           @1
  xli_name                 @2
  xli_readmodel            @3
  xli_writemodel           @4

The definition file must be added to the project. How to do this depends on the version.
For Visual Studio 6: Project, Add to Project, Files, Files of type: Definition files (.def) and choose the created .def file
For Visual Studio .NET: Project, Properties, Linker, Input, Module Definition file. There you enter the name (with optional path) of the .def file.

XLI prototype
/* Generic include libraries */
#include <malloc.h>
#include <string.h>
#include "lp_lib.h"
#ifdef FORTIFY
# include "lp_fortify.h"
#endif

/* Include routines common to language interface implementations */

#include "lp_XLI1.c" 

char * XLI_CALLMODEL xli_name(void)
{
  return("XLI_xxx v1.0" );  /* return the name and version */ 
}

MYBOOL XLI_CALLMODEL xli_readmodel(lprec *lp, char *model, char *data, char *options, int verbose)
{
  MYBOOL status = FALSE;
  
  /* implement the code here to read the model */
  
  return(status); /* status says if the model could be read or not. TRUE is ok, FALSE is not ok */
}

MYBOOL XLI_CALLMODEL xli_writemodel(lprec *lp, char *filename, char *options, MYBOOL results)
{
  MYBOOL status = FALSE;

  /* implement the code here to write the model */

  return( status ); /* status says if the model could be written or not. TRUE is ok, FALSE is not ok */
}
Working example:
demo.c:
/*  Modularized external language interface module - w/interface for lp_solve v5.0+
   ----------------------------------------------------------------------------------
    Author:        Peter Notebaert
    Contact:       peno@mailme.org
    License terms: LGPL.

    Template used:
    Requires:

    Release notes:
    v1.0.0  28 June 2004        First implementation.

   ---------------------------------------------------------------------------------- */

/* Generic include libraries */
#include <malloc.h>
#include <string.h>
#include "lp_lib.h"

/* Include libraries for this language system */
#include <math.h>

#ifdef FORTIFY
# include "lp_fortify.h"
#endif

/* Include routines common to language interface implementations */
#include "lp_XLI1.c"

MYBOOL XLI_CALLMODEL xli_name(void)
{
  return( "xli_demo v1.0" );
}

MYBOOL XLI_CALLMODEL xli_readmodel(lprec *lp, char *model, char *data, char *options, int verbose)
{
  MYBOOL status = FALSE;
  REAL row[1+2]; /* must be 1 more then number of columns ! */

  lp->add_columnex(lp, 0, NULL, NULL); /* add empty column */
  lp->add_columnex(lp, 0, NULL, NULL); /* add empty column */
  lp->set_add_rowmode(lp, TRUE);
  row[1] = 1.0;
  row[2] = 2.0;
  lp->add_constraint(lp, row, GE, 3.0); /* constructs the row: +v_1 +2 v_2 >= 3 */
  lp->set_add_rowmode(lp, FALSE);
  status = TRUE;

  return(status);
}

MYBOOL XLI_CALLMODEL xli_writemodel(lprec *lp, char *filename, char *options, MYBOOL results)
{
  return(lp->write_lp(lp, filename));
}
demo.def:
EXPORTS
  xli_compatible           @1
  xli_name                 @2
  xli_readmodel            @3
  xli_writemodel           @4

This example is not very practical. It is only usefull for demonstration purposes. Let's assume that the name of this library is xli_demo.
xli_readmodel creates the model. It doesn't even read from a file in this example. xli_writemodel uses write_lp to write the model to filename.

This XLI can be called from lp_solve via the following syntax:

lp_solve -rxli xli_demo "" -wxli xli_demo output.txt

An empty ("") filename is provided because it isn't used by this demo. This command will create a file output.txt with contents:

/* Objective function */
min: ;

/* Constraints */
+C1 +2 C2 >= 3;

/* Variable bounds */

Because we didn't provide the option -parse_only, the model is also solved and the result is shown on screen:

Value of objective function: 0

Actual values of the variables:
C1                              3
C2                              0