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

Reply via email to