On Mon, 13 Jun 2022 at 07:47, Sumanth Rajkumar <[email protected]>
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