OPL Language

OPL language: the battle of array declarations
The OPL Development Studio, created by ILOG (and recently acquired by IBM), provides tools based on the Optimization Programming Language. This tool intends to simplify the process of feeding data and model formulae to other ILOG optimization tools for mathematical programming and constraint programming.

Experience has been proven OPL to be extremely helpful as a compact and high level language for both data and model. Nevertheless, this language still reveals some constructs that are not well understood nor well documented.

For example, there are many pitfalls a novice developer will face on OPL while working with arrays. Here, and on subsequente articles, I will share some advices that would have been useful while I learned OPL.

What arrays are

An OPL array is a list of elements that are identified by an index. OPL is very strict for an array declaration:

o The index must be a element of discrete data type. Even more, those type must be the same for all indexes of the array.
o An array stores values of any type. Again, those type must be the same for all values of the array.
o All values that are possible as index must be enumerated at the array declaration. Of course, all those index values have to be of the same data type.
o This enumeration implies that, for every index value, there must be an element in the array. Than means that no position in the array may be left “empty”.
o Furthermore, the order the index values were enumerated determines the order that array elements are transversed.

Because of these restrictions on OPL arrays, they are not just a listing of elements, but may be understood an associative map that where each exactly index value has a relationship to exactly one element value.

An OPL array may also be seen as a discrete function array(index) =>element. I personally like to call this index value enumeration as domain of the array and the stored elements as image of the array.

How an array is declared with ranges
The simplest array declaration defines the domain as a range of consecutive integer values. The example associates associates the respective square for the integer numbers from 1 to 4:
int a[1..4] = [1, 4, 9, 16];

Observe that the declaration contains the domain (the range 1..4, all consecutive integer from 1 to 4: 1, 2, 3 and 4). The declaration also defines the image: 1, 4, 9, 16. Both domain and image are ordered sets that define a relation, meaning that a[1]=>1, a[2]=>4, a[3]=>9 and a[4]=>16.

The image could also be read from a data file:
int a[1..4] = …;assuming there is a text file that contains a line as: a = [1, 4, 9, 16];

How an array is declared with formula
The image does not need to be expressed as a list. Formula is also allowed.
int a[x in 1..4] = x*x;

Observe that the declaration still presents the domain (1..4) and the image (x*x). The formula is automatically evaluated for each value from the domain.

How an array is declared with ordered sets
Alternatively, the declaration may define the domain as an ordered set or primitive values (sequence of possibly non consecutive values). The example associates the respective squares for three arbitrary integer numbers:
int a[{1, 3, 6}] = [1, 9, 36];

A index of string data type must be declared as a set as there is no concept of “range of strings”. The example shows a function that associates a uppercase letter for each lower case letter.
int a[{«a», «b», «c», «d»}] = [«A», «B», «C», «D»];

How an array is declared with ordered sets of tuples
Since tuples are also discrete and unique (according to OPL convention), they may be used as indices for arrays. Again, one is required to declare a set of tuples as the domain for the index.
int a[{, , }] = [3, 6, 9];

In this example, the domain is composed of a set of pairs of numbers. Each pair is associated to the sum of the numbers from the pair.

OPL and Java: loading dynamic Linux libraries
When calling IBM ILOG OPL (Optimization Programming Language) from a Java application running on Linux, one will face some issues regarding loading dynamic OPL libraries. Typical error messages look like:
Native code library failed to load: ensure the appropriate library (oplXXX.dll/.so) is in your path.
java.lang.UnsatisfiedLinkError: no opl63 in java.library.path
java.lang.UnsatisfiedLinkError: no opl_lang_wrap_cpp in java.library.path
java.lang.UnsatisfiedLinkError: no cp_wrap_cpp_java63 in java.library.path
java.lang.UnsatisfiedLinkError: no concert_wrap_cpp_java63 in java.library.path

This article explains my considerations and some approaches how to fix it.

According to the OPL Java Interface documentation, granting access to the OPL should be as simple as:
this.oplFactory = new IloOplFactory();
this.errorHandler = oplFactory.createOplErrorHandler();
this.settings = oplFactory.createOplSettings(this.errorHandler);

However, at the first time Java reaches a reference to any class that provides OPL, it will try to load all C-compiled dynamic libraries that implement the OPL interface. Under linux, this library is called oplXXX.so (where XXX is the OPL version, eg. 63 for 6.3) and usually found as a file ./bin/YYY/liboplXXX.so from the OPL installation directory (where YYY is the name of your operating system and machine architecture).

The easiest way to assure that Java finds the OPL library is passing its path on the java command line with the -Djava.library.path JVM parameter:
java -Djava.library.path=/opt/ilog/opl63/bin/x86-64_debian4.0_4.1 -jar OptApplication.jar

On other ILOG products, I used to write code that forces loading the library to avoid requiring the user to care about the -Djava.library.path JVM parameter.
try { // (does not work)
System.load(«/opt/ilog/opl63/bin/x86-64_debian4.0_4.1/libopl63.so»);
} catch (UnsatisfiedLinkError e) {
throw new OplLibraryNotFoundException(e);
}

Unfortunately, there is a hidden trap: the oplXXX.so itself has binary dependencies to other ILOG libraries. Both approaches (System.load and JVM parameter) will fail with an error message like:
java.lang.UnsatisfiedLinkError: /opt/ilog/opl63/bin/x86-64_debian4.0_4.1/libopl63.so: libdbkernel.so: cannot open shared object file: No such file or directory
java.lang.UnsatisfiedLinkError: no opl_lang_wrap_cpp in java.library.path
java.lang.UnsatisfiedLinkError: no cp_wrap_cpp_java63 in java.library.path
java.lang.UnsatisfiedLinkError: no concert_wrap_cpp_java63 in java.library.path

All required dependecies are, according to ldd:
libdbkernel.so, libdblnkdyn.so, libilog.so, libcplex121.so

One solution would be to load all the libraries in reverse order before referencing any OPL class:
try { // (does not work)
System.load(«/opt/ilog/opl63/bin/x86-64_debian4.0_4.1/libilog.so»);
System.load(«/opt/ilog/opl63/bin/x86-64_debian4.0_4.1/libdbkernel.so»);
System.load(«/opt/ilog/opl63/bin/x86-64_debian4.0_4.1/libdblnkdyn.so»);
System.load(«/opt/ilog/opl63/bin/x86-64_debian4.0_4.1/libcplex121.so»);
System.load(«/opt/ilog/opl63/bin/x86-64_debian4.0_4.1/libopl63.so»);
} catch (UnsatisfiedLinkError e) {
throw new OplLibraryNotFoundException(e);
}

Unfortunately, not all binary library dependencies conform to JNI and it is not possible to force pre-loading them.

It happens that the JVM, in order to load libopl63.so, passes control to LD GNU Linker, which is in charge to load libopl63.so and all its dependencies. The LD is a component of Linux and runs under the scope of the operating system. It is completely unaware of the JVM that called it. Therefore, it has no knowledge of the JVM configuration nor class loading policies. It will not look within paths listed by the -Djava.library.path JVM parameter. Instead, it was programmed to look for paths listed in LD_LIBRARY_PATH.

I agree that this is really odd. I checked thoroughly reference manuals/documentation and talked to experienced Linux system administrators. There is really nothing one can do with Java coding or configuring to fix this issue. The only solution is configuring the LD_LIBRARY_PATH environment variable to instruct LD where to locate additional OPL libraries. In order to call ones application, a redundant command line is required as:
LD_LIBRARY_PATH=/opt/ilog/opl63/bin/x86-64_debian4.0_4.1 java -Djava.library.path=/opt/ilog/opl63/bin/x86-64_debian4.0_4.1 -jar OptApplication.jar

Even worse, one needs to set LD_LIBRARY_PATH on each Java invocation. Editing bash_profile.sh or .bashrc is of little use, since most setuid’ tools (as bash or gdm that starts your graphical interface) do reset LD_LIBRARY_PATH for security reasons. And you practically all log-in access relies on a setuid’ application, LD_LIBRARY_PATH will always be reseted. Sphere: Related Content

Deja un comentario

Tu dirección de correo electrónico no será publicada.