Skip to content

Math with Class (Java)

Gleethos edited this page Jul 18, 2021 · 4 revisions

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:

public class ComplexNumber
{
    private double real = 0.0
    private double imaginary = 0.0

    public ComplexNumber(double real, double imaginary){
        this.real = real; this.imaginary = imaginary;
    }

    public ComplexNumber plus(ComplexNumber z2) {
        return new ComplexNumber(this.real + z2.real, this.imaginary + z2.imaginary);
    }

    public ComplexNumber minus(ComplexNumber z2) {
        return new ComplexNumber(this.real - z2.real, this.imaginary - z2.imaginary);
    }

    public 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);
    }

    public 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);
    }

    public double mod() {
        return Math.sqrt(Math.pow(this.real,2) + Math.pow(this.imaginary,2));
    }

    @Override
    public 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<ComplexNumber> a = Tsr.of(ComplexNumber.class)
                            withShape(3, 2)
                            andWhere( ( int i, int[] idx ) -> new ComplexNumber( idx[0], idx[1] ) );
                        
Tsr<ComplexNumber> b = Tsr.of(ComplexNumber.class)
                            .withShape(3, 2)
                            .andWhere( ( 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().equals("(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().equals("(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.plus(b).toString().equals("(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.minus(b).toString().equals( "(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.multiply(b).toString().equals("(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.