Difference between revisions of "Equations"

From Efficient Java Matrix Library
Jump to navigation Jump to search
(Created page with "Writing succinct and readable linear algebra code in Java, using any library, is problematic. Originally EJML just offered two API's for performing linear algebra. A procedu...")
 
 
(3 intermediate revisions by the same user not shown)
Line 5: Line 5:
 
This is achieved by parsing text strings with equations and converting it into a set of executable instructions, see the usage example below:
 
This is achieved by parsing text strings with equations and converting it into a set of executable instructions, see the usage example below:
  
<pre>
+
<syntaxhighlight lang="java">
 
eq.process("K = P*H'*inv( H*P*H' + R )");
 
eq.process("K = P*H'*inv( H*P*H' + R )");
</pre>
+
</syntaxhighlight>
  
 
It is easy to see that the equations code is the compact and readable.  While the syntax is heavily inspired by Matlab and its kin, it does not attempt to replicate their functionality.  It is also not a replacement for SimpleMatrix or the procedural API.  There are situations where those other interfaces are easier to use and most programs would need to use a mix.
 
It is easy to see that the equations code is the compact and readable.  While the syntax is heavily inspired by Matlab and its kin, it does not attempt to replicate their functionality.  It is also not a replacement for SimpleMatrix or the procedural API.  There are situations where those other interfaces are easier to use and most programs would need to use a mix.
Line 24: Line 24:
  
 
<pre>
 
<pre>
01: public void updateP( DenseMatrix64F P , DenseMatrix64F F , DenseMatrix64F Q ) {
+
01: public void updateP( DMatrixRMaj P , DMatrixRMaj F , DMatrixRMaj Q ) {
 
02:    Equation eq = new Equation();
 
02:    Equation eq = new Equation();
 
03:    eq.alias(P,"P",F,"F",Q,"Q");
 
03:    eq.alias(P,"P",F,"F",Q,"Q");
Line 38: Line 38:
  
 
Three types of variables are supported, matrix, double, and integer.  Results can be stored in each type and all can be aliased.  The example below uses all 3 data types and to compute the likelihood of "x" from a multivariable normal distribution defined by matrices 'mu' and 'P'.
 
Three types of variables are supported, matrix, double, and integer.  Results can be stored in each type and all can be aliased.  The example below uses all 3 data types and to compute the likelihood of "x" from a multivariable normal distribution defined by matrices 'mu' and 'P'.
 +
 
<syntaxhighlight lang="java">
 
<syntaxhighlight lang="java">
 
eq.alias(x.numRows,"k",P,"P",x,"x",mu,"mu");
 
eq.alias(x.numRows,"k",P,"P",x,"x",mu,"mu");
 
eq.process("p = (2*pi)^(-k/2)/sqrt(det(P))*exp(-0.5*(x-mu)'*inv(P)*(x-mu))");
 
eq.process("p = (2*pi)^(-k/2)/sqrt(det(P))*exp(-0.5*(x-mu)'*inv(P)*(x-mu))");
}}}
+
</syntaxhighlight>
 +
 
 
The end result 'p' will be a double.  There was no need to alias 'pi' since it's a built in constant.  Since 'p' is lazily defined how do you access the result?
 
The end result 'p' will be a double.  There was no need to alias 'pi' since it's a built in constant.  Since 'p' is lazily defined how do you access the result?
{{{
+
 
 +
<syntaxhighlight lang="java">
 
double p = eq.lookupDouble("p");
 
double p = eq.lookupDouble("p");
}}}
+
</syntaxhighlight>
 +
 
 
For a matrix you could use eq.lookupMatrix() and eq.lookupInteger() for integers.  If you don't know the variable's type then eq.lookupVariable() is what you need.
 
For a matrix you could use eq.lookupMatrix() and eq.lookupInteger() for integers.  If you don't know the variable's type then eq.lookupVariable() is what you need.
  
 
It is also possible to define a matrix inline:
 
It is also possible to define a matrix inline:
{{{
+
 
 +
<syntaxhighlight lang="java">
 
eq.process("P = [10 0 0;0 10 0;0 0 10]");
 
eq.process("P = [10 0 0;0 10 0;0 0 10]");
}}}
+
</syntaxhighlight>
 
Will assign P to a 3x3 matrix with 10's all along it's diagonal.  Other matrices can also be included inside:
 
Will assign P to a 3x3 matrix with 10's all along it's diagonal.  Other matrices can also be included inside:
{{{
+
<syntaxhighlight lang="java">
 
eq.process("P = [A ; B]");
 
eq.process("P = [A ; B]");
}}}
+
</syntaxhighlight>
 
will concatenate A and B horizontally.
 
will concatenate A and B horizontally.
  
 
Submatrices are also supported for assignment and reference.
 
Submatrices are also supported for assignment and reference.
{{{
+
<syntaxhighlight lang="java">
 
eq.process("P(2:5,0:3) = 10*A(1:4,10:13)");
 
eq.process("P(2:5,0:3) = 10*A(1:4,10:13)");
}}}
+
</syntaxhighlight>
 
P(2:5,0:3) references the sub-matrix inside of P from rows 2 to 5 (inclusive) and columns 0 to 3 (inclusive).
 
P(2:5,0:3) references the sub-matrix inside of P from rows 2 to 5 (inclusive) and columns 0 to 3 (inclusive).
  
Line 73: Line 78:
  
 
Original:
 
Original:
{{{
+
<syntaxhighlight lang="java">
 
eq.process("K = P*H'*inv( H*P*H' + R )");
 
eq.process("K = P*H'*inv( H*P*H' + R )");
}}}
+
</syntaxhighlight>
  
 
Precompiled:
 
Precompiled:
{{{
+
<syntaxhighlight lang="java">
 
// precompile the equation
 
// precompile the equation
 
Sequence s = eq.compile("K = P*H'*inv( H*P*H' + R )");
 
Sequence s = eq.compile("K = P*H'*inv( H*P*H' + R )");
 
// execute the results with out needing to recompile
 
// execute the results with out needing to recompile
 
s.perform();
 
s.perform();
}}}
+
</syntaxhighlight>
  
 
Both are equivalent, but if an equation is invoked multiple times the precompiled version can have a noticable improvement in performance.  Using precompiled sequences also means that means that internal arrays are only declared once and allows the user to control when memory is created/destroyed.
 
Both are equivalent, but if an equation is invoked multiple times the precompiled version can have a noticable improvement in performance.  Using precompiled sequences also means that means that internal arrays are only declared once and allows the user to control when memory is created/destroyed.
Line 90: Line 95:
  
 
When an equation is precompiled you can still change the alias for a variable.
 
When an equation is precompiled you can still change the alias for a variable.
{{{
+
<syntaxhighlight lang="java">
 
eq.alias(0,"sum",0,"i");
 
eq.alias(0,"sum",0,"i");
 
Sequence s = eq.compile("sum = sum + i");
 
Sequence s = eq.compile("sum = sum + i");
Line 97: Line 102:
 
     s.perform();
 
     s.perform();
 
}
 
}
}}}
+
</syntaxhighlight>
 
This will sum up the numbers from 0 to 9.
 
This will sum up the numbers from 0 to 9.
  
Line 105: Line 110:
  
 
For example:
 
For example:
{{{
+
<syntaxhighlight lang="java">
 
eq.process("y = z - H*x",true);
 
eq.process("y = z - H*x",true);
}}}
+
</syntaxhighlight>
 
When application is run it will print out
 
When application is run it will print out
{{{
+
<syntaxhighlight lang="java">
 
Parsed tokens:
 
Parsed tokens:
 
------------
 
------------
Line 125: Line 130:
 
subtract-mm
 
subtract-mm
 
copy-mm
 
copy-mm
}}}
+
</syntaxhighlight>
  
 
= Alias =
 
= Alias =
  
To manipulate matrices in equations they need to be aliased.  Both DenseMatrix64F and SimpleMatrix can be aliased.  A copy of scalar numbers can also be aliased.  When a variable is aliased a reference to the data is saved and a name associated with it.
+
To manipulate matrices in equations they need to be aliased.  Both DMatrixRMaj and SimpleMatrix can be aliased.  A copy of scalar numbers can also be aliased.  When a variable is aliased a reference to the data is saved and a name associated with it.
{{{
+
<syntaxhighlight lang="java">
DenseMatrix64F x = new DenseMatrix64F(6,1);
+
DMatrixRMaj x = new DMatrixRMaj(6,1);
 
eq.alias(x,"x");
 
eq.alias(x,"x");
}}}
+
</syntaxhighlight>
 
Multiple variables can be aliased at the same time too
 
Multiple variables can be aliased at the same time too
{{{
+
<syntaxhighlight lang="java">
 
eq.alias(x,"x",P,"P",h,"Happy");
 
eq.alias(x,"x",P,"P",h,"Happy");
}}}
+
</syntaxhighlight>
 
As is shown above the string name for a variable does not have to be the same as Java name of the variable.  Here is an example where an integer and double is aliased.
 
As is shown above the string name for a variable does not have to be the same as Java name of the variable.  Here is an example where an integer and double is aliased.
{{{
+
<syntaxhighlight lang="java">
 
int a = 6;
 
int a = 6;
 
eq.alias(2.3,"distance",a,"a");
 
eq.alias(2.3,"distance",a,"a");
}}}
+
</syntaxhighlight>
  
 
After a variable has been aliased you can alias the same name again to change it.  Here is an example of just that:
 
After a variable has been aliased you can alias the same name again to change it.  Here is an example of just that:
{{{
+
<syntaxhighlight lang="java">
 
for( int i = 0; i < 10; i++ ) {
 
for( int i = 0; i < 10; i++ ) {
 
     eq.alias(i,"i");
 
     eq.alias(i,"i");
 
     // do stuff with i
 
     // do stuff with i
 
}
 
}
}}}
+
</syntaxhighlight>
  
 
If after benchmarking your code and discover that the alias operation is slowing it down (a hashmap lookup is done internally) then you should consider the following faster, but uglier, alternative.
 
If after benchmarking your code and discover that the alias operation is slowing it down (a hashmap lookup is done internally) then you should consider the following faster, but uglier, alternative.
{{{
+
<syntaxhighlight lang="java">
 
VariableInteger i =  eq.lookupVariable("i");
 
VariableInteger i =  eq.lookupVariable("i");
 
for( i.value = 0; i.value < 10; i.value++ ) {
 
for( i.value = 0; i.value < 10; i.value++ ) {
 
     // do stuff with i
 
     // do stuff with i
 
}
 
}
}}}
+
</syntaxhighlight>
  
 
= Submatrices =
 
= Submatrices =
  
 
Sub-matrices can be read from and written to.  It's easy to reference a sub-matrix inside of any matrix. A few examples are below.
 
Sub-matrices can be read from and written to.  It's easy to reference a sub-matrix inside of any matrix. A few examples are below.
{{{
+
<syntaxhighlight lang="java">
 
A(1:4,0:5)
 
A(1:4,0:5)
}}}
+
</syntaxhighlight>
 
Here rows 1 to 4 (inclusive) and columns 0 to 5 (inclusive) compose the sub-matrix of A.  The notation "a:b" indicates an integer set from 'a' to 'b', where 'a' and 'b' must be integers themselves.  To specify every row or column do ":" or all rows and columns past a certain 'a' can be referenced with "a:".  Finally, you can reference just a number by typeing it, e.g. "a".
 
Here rows 1 to 4 (inclusive) and columns 0 to 5 (inclusive) compose the sub-matrix of A.  The notation "a:b" indicates an integer set from 'a' to 'b', where 'a' and 'b' must be integers themselves.  To specify every row or column do ":" or all rows and columns past a certain 'a' can be referenced with "a:".  Finally, you can reference just a number by typeing it, e.g. "a".
{{{
+
<syntaxhighlight lang="java">
 
A(3:,3)  <-- Rows from 3 to the last row and just column 3
 
A(3:,3)  <-- Rows from 3 to the last row and just column 3
 
A(:,:)  <-- Every element in A
 
A(:,:)  <-- Every element in A
 
A(1,2)  <-- The element in A at row=1,col=2
 
A(1,2)  <-- The element in A at row=1,col=2
}}}
+
</syntaxhighlight>
 
The last example is a special case in that A(1,2) will return a double and not 1x1 matrix.  Consider the following:
 
The last example is a special case in that A(1,2) will return a double and not 1x1 matrix.  Consider the following:
{{{
+
<syntaxhighlight lang="java">
 
A(0:2,0:2) = C/B(1,2)
 
A(0:2,0:2) = C/B(1,2)
}}}
+
</syntaxhighlight>
 
The results of dividing the elements of matrix C by the value of B(1,2) is assigned to the submatrix in A.  
 
The results of dividing the elements of matrix C by the value of B(1,2) is assigned to the submatrix in A.  
  
 
A named variable can also be used to reference elements as long as it's an integer.
 
A named variable can also be used to reference elements as long as it's an integer.
{{{
+
<syntaxhighlight lang="java">
 
a = A(i,j)
 
a = A(i,j)
}}}
+
</syntaxhighlight>
  
 
= Inline Matrix =
 
= Inline Matrix =
  
 
Matrices can be created inline and are defined inside of brackets. The matrix is specified in a row-major format, where a space separates elements in a row and a semi-colon indicates the end of a row.
 
Matrices can be created inline and are defined inside of brackets. The matrix is specified in a row-major format, where a space separates elements in a row and a semi-colon indicates the end of a row.
{{{
+
<syntaxhighlight lang="java">
 
[5 0 0;0 4.0 0.0 ; 0 0 1]
 
[5 0 0;0 4.0 0.0 ; 0 0 1]
}}}
+
</syntaxhighlight>
 
Defines a 3x3 matrix with 5,4,1 for it's diagonal elements.  Visually this looks like:
 
Defines a 3x3 matrix with 5,4,1 for it's diagonal elements.  Visually this looks like:
{{{
+
<syntaxhighlight lang="java">
 
[ 5 0 0 ]
 
[ 5 0 0 ]
 
[ 0 4 0 ]
 
[ 0 4 0 ]
 
[ 0 0 1 ]
 
[ 0 0 1 ]
}}}
+
</syntaxhighlight>
 
An inline matrix can be used to concatenate other matrices together.
 
An inline matrix can be used to concatenate other matrices together.
{{{
+
<syntaxhighlight lang="java">
 
[ A ; B ; C ]
 
[ A ; B ; C ]
}}}
+
</syntaxhighlight>
 
Will concatenate matrices A, B, and C along their rows.  They must have the same number of columns.  As you might guess, to concatenate along columns you would
 
Will concatenate matrices A, B, and C along their rows.  They must have the same number of columns.  As you might guess, to concatenate along columns you would
{{{
+
<syntaxhighlight lang="java">
 
[ A B C ]
 
[ A B C ]
}}}
+
</syntaxhighlight>
 
and each matrix must have the same number of rows.  Inner matrices are also allowed
 
and each matrix must have the same number of rows.  Inner matrices are also allowed
{{{
+
<syntaxhighlight lang="java">
 
[ [1 2;2 3] [4;5] ; A ]
 
[ [1 2;2 3] [4;5] ; A ]
}}}
+
</syntaxhighlight>
 
which will result in
 
which will result in
{{{
+
<syntaxhighlight lang="java">
 
[ 1 2 4 ]
 
[ 1 2 4 ]
 
[ 2 3 5 ]
 
[ 2 3 5 ]
 
[  A  ]
 
[  A  ]
}}}
+
</syntaxhighlight>
  
 
= Built in Functions and Variables =
 
= Built in Functions and Variables =
*Constants*
+
 
 +
'''Constants'''
 
<pre>
 
<pre>
 
pi = Math.PI
 
pi = Math.PI
Line 221: Line 227:
 
</pre>
 
</pre>
  
*Functions*
+
'''Functions'''
 +
 
 
<pre>
 
<pre>
 
eye(N)      Create an identity matrix which is N by N.
 
eye(N)      Create an identity matrix which is N by N.
Line 248: Line 255:
 
exp(a)      Math.exp(a) for scalars and element-wise matrices
 
exp(a)      Math.exp(a) for scalars and element-wise matrices
 
log(a)      Math.log(a) for scalars and element-wise matrices
 
log(a)      Math.log(a) for scalars and element-wise matrices
}}}
+
</pre>
*Symbols*
+
 
{{{
+
'''Symbols'''
 +
 
 +
<pre>
 
'*'        multiplication (Matrix-Matrix, Scalar-Matrix, Scalar-Scalar)
 
'*'        multiplication (Matrix-Matrix, Scalar-Matrix, Scalar-Scalar)
 
'+'        addition (Matrix-Matrix, Scalar-Matrix, Scalar-Scalar)
 
'+'        addition (Matrix-Matrix, Scalar-Matrix, Scalar-Scalar)
Line 264: Line 273:
 
'''        matrix transpose
 
'''        matrix transpose
 
'='        assignment by value (Matrix-Matrix, Scalar-Scalar)
 
'='        assignment by value (Matrix-Matrix, Scalar-Scalar)
 +
</pre>
 +
Order of operations:  [ ' ] precedes [ ^ .^ ] precedes [ *  /  .*  ./ ] precedes [ +  - ]
 +
 +
 +
= Specialized Submatrix and Matrix Construction =
 +
<pre>
 +
Extracts a sub-matrix from A with rows 1 to 10 (inclusive) and column 3.
 +
              A(1:10,3)
 +
Extracts a sub-matrix from A with rows 2 to numRows-1 (inclusive) and all the columns.
 +
              A(2:,:)
 +
Will concat A and B along their columns and then concat the result with  C along their rows.
 +
              [A,B;C]
 +
Defines a 3x2 matrix.
 +
          [1 2; 3 4; 4 5]
 +
You can also perform operations inside:
 +
          [[2 3 4]';[4 5 6]']
 +
Will assign B to the sub-matrix in A.
 +
            A(1:3,4:8) = B
 +
</pre>
 +
= Integer Number Sequences =
 +
 +
Previous example code has made much use of integer number sequences. There are three different types of integer number sequences 'explicit', 'for', and 'for-range'.
 +
<pre>
 +
1) Explicit:
 +
  Example: "1 2 4 0"
 +
  Example: "1 2,-7,4"    Commas needed to create negative numbers. Otherwise it will be subtraction.
 +
2) for:
 +
  Example:  "2:10"        Sequence of "2 3 4 5 6 7 8 9 10"
 +
  Example:  "2:2:10"      Sequence of "2 4 6 8 10"
 +
3) for-range:
 +
  Example:  "2:"          Sequence of "2 3 ... max"
 +
  Example:  "2:2:"        Sequence of "2 4 ... max"
 +
4) combined:
 +
  Example:  "1 2 7:10"    Sequence of "1 2 7 8 9 10"
 
</pre>
 
</pre>
  
Line 270: Line 313:
 
It's easy to add your own custom functions too.  A custom function implements ManagerFunctions.Input1 or ManagerFunctions.InputN, depending on the number of inputs it takes.  It is then added to the ManagerFunctions in Equation by call add().  The output matrix should also be resized.
 
It's easy to add your own custom functions too.  A custom function implements ManagerFunctions.Input1 or ManagerFunctions.InputN, depending on the number of inputs it takes.  It is then added to the ManagerFunctions in Equation by call add().  The output matrix should also be resized.
  
[https://github.com/lessthanoptimal/ejml/blob/equation/examples/src/org/ejml/example/EquationCustomFunction.java Custom Function Example]
+
[[Example Customizing Equations]]
 +
 
 +
= User Defined Macros =
 +
 
 +
Macros are used to insert patterns into the code.  Consider this example:
 +
 
 +
<syntaxhighlight lang="java">
 +
eq.process("macro ata( a ) = (a'*a)");
 +
eq.process("b = ata(c)");
 +
</syntaxhighlight>
 +
 
 +
The first line defines a macro named "ata" with one parameter 'a'. When compiled the equation in the second
 +
line is replaced with "b = (a'*a)".  The "(" ")" in the macro isn't strictly necissary in this situation, but
 +
is a good practice.  Consider the following.
 +
 
 +
<syntaxhighlight lang="java">
 +
eq.process("b = ata(c)*r");
 +
</syntaxhighlight>
 +
Will become "b = (a'*a)*r"  but with out () it will be "b = a'*a*r" which is not the same thing!
 +
 
 +
<p><b>NOTE:</b>In the future macros might be replaced with functions.  Macros are harder for the user to debug, but
 +
functions are harder for EJML's developer to implement.</p>

Latest revision as of 21:58, 17 May 2017

Writing succinct and readable linear algebra code in Java, using any library, is problematic. Originally EJML just offered two API's for performing linear algebra. A procedural API which provided complete control over memory and which algorithms were used, but was verbose and has a sharper learning curve. Alternatively you could use an object oriented API (SimpleMatrix) but lose control over memory and it has a limited set of operators. Neither of these API's produces code which is all that similar to how equations are written mathematically.

Languages, such as Matlab, are specifically designed for processing matrices and are much closer to mathematical notation. C++ offers the ability to overload operators allowing for more natural code, see Eigen. To overcome this problem EJML now provides the _Equation_ API, which allows a Matlab/Octave like notation to be used.

This is achieved by parsing text strings with equations and converting it into a set of executable instructions, see the usage example below:

eq.process("K = P*H'*inv( H*P*H' + R )");

It is easy to see that the equations code is the compact and readable. While the syntax is heavily inspired by Matlab and its kin, it does not attempt to replicate their functionality. It is also not a replacement for SimpleMatrix or the procedural API. There are situations where those other interfaces are easier to use and most programs would need to use a mix.

Equations is designed to have minimal overhead. It runs almost as fast as the procedural API and can be used such that all memory is predeclared.


Quick Start

The syntax used in Equation is very similar to Matlab and other computer algebra systems (CAS). It is assumed the reader is already familiar with these systems and can quickly pick up the syntax through these examples.

Let's start with a complete simple example then explain what's going on line by line.

01: public void updateP( DMatrixRMaj P , DMatrixRMaj F , DMatrixRMaj Q ) {
02:     Equation eq = new Equation();
03:     eq.alias(P,"P",F,"F",Q,"Q");
04:     eq.process("S = F*P*F'");
05:     eq.process("P = S + Q");
06: }

Line 02: Declare the Equation class.
Line 03: Create aliases for each variable. This allowed Equation to reference and manipulate those classes.
Line 04: Process() is called and passed in a text string with an equation in it. The variable 'S' is lazily created and set to the result of F*P*F'.
Line 05: Process() is called again and P is set to the result of adding S and Q together. Because P is aliased to the input matrix P that matrix is changed.

Three types of variables are supported, matrix, double, and integer. Results can be stored in each type and all can be aliased. The example below uses all 3 data types and to compute the likelihood of "x" from a multivariable normal distribution defined by matrices 'mu' and 'P'.

eq.alias(x.numRows,"k",P,"P",x,"x",mu,"mu");
eq.process("p = (2*pi)^(-k/2)/sqrt(det(P))*exp(-0.5*(x-mu)'*inv(P)*(x-mu))");

The end result 'p' will be a double. There was no need to alias 'pi' since it's a built in constant. Since 'p' is lazily defined how do you access the result?

double p = eq.lookupDouble("p");

For a matrix you could use eq.lookupMatrix() and eq.lookupInteger() for integers. If you don't know the variable's type then eq.lookupVariable() is what you need.

It is also possible to define a matrix inline:

eq.process("P = [10 0 0;0 10 0;0 0 10]");

Will assign P to a 3x3 matrix with 10's all along it's diagonal. Other matrices can also be included inside:

eq.process("P = [A ; B]");

will concatenate A and B horizontally.

Submatrices are also supported for assignment and reference.

eq.process("P(2:5,0:3) = 10*A(1:4,10:13)");

P(2:5,0:3) references the sub-matrix inside of P from rows 2 to 5 (inclusive) and columns 0 to 3 (inclusive).

This concludes the quick start tutorial. The remaining sections will go into more detail on each of the subjects touched upon above.

The Compiler

The current compiler is very basic and performs very literal translations of equations into code. For example, "A = 2.5*B*C'" could be executed with a single call to CommonOps.multTransB(). Instead it will transpose C, save the result, then scale B by 2.5, save the result, multiply the results together, save that, and finally copy the result into A. In the future the compiler will become smart enough to recognize such patterns.

Compiling the text string contains requires a bit of overhead but once compiled it can be run with very fast. When dealing with larger matrices the overhead involved is insignificant, but for smaller ones it can have a noticeable impact. This is why the ability to precompile an equation is provided.

Original:

eq.process("K = P*H'*inv( H*P*H' + R )");

Precompiled:

// precompile the equation
Sequence s = eq.compile("K = P*H'*inv( H*P*H' + R )");
// execute the results with out needing to recompile
s.perform();

Both are equivalent, but if an equation is invoked multiple times the precompiled version can have a noticable improvement in performance. Using precompiled sequences also means that means that internal arrays are only declared once and allows the user to control when memory is created/destroyed.

To make it clear, precompiling is only recommended when dealing with smaller matrices or when tighter control over memory is required.

When an equation is precompiled you can still change the alias for a variable.

eq.alias(0,"sum",0,"i");
Sequence s = eq.compile("sum = sum + i");
for( int i = 0; i < 10; i++ ) {
    eq.alias(i,"i");
    s.perform();
}

This will sum up the numbers from 0 to 9.

Debugging

There will be times when you pass in an equation and it throws some weird exception or just doesn't do what you expected. To see the tokens and sequence of operations set the second parameter in compile() or peform() to true.

For example:

eq.process("y = z - H*x",true);

When application is run it will print out

Parsed tokens:
------------
VarMATRIX
ASSIGN
VarMATRIX
MINUS
VarMATRIX
TIMES
VarMATRIX

Operations:
------------
multiply-mm
subtract-mm
copy-mm

Alias

To manipulate matrices in equations they need to be aliased. Both DMatrixRMaj and SimpleMatrix can be aliased. A copy of scalar numbers can also be aliased. When a variable is aliased a reference to the data is saved and a name associated with it.

DMatrixRMaj x = new DMatrixRMaj(6,1);
eq.alias(x,"x");

Multiple variables can be aliased at the same time too

eq.alias(x,"x",P,"P",h,"Happy");

As is shown above the string name for a variable does not have to be the same as Java name of the variable. Here is an example where an integer and double is aliased.

int a = 6;
eq.alias(2.3,"distance",a,"a");

After a variable has been aliased you can alias the same name again to change it. Here is an example of just that:

for( int i = 0; i < 10; i++ ) {
    eq.alias(i,"i");
    // do stuff with i
}

If after benchmarking your code and discover that the alias operation is slowing it down (a hashmap lookup is done internally) then you should consider the following faster, but uglier, alternative.

VariableInteger i =  eq.lookupVariable("i");
for( i.value = 0; i.value < 10; i.value++ ) {
    // do stuff with i
}

Submatrices

Sub-matrices can be read from and written to. It's easy to reference a sub-matrix inside of any matrix. A few examples are below.

A(1:4,0:5)

Here rows 1 to 4 (inclusive) and columns 0 to 5 (inclusive) compose the sub-matrix of A. The notation "a:b" indicates an integer set from 'a' to 'b', where 'a' and 'b' must be integers themselves. To specify every row or column do ":" or all rows and columns past a certain 'a' can be referenced with "a:". Finally, you can reference just a number by typeing it, e.g. "a".

A(3:,3)  <-- Rows from 3 to the last row and just column 3
A(:,:)   <-- Every element in A
A(1,2)   <-- The element in A at row=1,col=2

The last example is a special case in that A(1,2) will return a double and not 1x1 matrix. Consider the following:

A(0:2,0:2) = C/B(1,2)

The results of dividing the elements of matrix C by the value of B(1,2) is assigned to the submatrix in A.

A named variable can also be used to reference elements as long as it's an integer.

a = A(i,j)

Inline Matrix

Matrices can be created inline and are defined inside of brackets. The matrix is specified in a row-major format, where a space separates elements in a row and a semi-colon indicates the end of a row.

[5 0 0;0 4.0 0.0 ; 0 0 1]

Defines a 3x3 matrix with 5,4,1 for it's diagonal elements. Visually this looks like:

[ 5 0 0 ]
[ 0 4 0 ]
[ 0 0 1 ]

An inline matrix can be used to concatenate other matrices together.

[ A ; B ; C ]

Will concatenate matrices A, B, and C along their rows. They must have the same number of columns. As you might guess, to concatenate along columns you would

[ A B C ]

and each matrix must have the same number of rows. Inner matrices are also allowed

[ [1 2;2 3] [4;5] ; A ]

which will result in

[ 1 2 4 ]
[ 2 3 5 ]
[   A   ]

Built in Functions and Variables

Constants

pi = Math.PI
e  = Math.E

Functions

eye(N)       Create an identity matrix which is N by N.
eye(A)       Create an identity matrix which is A.numRows by A.numCols
normF(A)     Frobenius normal of the matrix.
det(A)       Determinant of the matrix
inv(A)       Inverse of a matrix
pinv(A)      Pseudo-inverse of a matrix
rref(A)      Reduced row echelon form of A
trace(A)     Trace of the matrix
zeros(r,c)   Matrix full of zeros with r rows and c columns.
ones(r,c)    Matrix full of ones with r rows and c columns.
diag(A)      If a vector then returns a square matrix with diagonal elements filled with vector
diag(A)      If a matrix then it returns the diagonal elements as a column vector
dot(A,B)     Returns the dot product of two vectors as a double.  Does not work on general matrices.
solve(A,B)   Returns the solution X from A*X = B.
kron(A,B)    Kronecker product
abs(A)       Absolute value of A.
max(A)       Element with the largest value in A.
min(A)       Element with the smallest value in A.
pow(a,b)     Scalar power of a to b.  Can also be invoked with "a^b".
sin(a)       Math.sin(a) for scalars only
cos(a)       Math.cos(a) for scalars only
atan(a)      Math.atan(a) for scalars only
atan2(a,b)   Math.atan2(a,b) for scalars only
exp(a)       Math.exp(a) for scalars and element-wise matrices
log(a)       Math.log(a) for scalars and element-wise matrices

Symbols

'*'        multiplication (Matrix-Matrix, Scalar-Matrix, Scalar-Scalar)
'+'        addition (Matrix-Matrix, Scalar-Matrix, Scalar-Scalar)
'-'        subtraction (Matrix-Matrix, Scalar-Matrix, Scalar-Scalar)
'/'        divide (Matrix-Scalar, Scalar-Scalar)
'/'        matrix solve "x=b/A" is equivalent to x=solve(A,b) (Matrix-Matrix)
'^'        Scalar power.  a^b is a to the power of b.
'\'        left-divide.  Same as divide but reversed.  e.g. x=A\b is x=solve(A,b)
'.*'       element-wise multiplication (Matrix-Matrix)
'./'       element-wise division (Matrix-Matrix)
'.^'       element-wise power. (scalar-scalar) (matrix-matrix) (scalar-matrix) (matrix-scalar)
'^'        Scalar power.  a^b is a to the power of b.
'''        matrix transpose
'='        assignment by value (Matrix-Matrix, Scalar-Scalar)

Order of operations: [ ' ] precedes [ ^ .^ ] precedes [ * / .* ./ ] precedes [ + - ]


Specialized Submatrix and Matrix Construction

Extracts a sub-matrix from A with rows 1 to 10 (inclusive) and column 3.
              A(1:10,3)
Extracts a sub-matrix from A with rows 2 to numRows-1 (inclusive) and all the columns.
              A(2:,:)
Will concat A and B along their columns and then concat the result with  C along their rows.
               [A,B;C]
Defines a 3x2 matrix.
           [1 2; 3 4; 4 5]
You can also perform operations inside:
           [[2 3 4]';[4 5 6]']
Will assign B to the sub-matrix in A.
            A(1:3,4:8) = B

Integer Number Sequences

Previous example code has made much use of integer number sequences. There are three different types of integer number sequences 'explicit', 'for', and 'for-range'.

1) Explicit:
   Example: "1 2 4 0"
   Example: "1 2,-7,4"     Commas needed to create negative numbers. Otherwise it will be subtraction.
2) for:
   Example:  "2:10"        Sequence of "2 3 4 5 6 7 8 9 10"
   Example:  "2:2:10"      Sequence of "2 4 6 8 10"
3) for-range:
   Example:  "2:"          Sequence of "2 3 ... max"
   Example:  "2:2:"        Sequence of "2 4 ... max"
4) combined:
   Example:  "1 2 7:10"    Sequence of "1 2 7 8 9 10"

User Defined Functions

It's easy to add your own custom functions too. A custom function implements ManagerFunctions.Input1 or ManagerFunctions.InputN, depending on the number of inputs it takes. It is then added to the ManagerFunctions in Equation by call add(). The output matrix should also be resized.

Example Customizing Equations

User Defined Macros

Macros are used to insert patterns into the code. Consider this example:

eq.process("macro ata( a ) = (a'*a)");
eq.process("b = ata(c)");

The first line defines a macro named "ata" with one parameter 'a'. When compiled the equation in the second line is replaced with "b = (a'*a)". The "(" ")" in the macro isn't strictly necissary in this situation, but is a good practice. Consider the following.

eq.process("b = ata(c)*r");

Will become "b = (a'*a)*r" but with out () it will be "b = a'*a*r" which is not the same thing!

NOTE:In the future macros might be replaced with functions. Macros are harder for the user to debug, but functions are harder for EJML's developer to implement.