Function Objects

Example 1: Transformation

The following examples will often use prefabricated function objects from the library cern.jet.math.tdouble.DoubleFunctions But you need not yet know all about that library, only that it exists. Let's stay focused and browse through the examples.


Frequently Used Scaling

Operation Method Comment
elementwise scaling assign(f) where f is one of {F.mult(a),F.div(a)} x[i] = x[i] {*,/} a
x[i,j] = x[i,j] {*,/} a
elementwise scaling assign(y,f) where f is one of {F.plus,F.minus, F.mult,F.div, F.plusMult(a),F.minusMult(a)} x[i] = x[i] {+,-,*,/} y[i]
x[i] = x[i] {+,-} y[i] {*,/} a

x[i,j] = x[i,j] {+,-,*,/} y[i,j]
x[i,j] = x[i,j] {+,-} y[i,j] {*,/} a

Usually, assign operations are heavily optimized for function objects implementing frequently used numerical scaling like plus,minus,mult,div,plusMult,minusMult, etc. Here are idioms that make numerical codes efficient:

cern.jet.math.Functions F = cern.jet.math.Functions.functions; // naming shortcut (alias) saves some keystrokes:

double a = 2;
// x and y are 1,2 or 3-d matrices

x.assign(F.mult(a));           // x[i] = x[i] * a
x.assign(F.div(a));            // x[i] = x[i] / a

x.assign(F.plus(a));           // x[i] = x[i] + a
x.assign(F.minus(a));          // x[i] = x[i] - a


x.assign(y, F.mult);           // x[i] = x[i] * y[i]
x.assign(y, F.div);            // x[i] = x[i] / y[i]

x.assign(y, F.plus);           // x[i] = x[i] + y[i]
x.assign(y, F.minus);          // x[i] = x[i] - y[i]


x.assign(y, F.plusMult(a));    // x[i] = x[i] + y[i]*a

x.assign(y, F.plusMult(a));    // x[i,j] = x[i,j] + y[i,j]*a
x.assign(y, F.minusMult(1/a)); // x[i,j] = x[i,j] - y[i,j]/a

Try the examples also on 2-d or 3-d matrices. They work without changes regardless of dimensionality.


Transformation over one matrix

To prepare with, let's construct a 1-d matrix:

double[] v1 = {0, 1, 2, 3}; 
DoubleMatrix1D x = new DenseDoubleMatrix1D(v1);

Using a mult function object, we multiply the matrix with a scalar c

// x[i] = x[i] * c
double c = 2;
x.assign(cern.jet.math.Functions.mult(c)); System.out.println(x); --> 0 2 4 6

It would be equivalent but more clumsy to write

x.assign( 
   new DoubleFunction() {
      public final double apply(double a) { return a*c); } 
   }
); 

Similarly, the sin function object is used to transform the matrix to hold in each cell the sine of the former corresponding cell value:

// set each cell to its sine
System.out.println(x.assign(cern.jet.math.Functions.sin)); // set each cell to random state uniform in (0,1)
x.assign(cern.jet.math.Functions.random()));
--> 0.002489 0.793068 0.620307 0.35774
// set each cell to random state uniform in (0,1)
System.out.println(x.assign(cern.jet.math.Functions.random()));
--> 0.002489 0.793068 0.620307 0.35774
// set each cell to random state uniform in (-0.5, 0.5)
int seed = 12345;
System.out.println(x.assign(new cern.jet.random.Uniform(-0.5, 0.5, seed)));
--> 0.31733 0.499061 0.010354 -0.368467 // set each cell to random state from Poisson distribution with mean=2
System.out.println(x.assign(new cern.jet.random.Poisson(2, cern.jet.random.Poisson.makeDefaultGenerator()))); --> 9 6 2 2

Transformation over two matrices

To prepare with, let's construct two 1-d matrices:

double[] v1 = {0, 1, 2, 3}; 
double[] v2 = {0, 2, 4, 6}; DoubleMatrix1D x = new DenseDoubleMatrix1D(v1); DoubleMatrix1D y = new DenseDoubleMatrix1D(v2);

x = xy <==> x[i] = x[i]y[i] for all i

A prefabricated pow function object is used to compute the power transformation:

// x[i] = Math.pow(x[i], y[i])
System.out.println(x.assign(y, cern.jet.math.Functions.pow));
--> 1 1 16 729

A prefabricated mult function does something similar:

// x[i] = x[i] * y[i]
System.out.println(x.assign(y, cern.jet.math.Functions.mult)); --> 0 2 8 18

The naming shortcut (alias) saves some keystrokes:

cern.jet.math.Functions F = cern.jet.math.Functions.functions;

Chaining function objects yields more complex functions:

// x[i] = x[i] * y[i] * 3
System.out.println(x.assign(y, F.chain(F.mult,F.mult(3)))); --> 0 6 24 54

More complex transformation functions need to be written by hand:

m1.assign(m2,
   new DoubleDoubleFunction() {
      public double apply(double a, double b) { return Math.PI*Math.log(a-5)*Math.pow(a,b); }
   }
);

If we want to generate a third matrix holding the result of the power transformation, and leave both source matrices unaffected, we make a copy first and then apply the transformation on the copy:

// z[i] = Math.pow(x[i],y[i])
DoubleMatrix2D z = x.copy().assign(y, F.pow); System.out.println(z);