Math with Class (Groovy) - Gleethos/neureka GitHub Wiki
Custom Numeric Data Types
Sometimes the default primitive data types like double, float, int, short, byte, long... are not enough! One classic example for an important numeric format which is not embedded into most languages is the complex number. If you want to go beyond primitive data types and get any class to act as a numeric type within a tensor and tensor operations then take a look at the following example.
First we have to define a class acting as numeric data type, meaning it implements methods like "plus", "minus", "divide" and so on... Let's create a simple "ComplexNumber" class:
class ComplexNumber
{
private double real = 0.0
private double imaginary = 0.0
ComplexNumber(double real, double imaginary)
{
this.real = real
this.imaginary = imaginary
}
ComplexNumber plus(ComplexNumber z2) {
return new ComplexNumber(this.real + z2.real, this.imaginary + z2.imaginary)
}
ComplexNumber minus(ComplexNumber z2) {
return new ComplexNumber(this.real - z2.real, this.imaginary - z2.imaginary)
}
ComplexNumber multiply(ComplexNumber z2) {
double _real = this.real*z2.real - this.imaginary*z2.imaginary
double _imaginary = this.real*z2.imaginary + this.imaginary*z2.real
return new ComplexNumber(_real,_imaginary)
}
ComplexNumber divide(ComplexNumber z2) {
ComplexNumber output = multiply(z2.conjugate())
double div = Math.pow(z2.mod(),2)
return new ComplexNumber(output.real/div,output.imaginary/div)
}
double mod() {
return Math.sqrt(Math.pow(this.real,2) + Math.pow(this.imaginary,2))
}
@Override
String toString() {
String re = this.real + ""
String im
if (this.imaginary < 0) im = this.imaginary+"i"
else im = "+"+this.imaginary+"i"
return re + im
}
}
Next we have to create two tensors holding some complex numbers. Let's just initialize them for 2D tensors and let them be points which are also the indices of the tensors.
Tsr a = Tsr.of(
DataType.of( ComplexNumber.class ),
[ 3, 2 ],
( int i, int[] idx ) -> new ComplexNumber( idx[0], idx[1] )
)
Tsr b = Tsr.of(
DataType.of( ComplexNumber.class ),
[ 3, 2 ],
( int i, int[] idx ) -> new ComplexNumber( idx[1], idx[0] )
)
And as expected, when doing calculations on these two tensors then this will translate to elementwise operation calls and new tensors containing the results!
assert a.toString() == "(3x2):[0.0+0.0i, 0.0+1.0i, 1.0+0.0i, 1.0+1.0i, 2.0+0.0i, 2.0+1.0i]"
assert b.toString() == "(3x2):[0.0+0.0i, 1.0+0.0i, 0.0+1.0i, 1.0+1.0i, 0.0+2.0i, 1.0+2.0i]"
assert (a+b).toString() == "(3x2):[0.0+0.0i, 1.0+1.0i, 1.0+1.0i, 2.0+2.0i, 2.0+2.0i, 3.0+3.0i]"
assert (a-b).toString() == "(3x2):[0.0+0.0i, -1.0+1.0i, 1.0-1.0i, 0.0+0.0i, 2.0-2.0i, 1.0-1.0i]"
assert (a*b).toString() == "(3x2):[0.0+0.0i, 0.0+1.0i, 0.0+1.0i, 0.0+2.0i, 0.0+4.0i, 0.0+5.0i]"
This feature currently only work for elementwise operations. In future versions linear operations will be supported as well. One important note that has to be kept in mind when using this feature is that is substantially slower than operations in conventional (primitive) data types.