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:

  • The index must be a element of discrete data type. Even more, those type must be the same for all indexes of the array.
  • An array stores values of any type. Again, those type must be the same for all values of the array.
  • 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.
  • 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”.
  • 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[{<1,2>, <3,3>, <4,5>}] = [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.