2) ComplexFunction and ComplexResult functional interfaces
Following up on my previous email, another alternative for ComplexFunction
without using generic ComplexResult is as follows

@FunctionalInterface
public interface ComplexFunction3 {
  void apply(Complex input, int offset, double[] result);
}

Example Conjugate implementation

public static void conj(Complex in, int offset, double[] result) {
        result[offset] = in.getReal();
        result[offset+1] = in.getImaginary();
 }

ComplexCartesianImpl data structure will change to double[] realAndImgPair
with static factory method as below

Complex {
  static Complex ofCartesian(double[] realAndImgPair);
}

And the complex functions used like below in Complex and ComplexList

Complex {
    // default implementation are immutable always returning new instance
to maintain b/w compatibility
    default Complex applyFunction(ComplexFunction  function) {
         double[] result = new double[2];
        return Complex.ofCartesian(function.apply(this,0,result);
    }
 }
}

ComplexList {
..
// applies changes in place
void forEach(ComplexFunction fun) {

    ComplexCursor cursor = new ComplexCursor();
    while (cursor.index < r.length) {
        cursor.apply(realFunc.applyAsDouble(cursor),
imgFunc .applyAsDouble(cursor));
        cursor.index++;
    }
}
...
}



On Fri, 10 Jun 2022 at 07:55, Sumanth Rajkumar <rajkumar.suma...@gmail.com>
wrote:

> Hi Alex and Giles,
>
> Thanks for the feedback.
>
> 1) Backward Compatibility and Complex Interface
> Yes. I understand the backward compatibility requirement and my goal is to
> be fully backward compatible.
>
> Fortunately, the existing Complex class has private constructors and so it
> is possible to refactor it as an interface.
> I was able to make the change along with a ComplexCartesianImpl class and
> run all unit tests successfully. I did not have to make any changes to unit
> tests.
> "mvn test" runs successfully on my local machine after the changes
>
>
> https://github.com/sumanth-rajkumar/commons-numbers/blob/sumanth-gsoc-22/commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/Complex.java
>
> https://github.com/sumanth-rajkumar/commons-numbers/blob/sumanth-gsoc-22/commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/ComplexCartesianImpl.java
>
> This I assume should meet the backward compatibility requirements?
>
> The proposed functional interface changes introduces a new interface
> ComplexNumber.
> I think we could reuse the refactored Complex interface instead of  a new
> ComplexNumber interface and still maintain full backward compatibility.
>
> This would provide flexibility to older applications to work with new
> implementations of Complex such as MutableComplexImpl or ComplexPolarImpl
> or even ComplexStructImpl in the future whenever Java supports value types.
>
> Please let me know what you think.
>
> 2) ComplexFunction and ComplexResult functional interfaces
> Yes the generic <R> type introduces noise and can be solved as you
> suggested.
>
> I was also thinking about an alternative that avoids the ComplexResult and
> the generic type <R>.
>
> We could split complex unary operators into two primitive functions (
> ToDoubleFunction<Complex>) one returning the real part of result and other
> for imaginary part
>
> interface ComplexFunction  {
>      ToDoubleFunction<Complex> getReal() ;
>      ToDoubleFunction<Complex> getImaginary() ;
> }
>
> And for example the Conjugate implementation would look like this
> ComplexFunction conj = new ComplexFunction2() {
>         @Override
>         public ToDoubleFunction<Complex> getReal() {
>             return complex -> complex.real();
>         }
>         @Override
>         public ToDoubleFunction<Complex> getImaginary() {
>             return complex -> -complex.imag();
>         }
>
>         };
>    };
>
> And the functions used like below in Complex and ComplexList
>
> Complex {
>     // default implementation are immutable always returning new instance
> to maintain b/w compatibility
>     default Complex applyFunction(ComplexFunction  function) {
>         return
> Complex.ofCartesian(function.getReal().applyAsDouble(this),function.getImaginary().applyAsDouble(this));
>     }
>  }
> }
>
> ComplexList {
> ..
> // applies changes in place
> void forEach(ComplexFunction fun) {
>     ToDoubleFunction<Complex> realFunc = fun.getReal();
>     ToDoubleFunction<Complex> imgFunc = fun.getImaginary();
>     ComplexCursor cursor = new ComplexCursor();
>     while (cursor.index < r.length) {
>         cursor.apply(realFunc.applyAsDouble(cursor),
> imgFunc .applyAsDouble(cursor));
>         cursor.index++;
>     }
> }
> ...
> }
>
> Does this make sense or we just stick to the original interface that
> includes ComplexResult<R>?
>
>
> 3) Naming convention for the Functional Interfaces
>
> On reviewing the functions in java.util.functions package, the convention
> is
>      "Function" name is used for interfaces that can accept inputs of
> different types and return result of different type
>      "Operator" are specialization of "Function" that take same type for
> all inputs and result
>
> Should we follow a similar naming convention for Complex functional
> interfaces?
>
> Thanks
>
>
>
>
> On Fri, 27 May 2022 at 21:34, Alex Herbert <alex.d.herb...@gmail.com>
> wrote:
>
>> On Thu, 26 May 2022 at 15:04, Gilles Sadowski <gillese...@gmail.com>
>> wrote:
>>
>> >
>> > > Next, I wanted to mention how I plan to start this project and was
>> hoping
>> > > to get some feedback.
>> > >
>> > > As per my proposal, the first thing I wanted to start with was the API
>> > > design which would have interfaces to represent complex numbers,
>> methods
>> > to
>> > > convert to/from linear primitive arrays, Java 8 functional interfaces
>> for
>> > > unary/binary operators and for functions for complex operations and
>> > > transforms involving: complex number and real numbers, complex vectors
>> > and
>> > > scalars, complex matrix, vectors and scalars.
>> >
>>
>> There are many items here. I would suggest breaking it down. Perhaps:
>>
>> 1.
>> interfaces to represent complex numbers
>> unary/binary operators and for functions for complex operations
>>
>> 2.
>> methods to convert to/from linear primitive arrays
>>
>> 3.
>> complex vectors and scalars, complex matrix, vectors and scalars.
>>
>>
>> Although not completely independant, we could discuss each in turn and see
>> what functionality is required.
>>
>> I will start with topic 1. Currently we have a single object, Complex,
>> that
>> represents a complex number in cartesian form. It has a full set of
>> operations specified in ISO C99. I would suggest you have a look at the
>> specification as it has a lot of information about this [1].
>>
>> There is a benchmark for these operations in the examples JMH module:
>> org.apache.commons.numbers.examples.jmh.complex.ComplexPerformance
>>
>> Ideally any changes to extract all the methods into a static class should
>> not impact performance. Many of the methods are quite involved and
>> therefore slow. However some methods such as those for
>> add/subtract/multiply/divide with real or imaginary scalars will be fast.
>> It would be interesting to see if abstraction to a static class impacts
>> their performance. These operations are not in the JMH benchmark so this
>> could be added to provide a reference point for these.
>>
>>
>> From your GH code you have the following interface:
>>
>> public interface ComplexFunction {
>>     <R> R apply(double r, double i, ComplexResult<R> result);
>> }
>>
>> I cannot create a lambda function for this as the method has a generic
>> type
>> parameter. This fails to compile.
>>
>> ComplexFunction f = (r, i, result) -> {
>>     // conjugate
>>     return result.apply(r, -i);
>> };
>>
>> This can be solved by moving <R> to the interface declaration:
>>
>> public interface ComplexFunction<R> {
>>     R apply(double r, double i, ComplexResult<R> result);
>> }
>>
>> But then all use of ComplexFunction has to be typed which can get noisy.
>> It
>> is however explicit in what the function output will be (and we assume the
>> input is a complex number of some sort).
>>
>>
>> Q. Do we wish to support effectively duplication of operations by
>> accepting
>> primitives and also a ComplexNumber type in the static methods:
>>
>> interface ComplexNumber {
>>     double real();
>>     double imag();
>> }
>>
>> class ComplexFunctions {
>>     <R extends ComplexNumber> R sin(ComplexNumber c, ComplexResult<R> r) {
>>         return sin(c.real(), c.imag(), r);
>>     }
>>     <R extends ComplexNumber> R sin(double r, double i, ComplexResult<R>)
>> {
>>         // ...
>>     }
>> }
>>
>> There are various options for chaining methods together for sequential
>> operations on the same complex. Should this avoid repeat object allocation
>> by providing a MutableComplex holder:
>>
>> class MutableComplex implements ComplexNumber,
>> ComplexResult<MutableComplex> {
>>    // allows read, write   from/to  real, imaginary parts
>> }
>>
>>
>>
>> Q. How to manipulate ComplexList:
>>
>> class ComplexList implements List<Complex> {
>>     private double[] r;
>>     private double[] i;
>> }
>>
>> You have forEach methods (note this is without the type parameter):
>>
>> void forEach(ComplexFunction fun);
>>
>> So how do I declare a function to pass to the list, that accepts the real
>> and imaginary parts and saves them back to the list. Currently you have
>> the
>> ComplexFunction accept the real and imaginary parts. But what if it
>> accepted a ComplexNumber:
>>
>> public interface ComplexFunction<R> {
>>     R apply(ComplexNumber c, ComplexResult<R> result);
>> }
>>
>>
>> The ComplexList need only provide a single object that acts as a
>> read/write
>> cursor over the data by implementing both interfaces ComplexNumber and
>> ComplexResult:
>>
>> // Internal class
>> class ComplexCursor implements ComplexNumber, ComplexResult<Void> {
>>     // Directly manipulated by the enclosing list
>>     int index;
>>
>>     @Override
>>     public Void apply(double r, double i) {
>>         ComplexList.this.r[index] = r;
>>         ComplexList.this.i[index] = i;
>>         return null;
>>     }
>>
>>     @Override
>>     public double real() {
>>         return ComplexList.this.r[index];
>>     }
>>
>>     @Override
>>     public double imag() {
>>         return ComplexList.this.i[index];
>>     }
>> }
>>
>> I can write the method:
>>
>> void forEach(ComplexFunction<Void> fun) {
>>     ComplexCursor cursor = new ComplexCursor();
>>     while (cursor.index < r.length) {
>>         fun.apply(cursor, cursor);
>>         cursor.index++;
>>     }
>> }
>>
>> And call it like this:
>>
>> class ComplexFunctions {
>>     static <R> R conj(ComplexNumber c, ComplexResult<R> r) {
>>         return r.apply(c.real(), -c.imag());
>>     }
>> }
>>
>>
>> ComplexList l;
>> l.forEach(ComplexFunctions::conj);
>>
>> // Using a lambda
>> l.forEach((c, result) -> {
>>     return result.apply(c.real(), -c.imag());
>> });
>>
>>
>> I've provided a few ideas there to get started. In summary here are some
>> requirements we should keep in mind:
>>
>> - Complex functions should be able to be declared with lambda functions
>> - The ComplexList allows read/write type operations on elements with
>> complex functions
>>
>> Alex
>>
>>
>> [1] http://www.open-std.org/JTC1/SC22/WG14/www/standards, specifically
>> WG14
>> N1256 sections 7.3 (p 170) and Annex G (p 467).
>>
>

Reply via email to