>
>
> Some thoughts:
>
> - an array should be mutable by default. It would be made immutable using a
> Collections.immutableX(...) type wrapper.
> - an array should have get and set methods for complex, real and imaginary
> using an index within the array size.
>

Ok

The last two requirements bring us back to the requirement for a generic
> way to write the result of a complex number computation (i.e. a single
> [real, imaginary] pair).
>


I have provided both approaches. Functional interfaces that operate on
single complex and on Arrays

Source available here
https://github.com/sumanth-rajkumar/commons-numbers/tree/98fbedeb7ed3e99597f461d88856b4d94c53343d/commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex

I have summarized the approaches below

1) Functional interfaces
Single Pair Approach

@FunctionalInterface
public interface ComplexFunction<R> {

R apply(double r, double i, ComplexResult<R> result);
}

@FunctionalInterface
public  interface ComplexResult<R> {

R apply(double r, double i);
}


Array Approach
@FunctionalInterface
public interface ComplexDoubleUnaryOperator {
ComplexDoubleArray apply(ComplexDoubleArray input, ComplexDoubleArray
result);
}

@FunctionalInterface
public  interface ComplexArrayResult<R> {

R set(int index, double r, double i);

}

    interface ComplexDoubleArray extends Iterable<ComplexDouble>,
ComplexArrayResult<ComplexDoubleArray> {

...

}



2) Implementation for conjugate example in ComplexFunctions

https://github.com/sumanth-rajkumar/commons-numbers/blob/98fbedeb7ed3e99597f461d88856b4d94c53343d/commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/ComplexFunctions.java#L779

Single Pair Approach

     public static <R> R conj(double r, double i, ComplexResult<R> result) {
        return result.apply(r, -i);
    }


Array Approach (the implementation below can be different in
ComplexParallelFunctions for example)

    public static ComplexDoubleArray conj(ComplexDoubleArray input,
ComplexDoubleArray out) {
        final int len = input.size();
        for (int i = 0; i < len; i++) {
            ComplexDouble c = input.get(i);
            out = arrayConj(c.real(), c.imag(), out, i);
        }
        return out;
    }

    public static ComplexDoubleArray arrayConj(double r, double i,
                                     ComplexArrayResult<ComplexDoubleArray>
resultConsumer, int index) {
        return resultConsumer.set(index, r, -i);
    }

3) Usage of conj in Complex class

https://github.com/sumanth-rajkumar/commons-numbers/blob/98fbedeb7ed3e99597f461d88856b4d94c53343d/commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/Complex.java#L1127

Single Pair Approach

    Complex implements Serializable, ComplexDouble {

       public Complex conj() {
             return this.applyUnaryOperator(ComplexFunctions::conj);
         }

        public Complex applyUnaryOperator(ComplexFunction<Complex>
operator) {
            return operator.apply(this.real, this.imaginary,
Complex::ofCartesian);
        }
}


Array Approach

Complex implements Serializable, ComplexDoubleArray, ComplexDouble {

       public Complex conj() {
             return this.apply(ComplexFunctions::conj);
         }
}

4) Usage of conj in ComplexList<ComplexDouble> (implements
ComplexDoubleArray)


Single Pair Approach

    ComplexList {

public ComplexList conj() {
return this.forEach(ComplexFunctions::conj);
}

public ComplexList forEach(ComplexFunction<Void> operator) {
   . . .
Cursor cursor = new Cursor(..);
for (int i = 0; i < len; i++) {
cursor.setIndex(i);
operator.apply(cursor.getReal(), cursor.getImginary(), cursor);
}
}

private final class Cursor  implements ComplexDouble, ComplexResult<Void> {
  . . .
}

}

Array Approach
   ComplexList implements ComplexDoubleArray {

     public ComplexList conj() {
         return this.apply(ComplexFunctions::conj);
      }

   }

  Here the implementation including iteration/cursors has moved to the
array based Lambda.
  For Complex arrays, I think it is beneficial to move the iteration logic
from data storage (ComplexList) to the Lambda  (ComplexFunctions::conj)

 This would allow developers to use different Lambdas for the same
operation/on the same list



What are you streaming? ComplexDoubleArray sub-arrays? Would the
> ComplexParrallelStreamFunctions class be responsible for creating the
> Spliterator for this to control sub-ranges?
>

Yes. The parallelization details would be abstracted by
ComplexParrallelFunctions
ComplexFunctions could provide the sequential cursor implementation as you
had suggested before

The different implementations abstracted by functional interface would only
be possible with Array based approach

The developer could choose explicitly to call
list.apply(ComplexFunctions::conj) or
list.apply(ComplexParallelFunctions::conj)


ComplexList.conj() could choose to apply sequential or parallel etc based
on some config.


https://github.com/sumanth-rajkumar/commons-numbers/blob/1f013bfcf7d92c4daa160284c3d6a2ec26fd6464/commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/ComplexList.java#L683

If you look at how this can be used then there is a lot of excess object
> overhead here when you wish to write code for complex number computation in
> an OO way:
> See org.apache.commons.math4.legacy.analysis.solvers.LaguerreSolver
>

 I have included both implementations as explained above.
I have tried to avoid excess object creation in the Array approach,

Even when using Array approach for single Complex instance, object creation
overhead is same as Single number approach

Could you please take a quick look and confirm?
The unit test for both approaches for conj operation on ComplexList is here

https://github.com/sumanth-rajkumar/commons-numbers/blob/1f013bfcf7d92c4daa160284c3d6a2ec26fd6464/commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/ComplexListTest.java#L94

Bulk operations such as FFT would require the entire data to be operated
> on. So this would not work in parallel streams using sub-ranges. Using a
> 3rd party such as JTransforms would require extracting the data for their
> API.
>

I see parallelizations in jtransform...
https://github.com/wendykierp/JTransforms/blob/master/src/main/java/org/jtransforms/fft/DoubleFFT_1D.java#L688

It is possible someone comes up with algorithm to implement FFT using SIMD
( Vector API/GPUs) and then we could adapt into Functional interface
similar to Jtransform

The ComplexDoubleArray approach with the methods to convert to/from
interleaved double[] for complex pairs is specifically done for extracting
data in Jtransforms formats

https://github.com/wendykierp/JTransforms/blob/7084c209d7be7952f7bdde7af2fb8eec7cbc17cd/src/main/java/org/jtransforms/fft/DoubleFFT_1D.java#L228

We can add more methods to ComplexDoubleArray to convert/to from for other
providers as needed such as DoubleBuffer formats etc..


> Note: Vector functions on a single complex number is not applicable.
>
>
   Yes, in general there won't be any ComplexVectorFunctions for the single
pair functional interface.
   We provide ComplexVectorFunctions operations only for List based
operation that would benefit from Vector based SIMD parallelization
   However if we pass a List of size 1, it would still work, but possibly
not perform as well.

Thanks
Sumanth

Reply via email to