JOM (Java Optimization Modeler)

JOM Expressions syntax

The syntax of JOM Expressions is quite similar to that of MATLAB or Octave languages, and the manner in which they handle matrices and multidimensional arrays. For those familiar with MATLAB or Octave, the main point to remember is that JOM uses 0-indexing for all the arrays (of variables or input parameters). Then, the first element in any coordinate of an array has the index 0 and not the index 1. E.g. in an 5x5x5 array the first element is (0,0,0), and the last element is (4,4,4).

In the next subsections we will try to cover the key concepts within JOM Expressions: scalar constants, ...

1. Numerical constants

Numerical constants are of course valid JOM expressions themselves, and take part of other expressions. The following code uses the parseExpression method to create an Expression object with an scalar constant, and then prints it.


Expression e = new OptimizationProblem ().parseExpression("1.34");

System.out.println("Value of e: " + e.evaluate().get(0)); // e is an scalar expression, I take its first cell which is a double

 

2. 2-D Matrix constants

Two dimensional matrices, composed of constants or of other expressions, can be created in JOM, using a slightly different syntax than MATLAB. As in MATLAB, matrices start with the [ character and end with the ] character. The difference is that in JOM we use the character ; for separating elements in the same row, and ;; for separating rows among them. As an example, the matrix:

\( c = \left( \begin{array}{cc} 7 & 1 & 4 \\ 5 & 2 & 6 \end{array} \right) \)

Can be defined as a JOM Expression with the code:


Expression c = new OptimizationProblem ().parseExpression("[7 ; 1 ; 4 ;; 5 ; 2 ; 6]");

System.out.println("Value of c: " + c.evaluate()); 

 

3. Accessing the elements in an array: indexing

Given an array of decision variables, or an input parameter, it is possible to access individual cells of the array in two different forms: (i) providing the coordinates or subindexes of the cell (recall the 0-indexing!!), and (ii) directly specifying the so-called linear index of the cell (again first cell of an array has index 0, not 1). JOM syntax and behaviour in this, is very much similar to MATLAB and Octave.

Let c be the same input parameter as in previous section. The following code illustrate some examples on how to access a cell from its coordinates:


System.out.println ("c(0,2) = " + op.parseExpression("c(0,2)").evaluate()); // this prints number 4

System.out.println ("c(1,2) = " + op.parseExpression("c(1,2)").evaluate()); // this prints number 6

System.out.println ("c(0,0) = " + op.parseExpression("c(0,0)").evaluate()); // this prints number 7

As in MATLAB and Octave, each cell in an array has a linear index associated. In JOM , first index is index 0 (while in MATLAB start in 1). As in MATLAB, indexes grow first filling the first dimension, then the second, then the third.... For instance, in a 2x3x2 array x, the indexes are:

Coordinates Linear index
x(0,0,0) x(0)
x(1,0,0) x(1)
x(0,1,0) x(2)
x(1,1,0) x(3)
x(0,2,0) x(4)
x(1,2,0) x(5)
x(0,0,1) x(6)
... ...
x(1,2,1) x(11)
It is possible to access the cells using these linear indexes. See these examples:

System.out.println ("c(0) = " + op.parseExpression("c(0)").evaluate()); // this prints number 7

System.out.println ("c(3) = " + op.parseExpression("c(3)").evaluate()); // this prints number 2

System.out.println ("c(6) = " + op.parseExpression("c(6)").evaluate()); // error index out of bounds

 

3. Accessing ranges of elements in an array: subarrays

Given an array (of decision variables or an input parameter), it is possible to access ranges of cells (subarrays), in a very similar manner as MATLAB makes (see this page for details on the MATLAB syntax). Let's continue with the c input parameter before. There are two manners of creating subarrays:

  • Providing one array of linear indexes. For instance c([1 ; 0 ; 5]) returns a 1x3 vector with three elements of c, with the given indexes. The clausule "all" means all the indexes of the array. c(all) is the same as c. Ranges of indexes can be defined with the operator :, as in MATLAB. For instance, c(1:4) is the same as c([1; 2 ; 3 ;4]).
  • 
    System.out.println ("c([1 ; 4 ; 1]) = " + op.parseExpression("c([1 ; 4 ; 1])").evaluate()); // returns [5 ; 4 ; 5]
    
    System.out.println ("c([1 ;; 4 ;; 1]) = " + op.parseExpression("c([1 ;; 4 ;; 1])").evaluate()); // returns [5 ;; 4 ;; 5]
    
    System.out.println ("c([0 ; 2 ;; 1 ; 2]) = " + op.parseExpression("c([0 ; 2 ;; 1 ; 2])").evaluate()); // returns [7 ; 1 ;; 5 ; 1]
    
    System.out.println ("c(2:4) = " + op.parseExpression("c(2:4)").evaluate()); // returns [1 ; 2 ; 4]
    
    System.out.println ("c(all) = " + op.parseExpression("c([all])").evaluate()); // returns [7 ; 5 ; 1 ; 2 ; 4 ; 6]
    
    
  • Providing arrays of subindices, one for each coordinate. For instance c ([0 ; 0 ] , [1 ; 2]) returns an array of size 2x2, with the first row of c repeated twice, but with only the last two columns. In a coordinate, the clausule "all" is equivalent to all the subindexes in that coordinate. For instance, c(1,all) is the same as c(1,[0 ; 1 ; 2]). The operator : can be used also here to define ranges. c(0 , 0:2) is equivalent to c(0 , [0 ; 1 ; 2])
  • 
    System.out.println ("c(1 , all) = " + op.parseExpression("c(1,all)").evaluate()); // returns [5 ; 2 ; 6]
    
    System.out.println ("c(0:1 , 0:1) = " + op.parseExpression("c(0:1,0:1)").evaluate()); // returns [7 ; 1 ;; 5 ; 2]
    
    System.out.println ("c([0 ; 1 ; 0] , [1 ; 1 ; 2]) = " + op.parseExpression("c([0 ; 1 ; 0] , [1 ; 1 ; 2])").evaluate()); // returns [7;1;4 ;; 2;2;6 ;; 1;1;4]
    
    

As a final remark, we note the differences between MATLAB and JOM syntax when handling indexing of arrays:

  • JOM uses 0-indexing, and MATLAB 1-indexing
  • Both in MATLAB and JOM you can use the operator : to specify ranges. For instance 0:3 is equal to the expression [0 ; 1 ; 2 ; 3]. However, MATLAB permits using the operator : twice like in 0:2:8, which means "the numbers from 0 to 8, in steps of 2". In JOM it is not possible to define the step, and the previous expression raises an error.
  • In MATLAB, using : as the single index in an coordinate means "all the elements in this coordinate". The same meaning exists in JOM using the reserved word all.
  • The end tag in MATLAB is a reference to the index of the last element in a dimension. JOM has no such end tag.

 

 

4. Operators

Operators combine one or more expressions, producing another expression. The JOM parser applies the operators following a set priorities (for instance, product of expressions is evaluated before the sum). These priorities are the usual ones (similar as in MATLAB and "all" programming languages), and can be modified using parenthesis in the typical form (i.e. x + y * 5 is equivalent to x + (y * 5), if we want to apply the sum first we have to write (x+y)*5).

Internally, JOM computes for each expression (i) which are the decision variables involved, and thus knows if the expression is a constant (depends on no decision variable), and (ii) if it is linear with respet to the decision variables. In this latter case the expression is stored internally in a compact numerical format. Tracking this information is needed to effectively handle the interface with the solvers.

The following table summarizes the operators in JOM and their main information. In our examples, c is the same 2x3 matrix as the one used above, and x is a decision variable of the same size as c.

Name Symbol Example Comments
Plus + x + y

If x and y are of the same size, produces a new array of that size, summing element by element.
If one of the two summands is a scalar (an array of one cell, constant or not), then x+y has the size of the array where the i-th cell of the sum equals the i-th cell of the array, plus the scalar.

Minus - x - y

If x and y are of the same size, produces a new array of that size, substracting each element in x by the corresponding element in y.
If x or y is a scalar (an array of one cell, constant or not), then x-y has the size of the array and the scalar is substracted to/from the each element of the array.

Matrix multiplication * x * y

When both x and y are 2D matrices, it performs the standard matrix-by-multiplication. One dimension can be singleton, producing a row or column matrix. If x or y are scalar, the operation is the classical scalar by matrix multiplication.
Since version 0.1.6, y can have more than 2 dimensions (3, 4, ...). In this case, x is matrix-multiplied with the block matrices of the first two dimensions of y:

\(
x \in \mathbb R^{N_1 \times N}, y \in \mathbb R^{N \times N_2 \times N_3 \times \ldots N_n} \text{ then } z = x \text{ * } y \in \mathbb R^{N_1 \times N_2 \times N_3 \ldots \times N_n}
\)

where:

\(
z(i_1,i_2,\ldots,i_n) = \sum_{s=0}^{N-1}x (i_1,s) y(s,i_2,i_3,\ldots,i_n)
\)

Note that this is an extension of the standard behavior with 2D matrices.
Element-by-element multiply .* x .* y

If x and y are of the same size, produces a new array of that size, multiplying element by element.
If one of the two summands is a scalar (an array of one cell, constant or not), then x*y has the size of the array where the i-th cell of the product equals the i-th cell of the array, multiplied by the scalar.

Element-by-element division / or ./ x ./ y

If x and y are of the same size, produces a new array of that size, dividing each element in x by the corresponding element in y.
If x or y is a scalar (an array of one cell, constant or not), then x ./ y has the size of the array and the scalar is substracted to/from the each element of the array.

Change sign - -x

Equal to 0 - x.

Power ^ or .^ x ^ y

If x and y are of the same size, produces a new array of that size, where each cell i equals to \(x_i^{y_i} \).
If x or y is a scalar (an array of one cell, constant or not), then x .^ y has the size of the array and the scalar is substracted used in the base/exponent for each element of the array.

Transpose ' x'

Calculates the matrix transponse. If x is not a matrix (an array of two dimensions) it raises an error.

5. Functions

Similarly to operators, functions combine one or more expressions, producing another expression. Functions must be derivable. JOM is able to automatically compute the first derivative (jacobian matrix) for each expression. This is needed to autonomously interact with non-linear solvers like IPOPT.

The following table summarizes the built-in functions in JOM and their main information. The amount if functions in JOM is expected to keep growing in future releases.

Name Example Comments
Summation sum sum(x) , sum(x , dim)

Takes the array x, sums along its dimension dim (first dimension is dim = 1). For instance if x is of size 2x3x4x5, then sum(x,3) is an array of size 2x3x5, so that \( \left( \text{sum(x,3)} \right) _{ijm} = \sum_{k} x_{ijkm} \)
If the dimension dim is not specified, then sum(x) is a 1x1 matrix summing all elements in x. In the previous example: \( \left( \text{sum(x)} \right) = \sum_{ijkm} x_{ijkm} \) Note that this is a difference with respect to the sum function in MATLAB. In MATLAB sum(x) would sum along the first non-singleton dimension (first dimension with a size larger than one, in our example dimension 1).

Square root sqrt sqrt(x)

sqrt(x) is an array of the same size as x, where each element in sqrt(x) is the square root of the corresponding element in x. If an element is negative, an exception is raised.

Natural logarithm ln ln(x)

ln(x) is an array of the same size as x, where each element in ln(x) is the natural logarithm of the corresponding element in x. If an element is negative or zero, an exception is raised.

Exponential exp exp(x)

exp(x) is an array of the same size as x, where each element in exp(x) is the exponential ( \( e^{x_i} \) ) of the corresponding element in x.

Sine sin sin(x)

sin(x) is an array of the same size as x, where each element in sin(x) is the trigonometric sine of the corresponding element in x (assumed in radians).

Cosine cos cos(x)

cos(x) is an array of the same size as x, where each element in cos(x) is the trigonometric cosine of the corresponding element in x (assumed in radians).

Tangent tan tan(x)

tan(x) is an array of the same size as x, where each element in tan(x) is the trigonometric tangent of the corresponding element in x (assumed in radians).

Arcsine asin asin(x)

asin(x) is an array of the same size as x, where each element in asin(x) is the trigonometric arcsine (in radians, in the range \( -\frac{\pi}{2} \ldots \frac{\pi}{2} \)) of the corresponding element in x.

Arccosine acos acos(x)

acos(x) is an array of the same size as x, where each element in acos(x) is the trigonometric arccosine (in radians, in the range \( 0 \ldots \pi \)) of the corresponding element in x.

Arctangent atan atan(x)

atan(x) is an array of the same size as x, where each element in atan(x) is the trigonometric arcsine (in radians, in the range \( -\frac{\pi}{2} \ldots \frac{\pi}{2} \)) of the corresponding element in x.

Erlang-B blocking erlangB erlangB(load,cap)

Computes the Erlang-B function taking as load parameter load and as capacity parameter cap. Both parameters should be arrays of the same size. If one parameter is an array and the other is an scalar (an array with one cell), then the function operates as if the scalar was an array of the same size of the other parameter, with all its elements equal. For each element i of load and cap, the Erlang-B function computes the blocking probability of a link of capacity \( c = \text{cap}_i \) units, that is offered a traffic of load \( x = \text{load}_i \). This is given by:
\( \text{erlangB} (x,c) = \frac{x^c / c!}{\sum_{k=0}^{c} x^k / k!} \)
The capacity should be an integer greater or equal to 1, and the load should be a non-negative real. Otherwise an expression is raised. While the load can be an expression depending on the decision variables of the problem, the capacities must be a constant expression. This is because, since capacities must be integers, the Erlang-B function has no derivativwe with respect to the capacities.

Create a diagonal matrix diag diag(v)

Receives a row or column vector v and returns an square diagonal matrix, where the i-th element of the diagonal contains the i-th element of vector v

Create a matrix of ones ones ones(s)

Receives a row or column vector s with the size of the array, and returns an array of that size full of ones

Create a matrix of zeros zeros zeros(s)

Receives a row or column vector s with the size of the array, and returns an array of that size full of zeros

Create a matrix with ones in the diagonal eye eye(r) , eye(r,c)

Receives the number of rows r and columns c, and returns a matrix of that size, ones in the diagonal and zeros elsewhere. eye(r) returns a square identity matrix of r rows and columns

Permute the dimensions of an array permute permute(x,r)

Receives an array x of N > 1 dimensions, and a row or column vector r of N elements. It produces an array of N dimensions, where dimensions of x are permuted, so that dimension i of the output array, is the dimension N(i) of the orginal array. For instance, if x is an array of size 2 x 3 x 5, then y = permute (x , [2;3;1]) is an array of size 3 x 5 x 2, where y(i,j,k) = x(j,k,i).