Hi, This lost the dev@commons in the to address. I am forwarding to the list to include the history.
On Sun, 20 Mar 2022 at 16:49, Sumanth Rajkumar <rajkumar.suma...@gmail.com> wrote: > Thanks for the feedback Alex! > > As suggested, I reviewed the JTransforms and ComplexUtils class in the > complex streams package. > > The existing complex utils class has methods to convert to and from Array > data structures (the forms used in JTransform) to Complex class. > > I can come up with a Java 8/Streams based API for implementing complex > FFT algorithms of the types in JTransforms and support various methods in > ComplexUtils > The streams based complex operations API should allow for decoupling the > backing data structures. > This should make it possible to use single API to create an unit test > suite to benchmark/compare different backing data structures such as single > arrays, floats or even polar representations > > As part of the project, I could implement a subset of the FFT operations > in JTransform using the new streams based Complex Numbers API and > benchmark it against JTransform implementation > > > I understand that we are in the GSOC discussion phase. I am trying to > understand the background of this project and the requirements in order to > come up with my GSOC proposal > > Can you provide with more information on the envisaged usage of > Commons-Numbers (especially Complex Numbers), its current usage/users and > the vision/roadmap for future enhancements > > Are we expecting complex-numbers to be an efficient pure java library that > could be used by other java libraries such as commons-imaging for data > compression (DCT /JPEG lossy compression)? > > Are there other Java/Non-Java (C/Python) libraries that provide similar > features that I can look into for design inspiration and also benchmark > Complex Numbers with? > > Thanks > Sumanth > > > On Tue, 15 Mar 2022 at 20:50, Alex Herbert <alex.d.herb...@gmail.com> > wrote: > >> Hi Sumanth, >> >> These changes to use static methods with functional interfaces is an >> improvement. However I would advise that we consider the use cases for this >> functionality to ensure that any design does not prevent extension and also >> allows full flexibility to achieve various tasks. >> >> For example: >> >> - multiply all the complex numbers in one list with another >> - wrap an existing complex number data structure, for example the FFT >> result produced by JTransforms [1] >> >> This project originates from a previous enhancement request that was made >> to store a large set of complex numbers efficiently. The argument was that >> the 16 bytes to store 2 doubles is inflated by the object allocation to >> store a Complex, perhaps by even double the 16 bytes. The natural storage >> would be two arrays of doubles, but what about 1 linear array packed as >> real/imag for each number. This will be able to store half as many numbers >> but access to each will take advantage of efficient caching when >> reading/writing memory. The JTransforms library (and others) may have ideas >> for useful data structures. >> >> Unfortunately I cannot find if there was a Jira ticket for this or it is >> only in the mailing archives. I've added links to the GSoC ticket for the >> other tickets that mention complex number array utils and streams. However >> these do not have a use case. Perhaps an investigation of the functionality >> in the unreleased commons-number-complex-streams package would be the place >> to start. The original author of that package is not actively involved in >> the development any more. >> >> I should also point out the process for GSoC. It is outlined here [2]. In >> short the initial period is about understanding what a project may involve. >> Then you create a proposal for the project that is ranked with all the >> other potential coders. Some projects are selected. It is only then that >> the formal coding process begins and you have 12 weeks to create some code. >> Previous years have had a timeline but this year the only date on the info >> page is April 4 - 19 for when applications open. So right now we are in the >> discussion phase. Any code developed now technically cannot be part of >> GSoC, although this is not strictly enforced. >> >> Regards, >> >> Alex >> >> [1] https://github.com/wendykierp/JTransforms >> [2] https://summerofcode.withgoogle.com/how-it-works >> >> >> On Tue, 15 Mar 2022 at 02:23, Sumanth Rajkumar < >> rajkumar.suma...@gmail.com> wrote: >> >>> Hi Alex/Gilles >>> >>> Thank you both for the detailed review. I think I have a better >>> understanding now. >>> >>> 1) Refactor using Functional Interfaces and move current instance >>> methods to static methods >>> >>> As suggested, I have attempted to refactor the Complex class to extract >>> the functions out to static methods and use Functional interfaces >>> >>> I have added following Complex Functional interfaces similar to >>> Functions and Operators defined in java.util.function but only using >>> primitive doubles and avoiding any Object creation overheads >>> >>> @FunctionalInterface >>> public interface ComplexFunction { >>> <R> R apply(double r, double i, ComplexResult<R> result); >>> } >>> >>> @FunctionalInterface >>> public interface ComplexBiFunction { >>> <R> R apply(double r1, double i1, double r2, double i2, >>> ComplexResult<R> result); >>> } >>> >>> >>> @FunctionalInterface >>> public interface ComplexResult<R> { >>> R apply(double r, double i); >>> default <V> ComplexResult<V> andThen(Function<? super R, ? extends >>> V> after) { >>> Objects.requireNonNull(after); >>> return (r, i) -> after.apply(apply(r, i)); >>> } >>> default ComplexResult<R> compose(ComplexFunction before) { >>> Objects.requireNonNull(before); >>> return (r, i) -> before.apply(r, i, (x, y) -> apply(x, y)); >>> } >>> } >>> >>> >>> >>> I have refactored a few Functions (exp, conj, asin) and a few >>> BFunctions (multiply, divide) as static functions into a new >>> ComplexFunctions class >>> >>> I have modified the existing implementations for above in the Complex >>> class to use the new static functions using the new Function interfaces >>> >>> I have also refactored ComplexList to apply the above function >>> interfaces in forEach methods as suggested. These apply the results in >>> place without incurring object overheads >>> >>> Refactored source is available here for review >>> >>> https://github.com/sumanth-rajkumar/commons-numbers/tree/sumanth-gsoc-22/commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex >>> >>> Please let me know if the above changes are on expected lines? >>> >>> 2) List Implementation >>> >>> Thanks for the feedback on the license issue. I should be able >>> refactor this as suggested just using the javadoc specifications >>> >>> >>> -Sumanth. >>> >>> >>> On Sun, 13 Mar 2022 at 21:39, Alex Herbert <alex.d.herb...@gmail.com> >>> wrote: >>> >>>> Hi, >>>> >>>> Thanks for your interest in Commons Numbers. >>>> >>>> On Mon, 14 Mar 2022 at 00:09, Gilles Sadowski <gillese...@gmail.com> >>>> wrote: >>>> >>>> > > >>>> > > My partial implementation (with TODOs for many operations) is >>>> available >>>> > > here. >>>> > > >>>> > >>>> https://github.com/sumanth-rajkumar/commons-numbers/blob/sumanth-gsoc-22/commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/ComplexList.java >>>> >>>> >>>> Thanks for the implementation idea. This is a literal implementation of >>>> a >>>> List<Complex>. I think we should take a step back and find use cases >>>> for a >>>> large set of complex numbers. That should drive the API design. >>>> >>>> For example a common operation with complex numbers is to conjugate >>>> multiply the fast fourier transform of two data arrays. The conjugate >>>> multiply in the frequency domain is equivalent to the correlation in the >>>> spatial domain. So I would require: >>>> >>>> ComplexList a; >>>> ComplexList b; >>>> >>>> a.conj().multiply(b); >>>> >>>> But what is the most appropriate method to do this? I do not think we >>>> want >>>> to implement full matrix functionality for multiplication of arrays. >>>> But we >>>> should allow an API that makes this type of work efficient in terms of >>>> memory usage, i.e. zero object allocation during computation (avoid >>>> creating Complex instances) and ideally no intermediate array >>>> allocation. >>>> So in the above I do not want to create an entire list of conjugates >>>> before >>>> multiplying by another complex number. I also want the option to write >>>> to a >>>> new array or back to the original one. >>>> >>>> So should we have some type of generic interface for an operation on a >>>> Complex: >>>> >>>> interface ComplexFunction { >>>> interface ComplexResult { >>>> void complex(double real, double imag); >>>> } >>>> void apply(double re, double im, ComplexResult); >>>> } >>>> >>>> Then a list to allow operations on elements in place. For example to >>>> compute the conjugate: >>>> >>>> ComplexList a; >>>> a.forEach((re, im, result) -> result.complex(re, -im)); >>>> >>>> All operations in the Complex class can be rewritten as static methods >>>> using a minimal set of functional interfaces. >>>> >>>> static void conj(double re, double im, ComplexResult result) { >>>> result.complex(re, -im); >>>> } >>>> >>>> The operation then becomes: >>>> >>>> ComplexList a; >>>> a.forEach(Complex::conj); >>>> >>>> Which is a bit less cumbersome to write. >>>> >>>> Operations could be chained using a 'andThen' method in the interface: >>>> >>>> ComplexList a; >>>> a.forEach(((ComplexFunction) Complex::conj).andThen(Complex::sin)); >>>> >>>> I've not considered exactly how this will work in practice. >>>> >>>> Functions that convert to a real number (such as abs()) could write 0 to >>>> the imaginary. >>>> >>>> The specifics will depend on all the operations in Complex. So a start >>>> point may be to refactor the class to expose all the instance methods as >>>> static methods that take input and write the result to a destination. >>>> These >>>> can be used by (all) the Complex instance methods. I say (all) as some >>>> may >>>> be more efficient to leave as is, namely the simple methods like negate >>>> or >>>> conjugate, and the operations with real numbers. >>>> >>>> Re: Your code implementation >>>> >>>> I notice that the implementation for the expandable array backed list >>>> and >>>> hash code generation are "heavily reliant" on the JDK (11?) source for >>>> ArrayList and Arrays. It is good practice to be aware of the license >>>> terms >>>> of any open source code project you may wish to use. The Oracle Open >>>> JDK license terms are here [1]. This is under the GNU GPL v2 license >>>> which >>>> is not permissible to be used in an ASF project [2]. In general it is >>>> fine >>>> to look at the JDK source code to see how the function works or find >>>> bugs. >>>> However any new code reimplementing the functionality should be done as >>>> if >>>> blind to the code and only using a contractual specification of what the >>>> code is meant to do. The modern javadoc effort by the JDK tries to >>>> provide >>>> a very good specification of each method contract. So it is often >>>> possible >>>> to write the same function from the javadoc description. An ideal >>>> javadoc >>>> should have this as its goal. Effectively this would be someone telling >>>> you >>>> what the code does but not showing you how: >>>> >>>> 1. A default array size of 10 >>>> 2. Backing array will grow using a factor of 50% up to the maximum array >>>> size >>>> 3. Overflow of the maximum array size will result in OutOfMemoryError >>>> 4. Index operations outside of the current list size [0, size) will >>>> throw >>>> ArrayIndexOutOfBoundException >>>> 5. etc >>>> >>>> Alex >>>> >>>> [1] http://openjdk.java.net/legal/gplv2+ce.html >>>> [2] https://www.apache.org/legal/resolved.html#category-x >>>> >>>