On Mon, 13 Jun 2022 at 07:47, Sumanth Rajkumar <rajkumar.suma...@gmail.com> wrote:
> > > For Phase 1, I propose to do the following > > > 1) Introduce ComplexDouble, ComplexDoubleVector and ComplexDoubleMatrix > interfaces > What do you intend to support as a "Matrix"? Is it for 2D or ND? What functionality already exists for complex matrix operations such as add and multiply in for example EJML? This may require some expansion. > The interfaces to have methods for applying below unary and binary > double functions > The interfaces to have methods to convert to/from primitive double > arrays (both separate arrays for real/imaginary and single interleaved > array) > The interfaces to have methods to convert to/from DoubleBuffer (both > separate arrays for real/imaginary and single interleaved buffer) > > 2) Introduce generic (item based) functional interfaces for unary and > binary double functions > > @FunctionalInterface > public interface ComplexDoubleFunction<R> { > > R apply(double real, double imaginary, ComplexDoubleResult<R> result); > } > > @FunctionalInterface > public interface ComplexDoubleBiFunction<R> { > > R apply(double real1, double imaginary1, double real2, double imaginary2, > ComplexDoubleResult<R> result); > } > > @FunctionalInterface > public interface ComplexDoubleResult<R> { > > R apply(double r, double i); > > } > > I am still wondering how these functions can be composed. Here are a few ideas based on requiring that the functional interface generic type is a ComplexDouble. This allows the result single item R to be decomposed again into real and imaginary parts to pass to the next method. public static <R> R conj(double r, double i, ComplexResult<R> result) { return result.apply(r, -i); } public static <R> R multiplyImaginary(double r, double i, ComplexResult<R> result) { return result.apply(-i, r); } @FunctionalInterface public interface ComplexDoubleFunction<R extends ComplexDouble> { default R apply(ComplexDouble c, ComplexResult<R> result) { return apply(c.real(), c.imag(), result); } R apply(double r, double i, ComplexResult<R> result); default <V extends ComplexDouble> ComplexDoubleFunction<V> andThen(ComplexDoubleFunction<V> after, ComplexResult<R> intermediateResult) { Objects.requireNonNull(after); // Requires the intermediate which would be the terminal result if the function is not composed. return (r, i, result) -> after.apply(apply(r, i, intermediateResult), result); } default <V extends ComplexDouble> ComplexDoubleFunction<V> andThen2(ComplexDoubleFunction<V> after) { Objects.requireNonNull(after); return (re, im, result) -> { // Fabricate the intermediate. Function is not thread safe. double[] parts = {0, 0}; ComplexResult<R> intermediateResult = (x, y) -> { parts[0] = x; parts[1] = y; return null; }; R t = apply(re, im, intermediateResult); return after.apply(t, result); }; } default ComplexDoubleFunction<R> andThen(ComplexDoubleFunction<R> after) { Objects.requireNonNull(after); // Thread safe. The intermediate is also the terminal result. This is not optimal if the intermediate/terminal is a list with inefficient read/write to the current position. return (r, i, result) -> after.apply(apply(r, i, result), result); } } public static void example() { ComplexDoubleFunction<ComplexDouble> fun = ComplexDoubleFunctions::conj; ComplexDoubleFunction<ComplexDouble> fun2 = ComplexDoubleFunctions::multiplyImaginary; ComplexDoubleFunction<Complex> fun3 = ComplexDoubleFunctions::multiplyImaginary; // Not allowed as Void does not extend ComplexDouble //ComplexDoubleFunction<Void> fun4 = ComplexDoubleFunctions::multiplyImaginary; ComplexDoubleFunction<ComplexDouble> funA = fun.andThen(fun2); // Not allowed //ComplexDoubleFunction<Complex> funA2 = fun.andThen(fun2); ComplexDoubleFunction<Complex> funB = fun.andThen2(fun3); ComplexDoubleFunction<Complex> funC = fun.andThen(fun3, Complex::ofCartesian); } However you cannot create a ComplexDoubleFunction<Void> which will not return a result as the interface is typed to ComplexDouble. Not returning a final result is the usage required by list operations that do not return results but write back to storage in ComplexResult<Void>. Composite function requires an intermediate to hold the result to pass to the next function. If this is the same object then the function will not be thread-safe. This may need more work... > 4) Refactor existing instance methods in Complex class as static functions > in ComplexDoubleFunctions class using functional interface signatures > I would start with this. It can always be updated if the functional interface is later modified. Alex