swIMPSWIG-based Interfaces for Mathematical Programming |
hosted at |
swIMP (SWIG-based Interfaces for Mathematical Programming) aims at making solvers written in C or C++ available for other languages. The current focus is on Java as target language and on LP/IP-solvers which are compatible to the Open Solver Interface (OSI) from the Coin-OR-project. This includes both Open Source solvers as well as reknown commercial solvers like CPLEX and Xpress (see Features).
The wrappers are implemented using the generator tool SWIG. SWIG generates both C++ code which is then compiled into a shared library and Java-code which accesses the shared library through the Java Native Interface (JNI). In principle, SWIG also supports other target languages like Python or Perl but this would require non-trivial modifications of the SWIG interface definitions currently provided by swIMP.
The swIMP project was started as a subproject of Combean. Combean is a Java framework for mathematical structures and optimization algorithms. Among other features it contains a convenient framework for constructing complex linear programming models. If you are going to use swIMP, you might thus also want to consider combining it with Combean.
Keywords: Java, Linear Programming, LP, Integer Programming, IP, Operations Research, OR, OSI, Coin, Clp, Symphony, CPLEX, Xpress, GLPK, MOSEK, SoPlex, SWIG, JNI, Open Source.
The following solvers are currently supported:
Support for the following solvers should be easy to add (at most one day of work or so) but has not yet been added because I had no need to - a short notice if you need such a feature or even better a patch for including one of these solvers would of course be highly welcome:
swIMP is available for download at the Sourceforge project page.
swIMP is in beta-state and has been tested with a quite comprehensive test suite. We would be very much interested if you encounter any bugs are difficulties with the API as we would like to ensure that the quality is optimum before declaring production readiness.
The usual
./configure make cleangen # can be skipped, if all supported solvers are available. see below. make make check # (optional) make installshall suffice to build the shared library. You may want to give some arguments to configure, e.g.
./configure --prefix=/install/path \ CPPFLAGS="-I/path/to/coin/include" LDFLAGS="-L/path/to/coin/lib"
Please make sure that the path where the shared library is installed is accessible by the dynamic linker. Under Linux this means that the path either has to be contained in /etc/ld.so.conf (remember to run ldconfig after changing the setting) or in the environment variable LD_LIBRARY_PATH.
After the build the jar-file swimp.jar is located in the root build directory. You may want to put it to a more convenient place where you store other jars (like /usr/local/share/java) and include it in your CLASSPATH.
The ./configure script of swIMP tries to find out which LP solvers are installed at your site. The is done by checking whether header files of the given solvers are available for compilation. The following header files are used for this check:
| Solver | Corresponding header file |
|---|---|
| Cbc | CbcBranchCut.hpp |
| Clp | ClpSolve.hpp |
| SYMPHONY | cg_params.h (for technical reasons symphony_api.h does not work) |
| Vol | VolVolume.hpp |
| GLPK | glpk.h |
| MOSEK | mosek.h |
If a header is found, the wrapper classes (both C++ and Java) for the corresponding solver are generated, otherwise the unavailable solver is excluded from the build. Please examine the output of ./configure to find out whether all the solver that you expect are indeed accessible during the build. For example the output
checking VolVolume.hpp usability... yes checking VolVolume.hpp presence... yes checking for VolVolume.hpp... yes checking cg_params.h usability... no checking cg_params.h presence... nomeans that the Vol-solver has been found but the SYMPHONY solver is not available. Please note that ./configure only checks the availability of the header file and not of the library of the solver. If the library is not accessible (e.g. due to a wrong LDFLAGS-setting), you will get an error during linking.
Currently, swIMP is distributed with a version of the generated C++ and Java classes which contains wrappers for all supported solvers. If this works for you, you do not need to invoke 'make cleangen' and in particular you will not need to install SWIG at your site. If not all solver are available, however, you have to regenerate the wrappers by running 'make cleangen' first. Of course, this requires SWIG.
Alternatively, you can patch the file iface/coin_wrap.cxx. This is fairly easy. The only thing that has to be done is to comment out the JNI wrapper functions for the unavailable solver. In the future this shall be achieved by including suitable #ifdef's in coin_wrap.cxx but unfortunately I do not see a way to achieve this with SWIG directly and writing some kind of awk postprocessor or so looks like a quite ugly solution.
When the shared library is available through the dynamic linker, you can execute the (by now rather small, I admit) automatic test suite. This is done by executing
make testand test results will be written in the subdirectory testrep. The difference between 'make check' and 'make test' is that make test will execute the tests using your environment, whereas 'make check' uses the local copy of the library before installation. Thus, 'make test' checks your installation and not just the correctness of the build.
If you get an error message like
Testsuite: net.sourceforge.swimp.test.coin.TestCoinShallowPackedVectorBase
Tests run: 1, Failures: 0, Errors: 1, Time elapsed: 0,013 sec
Testcase: testMethodsOfDerivedClasses took 0,008 sec
Caused an ERROR
no swIMP in java.library.path
java.lang.UnsatisfiedLinkError: no swIMP in java.library.path
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1517)
at java.lang.Runtime.loadLibrary0(Runtime.java:788)
at java.lang.System.loadLibrary(System.java:834)
at net.sourceforge.swimp.coin.coinJNI.(coinJNI.java:44)
at net.sourceforge.swimp.coin.IntArray.(IntArray.java:41)
at net.sourceforge.swimp.coin.coin.jarrayToSwig(coin.java:39)
at net.sourceforge.swimp.test.coin.TestCoinShallowPackedVectorBase.setUp
(TestCoinShallowPackedVectorBase.java:53)
you should check your dynamic linker settings (i.e. your LD_LIBRARY_PATH on a Linux system).
Since version 0.9 swIMP uses a hand-tailored SWIG-mapping that hands Java-array of type int[] or double[] directly down to the C++ library layer. There is no copying of data involved. Measurements show that the overhead for using a Java-array in comparison to using a SWIG-generated wrapper for a C++-array (basically a representation of the pointer to the C++-array as a Java-long) is negligible.
Caveat: This mapping is based on the assumption that the data types jint and jdouble, used by JNI (Java Native Interface) and the native C++ data types int and double are identical. This is the case for the environment where we develop swIMP and we hope that this will be the case for most swIMP users. swIMP will stop the build process with an error if this assumption is not justified. In that case, please report this to the Sourceforge support tracker. If we see that there is need for that in the swIMP-user community, we will add an alternative mapping, which requires a bit more computational overhead but which will work without any assumptions on the structure of the data types jint and jdouble.
We have also compared the performance of swIMP-generated code with native C++-code. You can run our simple performance test suite by using the command make perftest. This show the times for calls to the methods setVector and setConstant of CoinPackedVector in the following three cases:
[exec] setVector(int, int*, double*): size=1000000, numVals=30000: 1060 (ms)
[exec] setConstant(int, int*, double): size=1000000: 1040 (ms)
[java] CoinPackedVector.setVector(Java): size=1000000, numVals = 30000:
[java] count = 1 total = 1379 (ms) average = 1379.0 (ms)
[java] CoinPackedVector.setVector(SWIG): size=1000000, numVals = 30000:
[java] count = 1 total = 1333 (ms) average = 1333.0 (ms)
[java] CoinPackedVector.setConstant(Java): size=1000000:
[java] count = 1 total = 1326 (ms) average = 1326.0 (ms)
[java] CoinPackedVector.setConstant(SWIG): size=1000000:
[java] count = 1 total = 1280 (ms) average = 1280.0 (ms)