You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by Matt Juntunen <ma...@gmail.com> on 2022/03/11 15:18:02 UTC

[geometry] PointMap and PointSet

Hello,

I recently posted a PR [1] for GEOMETRY-142 [2], which is for adding
PointMap and PointSet implementations. These are Map and Set
implementations specifically designed to use Points as keys. They
support fuzzy key comparison, meaning that points do not have to be
exactly equal to each other in order to be considered equal by the
map/set. (Note that this means these types do not follow the strict
Map/Set contracts since they are not consistent with equals. This is
documented in the PointMap/PointSet javadocs.) I've completely hidden
the implementation details from the public API since I anticipate
changes in the future with regard to the algorithms used. Instances
are created through factory classes in each space. Ex:

PointMap<Vector3D, String> map = EuclideanCollections.pointMap3D(precision);
PointSet<Point2S> set = SphericalCollections.pointSet2S(precision);

Since fuzzy key comparison is used, I've added the following methods
to the interfaces to allow access to the exact, "canonical" version of
the key stored in the collection.

PointMap<P, V>  {
    // return the key corresponding to pt, or null if not found
    P resolveKey(P pt);

    // return the map entry corresponding to pt, or null if not found
    Map.Entry<P, V> resolveEntry(P pt);
}

PointSet<P> {
    // return the key corresponding to pt, or null if not found
    P resolve(P pt);
}

Reviews and comments are welcome.

Regards,
Matt Juntunen


[1] https://github.com/apache/commons-geometry/pull/194
[2] https://issues.apache.org/jira/projects/GEOMETRY/issues/GEOMETRY-142

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org


Re: Re: [geometry] PointMap and PointSet

Posted by Matt Juntunen <ma...@gmail.com>.
Hello,

This kind of API sounds useful but not directly related to the
PointMap/Set types. As you mentioned, it seems like the API would most
likely use PointMap/Set internally. I believe the next step should be
to create a JIRA issue and figure out the details there.

Since it sounds like there are no objections to the current
PointMap/Set PR, I'm going to go ahead and merge it in and then
continue on with the closest point/farthest point API (GEOMETRY-146).

Regards,
Matt

On Wed, Mar 23, 2022 at 12:58 PM Gilles Sadowski <gi...@gmail.com> wrote:
>
> Hi.
>
> Le mer. 23 mars 2022 à 03:27, Matt Juntunen
> <ma...@gmail.com> a écrit :
> >
> > Gilles,
> >
> > > Say, for example, that "V" holds a single (floating-point) value.  We
> > > insert entries
> > >  map.put(x, 2);
> > >  map.put(y, 8);
> > > assuming that "x" and "y" and just barely different, according to the
> > > chosen "precision context".  Then:
> > > z = (x + y) / 2; // Pseudo-code.
> >  > m = map.get(z);
> > > Does "m" equal "2" or "8", depending on whether "z" is (however
> > > slightly) closer to either "x" or "y"?  Or is it "5" (interpolated value)?
> >
> > It would be either 2 or 8. In the current implementations the first
> > matching entry is returned and since entries are typically searched
> > low to high, the entry corresponding to the lower of the two keys
> > would be returned. However, I do not consider this "lowest match wins"
> > behavior to be part of the public API since it depends on the
> > implementation details.
>
> For sure, any functionality must start from some low-level data
> structure with some prescribed behaviour.  Here, we assume that
> the "mechanism" returns "2" or "8" (depending on the "details").
>
> My point is rather that the "cloud of points" abstraction seems to
> require a higher-level API (for which "PointMap" would, in turn,
> be an "implementation detail" too).
> Within that abstraction, querying the value at location between
> "x" and "y" would return some interpolation (i.e. any user-defined
> "combiner") of the data stored within a given "radius" of the
> queried location.
> This would make more sense (IMO) than the application having
> to deal with a result ("2" or "8") that is implementation-dependent:
> Such an additional API layer would allow the caller to specify the
> "combiner" as, for example, "the average of the values", the result
> would then be univocally defined ("5").
> [Obviously, when specifying a radius smaller than the "precision
> context", the behaviour would be identical to a direct query to the
> underlying "PointMap".]
>
> >
> > > But is it the right behaviour in all cases, or should there be a
> > > "replacement policy" (to apply whenever points are already stored
> > > within the "precision context" neighbourhood)?
> >
> > This seems to me like additional logic that could be built on top of
> > PointMap/Set, probably using the distance query methods in
> > GEOMETRY-146.
>
> Indeed; my question aimed at pointing to the importance of providing
> such an API.
>
> > Do you have a use case in mind here?
>
> In 2D: create an image (i.e. rectangular regular grid) that represents
> the (interpolated) data associated with the (scattered) points.
>
> Another (unrelated to the above discussion) feature: Allow different
> precision contexts in different regions of the space (cf. [1]).
>
> Best,
> Gilles
>
> [1] https://en.wikipedia.org/wiki/Unstructured_grid
>
> >
> > Regards,
> > Matt
> >
> > On Tue, Mar 22, 2022 at 1:05 PM Gilles Sadowski <gi...@gmail.com> wrote:
> > >
> > > Le mar. 22 mars 2022 à 14:46, Matt Juntunen
> > > <ma...@gmail.com> a écrit :
> > > >
> > > > Hello,
> > > >
> > > > Unless there are any other comments on the PR, I'm going to plan on
> > > > merging it into master within the next couple of days.
> > > >
> > >
> > > Thanks for providing this new functionality.
> > >
> > > Do you envision that [Geometry] will also provide ways to manipulate
> > > data stored in the map (the "V" in e.g. "PointMap<Vector3D, V>")?
> > >
> > > Say, for example, that "V" holds a single (floating-point) value.  We
> > > insert entries
> > >   map.put(x, 2);
> > >   map.put(y, 8);
> > > assuming that "x" and "y" and just barely different, according to the
> > > chosen "precision context".  Then:
> > >   z = (x + y) / 2; // Pseudo-code.
> > >   m = map.get(z);
> > > Does "m" equal "2" or "8", depending on whether "z" is (however
> > > slightly) closer to either "x" or "y"?  Or is it "5" (interpolated value)?
> > >
> > > This is related to the feature which I mentioned in GEOMETRY-146.
> > > I get that the low-level data-structure cannot "make up" a value that
> > > is not actually stored but it seems that the next step would be an API
> > > that lets the user specify what it means to retrieve data from the map.
> > >
> > > Then, there is also
> > >   map.put(z, 10);
> > > Currently "10" will replace either the value at "x" or the value at "y".
> > > But is it the right behaviour in all cases, or should there be a
> > > "replacement policy" (to apply whenever points are already stored
> > > within the "precision context" neighbourhood)?
> > >
> > > Does this make sense?
> > >
> > > Gilles
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
> For additional commands, e-mail: dev-help@commons.apache.org
>

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org


Re: Re: [geometry] PointMap and PointSet

Posted by Gilles Sadowski <gi...@gmail.com>.
Hi.

Le mer. 23 mars 2022 à 03:27, Matt Juntunen
<ma...@gmail.com> a écrit :
>
> Gilles,
>
> > Say, for example, that "V" holds a single (floating-point) value.  We
> > insert entries
> >  map.put(x, 2);
> >  map.put(y, 8);
> > assuming that "x" and "y" and just barely different, according to the
> > chosen "precision context".  Then:
> > z = (x + y) / 2; // Pseudo-code.
>  > m = map.get(z);
> > Does "m" equal "2" or "8", depending on whether "z" is (however
> > slightly) closer to either "x" or "y"?  Or is it "5" (interpolated value)?
>
> It would be either 2 or 8. In the current implementations the first
> matching entry is returned and since entries are typically searched
> low to high, the entry corresponding to the lower of the two keys
> would be returned. However, I do not consider this "lowest match wins"
> behavior to be part of the public API since it depends on the
> implementation details.

For sure, any functionality must start from some low-level data
structure with some prescribed behaviour.  Here, we assume that
the "mechanism" returns "2" or "8" (depending on the "details").

My point is rather that the "cloud of points" abstraction seems to
require a higher-level API (for which "PointMap" would, in turn,
be an "implementation detail" too).
Within that abstraction, querying the value at location between
"x" and "y" would return some interpolation (i.e. any user-defined
"combiner") of the data stored within a given "radius" of the
queried location.
This would make more sense (IMO) than the application having
to deal with a result ("2" or "8") that is implementation-dependent:
Such an additional API layer would allow the caller to specify the
"combiner" as, for example, "the average of the values", the result
would then be univocally defined ("5").
[Obviously, when specifying a radius smaller than the "precision
context", the behaviour would be identical to a direct query to the
underlying "PointMap".]

>
> > But is it the right behaviour in all cases, or should there be a
> > "replacement policy" (to apply whenever points are already stored
> > within the "precision context" neighbourhood)?
>
> This seems to me like additional logic that could be built on top of
> PointMap/Set, probably using the distance query methods in
> GEOMETRY-146.

Indeed; my question aimed at pointing to the importance of providing
such an API.

> Do you have a use case in mind here?

In 2D: create an image (i.e. rectangular regular grid) that represents
the (interpolated) data associated with the (scattered) points.

Another (unrelated to the above discussion) feature: Allow different
precision contexts in different regions of the space (cf. [1]).

Best,
Gilles

[1] https://en.wikipedia.org/wiki/Unstructured_grid

>
> Regards,
> Matt
>
> On Tue, Mar 22, 2022 at 1:05 PM Gilles Sadowski <gi...@gmail.com> wrote:
> >
> > Le mar. 22 mars 2022 à 14:46, Matt Juntunen
> > <ma...@gmail.com> a écrit :
> > >
> > > Hello,
> > >
> > > Unless there are any other comments on the PR, I'm going to plan on
> > > merging it into master within the next couple of days.
> > >
> >
> > Thanks for providing this new functionality.
> >
> > Do you envision that [Geometry] will also provide ways to manipulate
> > data stored in the map (the "V" in e.g. "PointMap<Vector3D, V>")?
> >
> > Say, for example, that "V" holds a single (floating-point) value.  We
> > insert entries
> >   map.put(x, 2);
> >   map.put(y, 8);
> > assuming that "x" and "y" and just barely different, according to the
> > chosen "precision context".  Then:
> >   z = (x + y) / 2; // Pseudo-code.
> >   m = map.get(z);
> > Does "m" equal "2" or "8", depending on whether "z" is (however
> > slightly) closer to either "x" or "y"?  Or is it "5" (interpolated value)?
> >
> > This is related to the feature which I mentioned in GEOMETRY-146.
> > I get that the low-level data-structure cannot "make up" a value that
> > is not actually stored but it seems that the next step would be an API
> > that lets the user specify what it means to retrieve data from the map.
> >
> > Then, there is also
> >   map.put(z, 10);
> > Currently "10" will replace either the value at "x" or the value at "y".
> > But is it the right behaviour in all cases, or should there be a
> > "replacement policy" (to apply whenever points are already stored
> > within the "precision context" neighbourhood)?
> >
> > Does this make sense?
> >
> > Gilles

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org


Re: Re: [geometry] PointMap and PointSet

Posted by Matt Juntunen <ma...@gmail.com>.
Gilles,

> Say, for example, that "V" holds a single (floating-point) value.  We
> insert entries
>  map.put(x, 2);
>  map.put(y, 8);
> assuming that "x" and "y" and just barely different, according to the
> chosen "precision context".  Then:
> z = (x + y) / 2; // Pseudo-code.
 > m = map.get(z);
> Does "m" equal "2" or "8", depending on whether "z" is (however
> slightly) closer to either "x" or "y"?  Or is it "5" (interpolated value)?

It would be either 2 or 8. In the current implementations the first
matching entry is returned and since entries are typically searched
low to high, the entry corresponding to the lower of the two keys
would be returned. However, I do not consider this "lowest match wins"
behavior to be part of the public API since it depends on the
implementation details.

> But is it the right behaviour in all cases, or should there be a
> "replacement policy" (to apply whenever points are already stored
> within the "precision context" neighbourhood)?

This seems to me like additional logic that could be built on top of
PointMap/Set, probably using the distance query methods in
GEOMETRY-146. Do you have a use case in mind here?

Regards,
Matt

On Tue, Mar 22, 2022 at 1:05 PM Gilles Sadowski <gi...@gmail.com> wrote:
>
> Le mar. 22 mars 2022 à 14:46, Matt Juntunen
> <ma...@gmail.com> a écrit :
> >
> > Hello,
> >
> > Unless there are any other comments on the PR, I'm going to plan on
> > merging it into master within the next couple of days.
> >
>
> Thanks for providing this new functionality.
>
> Do you envision that [Geometry] will also provide ways to manipulate
> data stored in the map (the "V" in e.g. "PointMap<Vector3D, V>")?
>
> Say, for example, that "V" holds a single (floating-point) value.  We
> insert entries
>   map.put(x, 2);
>   map.put(y, 8);
> assuming that "x" and "y" and just barely different, according to the
> chosen "precision context".  Then:
>   z = (x + y) / 2; // Pseudo-code.
>   m = map.get(z);
> Does "m" equal "2" or "8", depending on whether "z" is (however
> slightly) closer to either "x" or "y"?  Or is it "5" (interpolated value)?
>
> This is related to the feature which I mentioned in GEOMETRY-146.
> I get that the low-level data-structure cannot "make up" a value that
> is not actually stored but it seems that the next step would be an API
> that lets the user specify what it means to retrieve data from the map.
>
> Then, there is also
>   map.put(z, 10);
> Currently "10" will replace either the value at "x" or the value at "y".
> But is it the right behaviour in all cases, or should there be a
> "replacement policy" (to apply whenever points are already stored
> within the "precision context" neighbourhood)?
>
> Does this make sense?
>
> Gilles
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
> For additional commands, e-mail: dev-help@commons.apache.org
>

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org


Re: Re: [geometry] PointMap and PointSet

Posted by Gilles Sadowski <gi...@gmail.com>.
Le mar. 22 mars 2022 à 14:46, Matt Juntunen
<ma...@gmail.com> a écrit :
>
> Hello,
>
> Unless there are any other comments on the PR, I'm going to plan on
> merging it into master within the next couple of days.
>

Thanks for providing this new functionality.

Do you envision that [Geometry] will also provide ways to manipulate
data stored in the map (the "V" in e.g. "PointMap<Vector3D, V>")?

Say, for example, that "V" holds a single (floating-point) value.  We
insert entries
  map.put(x, 2);
  map.put(y, 8);
assuming that "x" and "y" and just barely different, according to the
chosen "precision context".  Then:
  z = (x + y) / 2; // Pseudo-code.
  m = map.get(z);
Does "m" equal "2" or "8", depending on whether "z" is (however
slightly) closer to either "x" or "y"?  Or is it "5" (interpolated value)?

This is related to the feature which I mentioned in GEOMETRY-146.
I get that the low-level data-structure cannot "make up" a value that
is not actually stored but it seems that the next step would be an API
that lets the user specify what it means to retrieve data from the map.

Then, there is also
  map.put(z, 10);
Currently "10" will replace either the value at "x" or the value at "y".
But is it the right behaviour in all cases, or should there be a
"replacement policy" (to apply whenever points are already stored
within the "precision context" neighbourhood)?

Does this make sense?

Gilles

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org


Re: Re: [geometry] PointMap and PointSet

Posted by Matt Juntunen <ma...@gmail.com>.
Hello,

Unless there are any other comments on the PR, I'm going to plan on
merging it into master within the next couple of days.

Regards,
Matt

On Sun, Mar 20, 2022 at 11:39 AM Matt Juntunen
<ma...@gmail.com> wrote:
>
> Hi Eric,
>
> > Would Java’s Map.entrySet provide the “getEntry” type method needed?
> > https://docs.oracle.com/javase/8/docs/api/java/util/Map.html#entrySet--
> > Or would this provide all entry’s and still need to find the specific entry so maybe a forEach variation to filter for a specific entry?
> > https://docs.oracle.com/javase/8/docs/api/java/util/Map.html#forEach-java.util.function.BiConsumer-
>
> This would work but it would require iterating through half of the set
> on average in order to find the correct entry, meaning we would lose
> the performance benefits of the spatial data structure. The
> PointMap.getEntry() method currently in the PR avoids this iteration
> requirement.
>
> Regards,
> Matt
>
> On Sun, Mar 20, 2022 at 9:13 AM Eric Bresie <eb...@gmail.com> wrote:
> >
> >
> >
> >
> > On March 14, 2022 at 10:19:14 AM CDT, Matt Juntunen <ma...@gmail.com> wrote:
> >
> > I'm a little bit confused: Isn't it always the case that
> >
> > getEntry(p).getKey()
> > will return the originally inserted (i.e. "canonical") point (i.e. not "p")?
> >
> > Map does not contain a "getEntry" method. If it did, that would indeed
> > be preferable.
> >
> >
> > Would Java’s Map.entrySet provide the “getEntry” type method needed?
> >
> > https://docs.oracle.com/javase/8/docs/api/java/util/Map.html#entrySet--
> >
> > Or would this provide all entry’s and still need to find the specific entry so maybe a forEach variation to filter for a specific entry?
> > https://docs.oracle.com/javase/8/docs/api/java/util/Map.html#forEach-java.util.function.BiConsumer-
> >
> > Unless I'm missing a standard use-case, the specialized methods
> >
> > "closestFirst" and "farthestFirst" don't seem useful (and wasteful
> > of computing resources: If iterating over the whole set, why would
> > one want to start from some particular point?).
> >
> >
> > Eric Bresie
> > Ebresie@gmail.com
> >
> >

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org


Re: Re: [geometry] PointMap and PointSet

Posted by Matt Juntunen <ma...@gmail.com>.
Hi Eric,

> Would Java’s Map.entrySet provide the “getEntry” type method needed?
> https://docs.oracle.com/javase/8/docs/api/java/util/Map.html#entrySet--
> Or would this provide all entry’s and still need to find the specific entry so maybe a forEach variation to filter for a specific entry?
> https://docs.oracle.com/javase/8/docs/api/java/util/Map.html#forEach-java.util.function.BiConsumer-

This would work but it would require iterating through half of the set
on average in order to find the correct entry, meaning we would lose
the performance benefits of the spatial data structure. The
PointMap.getEntry() method currently in the PR avoids this iteration
requirement.

Regards,
Matt

On Sun, Mar 20, 2022 at 9:13 AM Eric Bresie <eb...@gmail.com> wrote:
>
>
>
>
> On March 14, 2022 at 10:19:14 AM CDT, Matt Juntunen <ma...@gmail.com> wrote:
>
> I'm a little bit confused: Isn't it always the case that
>
> getEntry(p).getKey()
> will return the originally inserted (i.e. "canonical") point (i.e. not "p")?
>
> Map does not contain a "getEntry" method. If it did, that would indeed
> be preferable.
>
>
> Would Java’s Map.entrySet provide the “getEntry” type method needed?
>
> https://docs.oracle.com/javase/8/docs/api/java/util/Map.html#entrySet--
>
> Or would this provide all entry’s and still need to find the specific entry so maybe a forEach variation to filter for a specific entry?
> https://docs.oracle.com/javase/8/docs/api/java/util/Map.html#forEach-java.util.function.BiConsumer-
>
> Unless I'm missing a standard use-case, the specialized methods
>
> "closestFirst" and "farthestFirst" don't seem useful (and wasteful
> of computing resources: If iterating over the whole set, why would
> one want to start from some particular point?).
>
>
> Eric Bresie
> Ebresie@gmail.com
>
>

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org


Re: Re: [geometry] PointMap and PointSet

Posted by Eric Bresie <eb...@gmail.com>.

>
> On March 14, 2022 at 10:19:14 AM CDT, Matt Juntunen <matt.a.juntunen@gmail.com (mailto:matt.a.juntunen@gmail.com)> wrote:
>
> > I'm a little bit confused: Isn't it always the case that
> getEntry(p).getKey()
> will return the originally inserted (i.e. "canonical") point (i.e. not "p")?
>
> Map does not contain a "getEntry" method. If it did, that would indeed
> be preferable.

Would Java’s Map.entrySet provide the “getEntry” type method needed?

https://docs.oracle.com/javase/8/docs/api/java/util/Map.html#entrySet--

Or would this provide all entry’s and still need to find the specific entry so maybe a forEach variation to filter for a specific entry?
https://docs.oracle.com/javase/8/docs/api/java/util/Map.html#forEach-java.util.function.BiConsumer-

> > Unless I'm missing a standard use-case, the specialized methods
> "closestFirst" and "farthestFirst" don't seem useful (and wasteful
> of computing resources: If iterating over the whole set, why would
> one want to start from some particular point?).

Eric Bresie
Ebresie@gmail.com
>

Re: [geometry] PointMap and PointSet

Posted by Matt Juntunen <ma...@gmail.com>.
Hello,

I've updated the PR [1] with the following changes:
- made the Map.Entry instance returned by PointMap.getEntry() support
the setValue() method for all spaces and dimensions
- created an abstract base class to share code between the 1D PointMap
implementations
- fixed a bug in the 1D spherical point map remove() method
- added additional unit tests

Supporting setValue() in the multidimensional cases was trivial since
I simply returned the actual SimpleEntry instance stored in the map
instead of an immutable wrapper instance. For the 1D cases, I made
getEntry() return a wrapper instance that calls Map.replace() on the
underlying TreeMap when the value is changed. This forces another key
look up but I believe that the small overhead is worth it in order to
provide a consistent PointMap API. In any case,  the overall
performance is not affected since calling put() or replace() is the
only method available to modify a TreeMap entry (outside of using the
entrySet).

Let me know what you think.

Regards,
Matt

[1] https://github.com/apache/commons-geometry/pull/194

On Wed, Mar 16, 2022 at 12:30 PM Gilles Sadowski <gi...@gmail.com> wrote:
>
> Hi.
>
> Le mer. 16 mars 2022 à 15:42, Matt Juntunen
> <ma...@gmail.com> a écrit :
> >
> > Hello,
> >
> > > I suggest to carefully consider whether to return a "SimpleEntry"
> > (and prominently note that any sort of concurrent modification is
> > a caller responsibility).
> >
> > I see what you mean and I think that would be a good idea. However,
> > the sticking point is that the 1D implementations for both Euclidean
> > and spherical space internally use the JDK's TreeMap class to store
> > entries, due to its superior performance when compared to the
> > AbstractBucketPointMap class used for other dimensions. TreeMap
> > returns immutable Map.Entry instances from its entry-access methods
> > (e.g., ceilingEntry, floorEntry),
>
> The apidocs[1] state:
> ---CUT---
> Map.Entry<K,V> ceilingEntry(K key)
>    Returns a key-value mapping associated with the least key greater
> than or equal to the given key, or null if there is no such key.
> ---CUT---
>
> > so there is not a straightforward
> > way for us to implement the behavior you propose for these dimensions.
>
> AFAICT, (im)mutability of the returned entry is not part of the
> JDK-mandated API.
> So, assuming that the behaviour is implementation-dependent,
> it can be chosen to be different for different dimensions on the
> basis of which behaviour is most "natural" for applications.
>
> > The options I see are:
> > 1. Have all returned entries be immutable (the current behavior).
> > 2. Return specialized Map.Entry implementations for the 1D cases that
> > call the "put" method on the underlying map when "setValue" is called.
> >
> > Option #2 seems less than ideal so unless there is another approach
> > that I'm missing, I vote for #1.
>
> I agree that the situation is somewhat unsatisfying.  But, as said, I'd
> favour #1 only if there were an actual security promise.  Otherwise,
> immutability is a false claim.
> Unless I'm mistaken, calling "put" in order to update the "value" is
> necessarily less performant than calling "setValue" (map search in
> the former, no-op in the latter).
>
> Regards,
> Gilles
>
> [1] https://docs.oracle.com/javase/8/docs/api/java/util/TreeMap.html#ceilingEntry-K-
>
> > Regards,
> > Matt
> >
> >
> > On Wed, Mar 16, 2022 at 9:48 AM Gilles Sadowski <gi...@gmail.com> wrote:
> > >
> > > Hi.
> > >
> > > Le mer. 16 mars 2022 à 03:17, Matt Juntunen
> > > <ma...@gmail.com> a écrit :
> > > >
> > > > Hello,
> > > >
> > > > I've made the following changes to the PR:
> > > > - removed the "resolveKey" method from PointMap
> > > > - renamed PointMap.resolveEntry to PointMap.getEntry and
> > > > PointSet.resolve to PointSet.get
> > > > - added an entry on PointMap/PointSet to the user guide
> > > > - addressed Github comments (thanks, Bruno!)
> > > >
> > > > I ran some performance tests regarding the immutable entry instance
> > > > created in the PointMap.getEntry method and there seems to be no
> > > > impact.
> > > >
> > > > > Furthermore, what is actually meant here by "immutable
> > > > instance" (since the "value" could be mutable without the
> > > > map being aware of the fact)?
> > > >
> > > > It is immutable in that the object reference used as the entry value
> > > > cannot be changed. This reference could point to a mutable object.
> > > > This is the same behavior as other Map implementations.
> > >
> > > I don't see that "reference immutability" is mandated by the
> > > "Map" interface (see e.g. [1]).
> > >
> > > I've noted many times that I generally favour (true) immutability:
> > > It makes much sense for "small" data-structures (e.g. for future
> > > potential optimizations[2]).
> > >
> > > However, the "cloud of points" data-structure is at the opposite
> > > of the spectrum from this POV:  It is intended to contain a large
> > > number of points whose "key" should indeed be (truly) immutable
> > > but whose value would likely need to be mutable for many actual
> > > use cases.
> > > If a "SimpleImmutableEntry" is returned, then in order to modify
> > > the map's "value" contents, one has to (IIUC)
> > >  * retrieve the entry,
> > >  * create a new value,
> > >  * call "put" (on the map)
> > > rather than
> > >  * retrieve the entry
> > >  * call "setValue" (on the entry).
> > > So we have a somewhat crippled API that does not bring any
> > > advantage since reference immutability doesn't provide any
> > > security to the map's user (any other caller who is being passed
> > > the same map, is able to change its contents anyways).
> > >
> > > I suggest to carefully consider whether to return a "SimpleEntry"
> > > (and prominently note that any sort of concurrent modification is
> > > a caller responsibility).
> > >
> > > Regards,
> > > Gilles
> > >
> > > [1] https://docs.oracle.com/javase/8/docs/api/java/util/HashMap.html#entrySet--
> > > [2] https://cr.openjdk.java.net/~briangoetz/valhalla/sov/02-object-model.html
> > >
> > > >>> [...]
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
> For additional commands, e-mail: dev-help@commons.apache.org
>

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org


Re: [geometry] PointMap and PointSet

Posted by Gilles Sadowski <gi...@gmail.com>.
Hi.

Le mer. 16 mars 2022 à 15:42, Matt Juntunen
<ma...@gmail.com> a écrit :
>
> Hello,
>
> > I suggest to carefully consider whether to return a "SimpleEntry"
> (and prominently note that any sort of concurrent modification is
> a caller responsibility).
>
> I see what you mean and I think that would be a good idea. However,
> the sticking point is that the 1D implementations for both Euclidean
> and spherical space internally use the JDK's TreeMap class to store
> entries, due to its superior performance when compared to the
> AbstractBucketPointMap class used for other dimensions. TreeMap
> returns immutable Map.Entry instances from its entry-access methods
> (e.g., ceilingEntry, floorEntry),

The apidocs[1] state:
---CUT---
Map.Entry<K,V> ceilingEntry(K key)
   Returns a key-value mapping associated with the least key greater
than or equal to the given key, or null if there is no such key.
---CUT---

> so there is not a straightforward
> way for us to implement the behavior you propose for these dimensions.

AFAICT, (im)mutability of the returned entry is not part of the
JDK-mandated API.
So, assuming that the behaviour is implementation-dependent,
it can be chosen to be different for different dimensions on the
basis of which behaviour is most "natural" for applications.

> The options I see are:
> 1. Have all returned entries be immutable (the current behavior).
> 2. Return specialized Map.Entry implementations for the 1D cases that
> call the "put" method on the underlying map when "setValue" is called.
>
> Option #2 seems less than ideal so unless there is another approach
> that I'm missing, I vote for #1.

I agree that the situation is somewhat unsatisfying.  But, as said, I'd
favour #1 only if there were an actual security promise.  Otherwise,
immutability is a false claim.
Unless I'm mistaken, calling "put" in order to update the "value" is
necessarily less performant than calling "setValue" (map search in
the former, no-op in the latter).

Regards,
Gilles

[1] https://docs.oracle.com/javase/8/docs/api/java/util/TreeMap.html#ceilingEntry-K-

> Regards,
> Matt
>
>
> On Wed, Mar 16, 2022 at 9:48 AM Gilles Sadowski <gi...@gmail.com> wrote:
> >
> > Hi.
> >
> > Le mer. 16 mars 2022 à 03:17, Matt Juntunen
> > <ma...@gmail.com> a écrit :
> > >
> > > Hello,
> > >
> > > I've made the following changes to the PR:
> > > - removed the "resolveKey" method from PointMap
> > > - renamed PointMap.resolveEntry to PointMap.getEntry and
> > > PointSet.resolve to PointSet.get
> > > - added an entry on PointMap/PointSet to the user guide
> > > - addressed Github comments (thanks, Bruno!)
> > >
> > > I ran some performance tests regarding the immutable entry instance
> > > created in the PointMap.getEntry method and there seems to be no
> > > impact.
> > >
> > > > Furthermore, what is actually meant here by "immutable
> > > instance" (since the "value" could be mutable without the
> > > map being aware of the fact)?
> > >
> > > It is immutable in that the object reference used as the entry value
> > > cannot be changed. This reference could point to a mutable object.
> > > This is the same behavior as other Map implementations.
> >
> > I don't see that "reference immutability" is mandated by the
> > "Map" interface (see e.g. [1]).
> >
> > I've noted many times that I generally favour (true) immutability:
> > It makes much sense for "small" data-structures (e.g. for future
> > potential optimizations[2]).
> >
> > However, the "cloud of points" data-structure is at the opposite
> > of the spectrum from this POV:  It is intended to contain a large
> > number of points whose "key" should indeed be (truly) immutable
> > but whose value would likely need to be mutable for many actual
> > use cases.
> > If a "SimpleImmutableEntry" is returned, then in order to modify
> > the map's "value" contents, one has to (IIUC)
> >  * retrieve the entry,
> >  * create a new value,
> >  * call "put" (on the map)
> > rather than
> >  * retrieve the entry
> >  * call "setValue" (on the entry).
> > So we have a somewhat crippled API that does not bring any
> > advantage since reference immutability doesn't provide any
> > security to the map's user (any other caller who is being passed
> > the same map, is able to change its contents anyways).
> >
> > I suggest to carefully consider whether to return a "SimpleEntry"
> > (and prominently note that any sort of concurrent modification is
> > a caller responsibility).
> >
> > Regards,
> > Gilles
> >
> > [1] https://docs.oracle.com/javase/8/docs/api/java/util/HashMap.html#entrySet--
> > [2] https://cr.openjdk.java.net/~briangoetz/valhalla/sov/02-object-model.html
> >
> > >>> [...]

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org


Re: [geometry] PointMap and PointSet

Posted by Alex Herbert <al...@gmail.com>.
On Wed, 16 Mar 2022 at 14:42, Matt Juntunen <ma...@gmail.com>
wrote:

> Hello,
>
> > I suggest to carefully consider whether to return a "SimpleEntry"
> (and prominently note that any sort of concurrent modification is
> a caller responsibility).
>
> I see what you mean and I think that would be a good idea. However,
> the sticking point is that the 1D implementations for both Euclidean
> and spherical space internally use the JDK's TreeMap class to store
> entries, due to its superior performance when compared to the
> AbstractBucketPointMap class used for other dimensions. TreeMap
> returns immutable Map.Entry instances from its entry-access methods
> (e.g., ceilingEntry, floorEntry), so there is not a straightforward
> way for us to implement the behavior you propose for these dimensions.
> The options I see are:
> 1. Have all returned entries be immutable (the current behavior).
> 2. Return specialized Map.Entry implementations for the 1D cases that
> call the "put" method on the underlying map when "setValue" is called.
>
> Option #2 seems less than ideal so unless there is another approach
> that I'm missing, I vote for #1.
>
>
Option #3

Since the 1D TreeMap is hidden, you store entries in it as an
updatable reference, e.g. 'new Object[1]'. You can then get the immutable
entry from the TreeMap and update the contents stored in the immutable
object array without ever having to call set on the TreeMap after
modification:

class Hidden<K, V> {
    TreeMap<K, Object[]> map = new TreeMap<>();

    V put(K k, V v) {
        if (map.isEmpty()) {
            map.put(k, new Object[] {v});
            return null;
        }
        Entry<K, Object[]> e = map.ceilingEntry(k);
        Object[] o = e.getValue();
        // ugly
        @SuppressWarnings("unchecked")
        V old = (V) o[0];
        o[0] = v;
        return old;
    }
}

This will be memory inefficient as each entry in the TreeMap has an
additional pointer.

Depending on what your API is then other methods requiring indirection will
also be inefficient. However you can provide methods to bulk search the
collection with minimal object allocation (forEach) or to do bulk changes
(replaceAll).

Alex

Re: [geometry] PointMap and PointSet

Posted by Matt Juntunen <ma...@gmail.com>.
Hello,

> I suggest to carefully consider whether to return a "SimpleEntry"
(and prominently note that any sort of concurrent modification is
a caller responsibility).

I see what you mean and I think that would be a good idea. However,
the sticking point is that the 1D implementations for both Euclidean
and spherical space internally use the JDK's TreeMap class to store
entries, due to its superior performance when compared to the
AbstractBucketPointMap class used for other dimensions. TreeMap
returns immutable Map.Entry instances from its entry-access methods
(e.g., ceilingEntry, floorEntry), so there is not a straightforward
way for us to implement the behavior you propose for these dimensions.
The options I see are:
1. Have all returned entries be immutable (the current behavior).
2. Return specialized Map.Entry implementations for the 1D cases that
call the "put" method on the underlying map when "setValue" is called.

Option #2 seems less than ideal so unless there is another approach
that I'm missing, I vote for #1.

Regards,
Matt


On Wed, Mar 16, 2022 at 9:48 AM Gilles Sadowski <gi...@gmail.com> wrote:
>
> Hi.
>
> Le mer. 16 mars 2022 à 03:17, Matt Juntunen
> <ma...@gmail.com> a écrit :
> >
> > Hello,
> >
> > I've made the following changes to the PR:
> > - removed the "resolveKey" method from PointMap
> > - renamed PointMap.resolveEntry to PointMap.getEntry and
> > PointSet.resolve to PointSet.get
> > - added an entry on PointMap/PointSet to the user guide
> > - addressed Github comments (thanks, Bruno!)
> >
> > I ran some performance tests regarding the immutable entry instance
> > created in the PointMap.getEntry method and there seems to be no
> > impact.
> >
> > > Furthermore, what is actually meant here by "immutable
> > instance" (since the "value" could be mutable without the
> > map being aware of the fact)?
> >
> > It is immutable in that the object reference used as the entry value
> > cannot be changed. This reference could point to a mutable object.
> > This is the same behavior as other Map implementations.
>
> I don't see that "reference immutability" is mandated by the
> "Map" interface (see e.g. [1]).
>
> I've noted many times that I generally favour (true) immutability:
> It makes much sense for "small" data-structures (e.g. for future
> potential optimizations[2]).
>
> However, the "cloud of points" data-structure is at the opposite
> of the spectrum from this POV:  It is intended to contain a large
> number of points whose "key" should indeed be (truly) immutable
> but whose value would likely need to be mutable for many actual
> use cases.
> If a "SimpleImmutableEntry" is returned, then in order to modify
> the map's "value" contents, one has to (IIUC)
>  * retrieve the entry,
>  * create a new value,
>  * call "put" (on the map)
> rather than
>  * retrieve the entry
>  * call "setValue" (on the entry).
> So we have a somewhat crippled API that does not bring any
> advantage since reference immutability doesn't provide any
> security to the map's user (any other caller who is being passed
> the same map, is able to change its contents anyways).
>
> I suggest to carefully consider whether to return a "SimpleEntry"
> (and prominently note that any sort of concurrent modification is
> a caller responsibility).
>
> Regards,
> Gilles
>
> [1] https://docs.oracle.com/javase/8/docs/api/java/util/HashMap.html#entrySet--
> [2] https://cr.openjdk.java.net/~briangoetz/valhalla/sov/02-object-model.html
>
> >>> [...]
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
> For additional commands, e-mail: dev-help@commons.apache.org
>

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org


Re: [geometry] PointMap and PointSet

Posted by Gilles Sadowski <gi...@gmail.com>.
Hi.

Le mer. 16 mars 2022 à 03:17, Matt Juntunen
<ma...@gmail.com> a écrit :
>
> Hello,
>
> I've made the following changes to the PR:
> - removed the "resolveKey" method from PointMap
> - renamed PointMap.resolveEntry to PointMap.getEntry and
> PointSet.resolve to PointSet.get
> - added an entry on PointMap/PointSet to the user guide
> - addressed Github comments (thanks, Bruno!)
>
> I ran some performance tests regarding the immutable entry instance
> created in the PointMap.getEntry method and there seems to be no
> impact.
>
> > Furthermore, what is actually meant here by "immutable
> instance" (since the "value" could be mutable without the
> map being aware of the fact)?
>
> It is immutable in that the object reference used as the entry value
> cannot be changed. This reference could point to a mutable object.
> This is the same behavior as other Map implementations.

I don't see that "reference immutability" is mandated by the
"Map" interface (see e.g. [1]).

I've noted many times that I generally favour (true) immutability:
It makes much sense for "small" data-structures (e.g. for future
potential optimizations[2]).

However, the "cloud of points" data-structure is at the opposite
of the spectrum from this POV:  It is intended to contain a large
number of points whose "key" should indeed be (truly) immutable
but whose value would likely need to be mutable for many actual
use cases.
If a "SimpleImmutableEntry" is returned, then in order to modify
the map's "value" contents, one has to (IIUC)
 * retrieve the entry,
 * create a new value,
 * call "put" (on the map)
rather than
 * retrieve the entry
 * call "setValue" (on the entry).
So we have a somewhat crippled API that does not bring any
advantage since reference immutability doesn't provide any
security to the map's user (any other caller who is being passed
the same map, is able to change its contents anyways).

I suggest to carefully consider whether to return a "SimpleEntry"
(and prominently note that any sort of concurrent modification is
a caller responsibility).

Regards,
Gilles

[1] https://docs.oracle.com/javase/8/docs/api/java/util/HashMap.html#entrySet--
[2] https://cr.openjdk.java.net/~briangoetz/valhalla/sov/02-object-model.html

>>> [...]

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org


Re: [geometry] PointMap and PointSet

Posted by Matt Juntunen <ma...@gmail.com>.
Hello,

I've made the following changes to the PR:
- removed the "resolveKey" method from PointMap
- renamed PointMap.resolveEntry to PointMap.getEntry and
PointSet.resolve to PointSet.get
- added an entry on PointMap/PointSet to the user guide
- addressed Github comments (thanks, Bruno!)

I ran some performance tests regarding the immutable entry instance
created in the PointMap.getEntry method and there seems to be no
impact.

> Furthermore, what is actually meant here by "immutable
instance" (since the "value" could be mutable without the
map being aware of the fact)?

It is immutable in that the object reference used as the entry value
cannot be changed. This reference could point to a mutable object.
This is the same behavior as other Map implementations.

Regards,
Matt



On Tue, Mar 15, 2022 at 10:51 AM Gilles Sadowski <gi...@gmail.com> wrote:
>
> Hi.
>
> Le mar. 15 mars 2022 à 00:47, Matt Juntunen
> <ma...@gmail.com> a écrit :
> >
> > Hello,
> >
> > > Do I understand correctly that the "resolveEntry" method which
> > you added behaves as my above "getEntry"?
> >
> > Correct.
> >
> > > If so, the latter can
> > replace both "resolve" methods, for a (c)leaner API.
> >
> > That would work. I would need to add a matching "get" method to
> > PointSet to provide the same functionality there. One consideration
> > here is that the "resolveEntry" method creates and returns an
> > immutable Entry instance with each call. The "resolveKey" method
> > avoids this.
>
> I had missed that subtlety; but it entails the question of what
> this functionality's intended usage is; e.g. would a user often
> need to access the "key" but not the associated "value"?
>
> Furthermore, what is actually meant here by "immutable
> instance" (since the "value" could be mutable without the
> map being aware of the fact)?
>
> > I'm not sure if this will have an impact on performance.
> > I'll try reducing the API as you suggest and include it in the PR if
> > it doesn't make a difference in performance.
> >
> > Do you prefer the "get" verb over "resolve",
>
> Yes (I'm missing what is being resolved; it's just something
> being accessed).
>
> Best,
> Gilles
>
> > e.g. "getEntry" vs "resolveEntry"?
> >
> > Regards,
> > Matt
> >
> > On Mon, Mar 14, 2022 at 2:19 PM Gilles Sadowski <gi...@gmail.com> wrote:
> > >
> > > Hello.
> > >
> > > Le lun. 14 mars 2022 à 16:19, Matt Juntunen
> > > <ma...@gmail.com> a écrit :
> > > >
> > > > Gilles,
> > > >
> > > > > it would be great to keep the tutorials/userguide in sync.
> > > >
> > > > Sounds good. I'll update the user guide in this PR.
> > > >
> > > > > I'm a little bit confused: Isn't it always the case that
> > > >   getEntry(p).getKey()
> > > > will return the originally inserted (i.e. "canonical") point (i.e. not "p")?
> > > >
> > > > Map does not contain a "getEntry" method. If it did, that would indeed
> > > > be preferable.
> > >
> > > Do I understand correctly that the "resolveEntry" method which
> > > you added behaves as my above "getEntry"?  If so, the latter can
> > > replace both "resolve" methods, for a (c)leaner API.
> > >
> > > > > Unless I'm missing a standard use-case, the specialized methods
> > > > "closestFirst" and "farthestFirst" don't seem useful (and wasteful
> > > > of computing resources: If iterating over the whole set, why would
> > > > one want to start from some particular point?).
> > > >
> > > > Could you post this comment on the JIRA issue and we can continue the
> > > > discussion there?
> > >
> > > Done.
> > >
> > > Regards,
> > > Gilles
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
> For additional commands, e-mail: dev-help@commons.apache.org
>

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org


Re: [geometry] PointMap and PointSet

Posted by Gilles Sadowski <gi...@gmail.com>.
Hi.

Le mar. 15 mars 2022 à 00:47, Matt Juntunen
<ma...@gmail.com> a écrit :
>
> Hello,
>
> > Do I understand correctly that the "resolveEntry" method which
> you added behaves as my above "getEntry"?
>
> Correct.
>
> > If so, the latter can
> replace both "resolve" methods, for a (c)leaner API.
>
> That would work. I would need to add a matching "get" method to
> PointSet to provide the same functionality there. One consideration
> here is that the "resolveEntry" method creates and returns an
> immutable Entry instance with each call. The "resolveKey" method
> avoids this.

I had missed that subtlety; but it entails the question of what
this functionality's intended usage is; e.g. would a user often
need to access the "key" but not the associated "value"?

Furthermore, what is actually meant here by "immutable
instance" (since the "value" could be mutable without the
map being aware of the fact)?

> I'm not sure if this will have an impact on performance.
> I'll try reducing the API as you suggest and include it in the PR if
> it doesn't make a difference in performance.
>
> Do you prefer the "get" verb over "resolve",

Yes (I'm missing what is being resolved; it's just something
being accessed).

Best,
Gilles

> e.g. "getEntry" vs "resolveEntry"?
>
> Regards,
> Matt
>
> On Mon, Mar 14, 2022 at 2:19 PM Gilles Sadowski <gi...@gmail.com> wrote:
> >
> > Hello.
> >
> > Le lun. 14 mars 2022 à 16:19, Matt Juntunen
> > <ma...@gmail.com> a écrit :
> > >
> > > Gilles,
> > >
> > > > it would be great to keep the tutorials/userguide in sync.
> > >
> > > Sounds good. I'll update the user guide in this PR.
> > >
> > > > I'm a little bit confused: Isn't it always the case that
> > >   getEntry(p).getKey()
> > > will return the originally inserted (i.e. "canonical") point (i.e. not "p")?
> > >
> > > Map does not contain a "getEntry" method. If it did, that would indeed
> > > be preferable.
> >
> > Do I understand correctly that the "resolveEntry" method which
> > you added behaves as my above "getEntry"?  If so, the latter can
> > replace both "resolve" methods, for a (c)leaner API.
> >
> > > > Unless I'm missing a standard use-case, the specialized methods
> > > "closestFirst" and "farthestFirst" don't seem useful (and wasteful
> > > of computing resources: If iterating over the whole set, why would
> > > one want to start from some particular point?).
> > >
> > > Could you post this comment on the JIRA issue and we can continue the
> > > discussion there?
> >
> > Done.
> >
> > Regards,
> > Gilles

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org


Re: [geometry] PointMap and PointSet

Posted by Matt Juntunen <ma...@gmail.com>.
Hello,

> Do I understand correctly that the "resolveEntry" method which
you added behaves as my above "getEntry"?

Correct.

> If so, the latter can
replace both "resolve" methods, for a (c)leaner API.

That would work. I would need to add a matching "get" method to
PointSet to provide the same functionality there. One consideration
here is that the "resolveEntry" method creates and returns an
immutable Entry instance with each call. The "resolveKey" method
avoids this. I'm not sure if this will have an impact on performance.
I'll try reducing the API as you suggest and include it in the PR if
it doesn't make a difference in performance.

Do you prefer the "get" verb over "resolve", e.g. "getEntry" vs "resolveEntry"?

Regards,
Matt

On Mon, Mar 14, 2022 at 2:19 PM Gilles Sadowski <gi...@gmail.com> wrote:
>
> Hello.
>
> Le lun. 14 mars 2022 à 16:19, Matt Juntunen
> <ma...@gmail.com> a écrit :
> >
> > Gilles,
> >
> > > it would be great to keep the tutorials/userguide in sync.
> >
> > Sounds good. I'll update the user guide in this PR.
> >
> > > I'm a little bit confused: Isn't it always the case that
> >   getEntry(p).getKey()
> > will return the originally inserted (i.e. "canonical") point (i.e. not "p")?
> >
> > Map does not contain a "getEntry" method. If it did, that would indeed
> > be preferable.
>
> Do I understand correctly that the "resolveEntry" method which
> you added behaves as my above "getEntry"?  If so, the latter can
> replace both "resolve" methods, for a (c)leaner API.
>
> > > Unless I'm missing a standard use-case, the specialized methods
> > "closestFirst" and "farthestFirst" don't seem useful (and wasteful
> > of computing resources: If iterating over the whole set, why would
> > one want to start from some particular point?).
> >
> > Could you post this comment on the JIRA issue and we can continue the
> > discussion there?
>
> Done.
>
> Regards,
> Gilles
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
> For additional commands, e-mail: dev-help@commons.apache.org
>

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org


Re: [geometry] PointMap and PointSet

Posted by Gilles Sadowski <gi...@gmail.com>.
Hello.

Le lun. 14 mars 2022 à 16:19, Matt Juntunen
<ma...@gmail.com> a écrit :
>
> Gilles,
>
> > it would be great to keep the tutorials/userguide in sync.
>
> Sounds good. I'll update the user guide in this PR.
>
> > I'm a little bit confused: Isn't it always the case that
>   getEntry(p).getKey()
> will return the originally inserted (i.e. "canonical") point (i.e. not "p")?
>
> Map does not contain a "getEntry" method. If it did, that would indeed
> be preferable.

Do I understand correctly that the "resolveEntry" method which
you added behaves as my above "getEntry"?  If so, the latter can
replace both "resolve" methods, for a (c)leaner API.

> > Unless I'm missing a standard use-case, the specialized methods
> "closestFirst" and "farthestFirst" don't seem useful (and wasteful
> of computing resources: If iterating over the whole set, why would
> one want to start from some particular point?).
>
> Could you post this comment on the JIRA issue and we can continue the
> discussion there?

Done.

Regards,
Gilles

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org


Re: [geometry] PointMap and PointSet

Posted by Matt Juntunen <ma...@gmail.com>.
Gilles,

> it would be great to keep the tutorials/userguide in sync.

Sounds good. I'll update the user guide in this PR.

> I'm a little bit confused: Isn't it always the case that
  getEntry(p).getKey()
will return the originally inserted (i.e. "canonical") point (i.e. not "p")?

Map does not contain a "getEntry" method. If it did, that would indeed
be preferable.

> Unless I'm missing a standard use-case, the specialized methods
"closestFirst" and "farthestFirst" don't seem useful (and wasteful
of computing resources: If iterating over the whole set, why would
one want to start from some particular point?).

Could you post this comment on the JIRA issue and we can continue the
discussion there?

Regards,
Matt

On Sun, Mar 13, 2022 at 11:25 AM Gilles Sadowski <gi...@gmail.com> wrote:
>
> Hello Matt.
>
> Le dim. 13 mars 2022 à 15:41, Matt Juntunen
> <ma...@gmail.com> a écrit :
> >
> > Hello,
> >
> > > Is there a gentle introduction to how it works and/or the intended
> > use cases?
> >
> > Not specifically. The implementations are used the same way as JDK
> > Maps and Sets so usage should be very familiar. As far as the internal
> > implementation details, I've tried to describe that in the javadocs
> > for the implementing classes.
> >
> > One example use case is construction of meshes from a stream of
> > triangles. This is used internally in
> > o.a.c.geometry.euclidean.threed.mesh.SimpleTriangleMesh. Another use
> > case is finding unique entries in a cloud of points, where many points
> > are close but not exactly equal to each other. This case was actually
> > posted on the user mailing list (I believe) way back when I started
> > implementing this feature.
>
> I know; but as the code base provides more and more functionality
> (thank you!) it would be great to keep the tutorials/userguide in sync.
> A simple "How to..." is often enough (and faster than browsing the
> Javadoc) in order to get at the most common usage.
>
> >
> > > Does it entail issues about some use cases or applications that
> > need this functionality?  Or do they not generally care about that
> > contract?
> > If so, maybe this collection shouldn't implement the standard JDK
> > interfaces (?).
> >
> > No, there shouldn't be any issues. java.util.TreeMap documents that
> > it's behavior is well-defined and consistent even when a Comparator
> > that doesn't match equals is given, such as
> > String.CASE_INSENSITIVE_ORDER. This is the same sort of situation. The
> > map/set is still quite useful even without the strict contract.
> >
> > > Where does the anticipation come from?
> >
> > The approach I used for helping to maintain somewhat balanced trees in
> > Euclidean 2D and 3D and spherical 2D regardless of insertion order is
> > not based on a well-known algorithm or paper since I was unable to
> > find one. The literature on the subject seems to focus on situations
> > where the inserted points are all known beforehand and can be inserted
> > in a particular order. I did not want to enforce this condition on the
> > API. What I ended up with is just an idea I had for tree balancing
> > that seems to work pretty well. As such, I fully expect that there
> > will be a better option discovered later on.
>
> IMHO, the above two Q & A are worth mentioning in the userguide.
> The second especially may attract some user's attention who could
> provide the missing info.  [Of course, it should also appear at the
> relevant places in the Javadoc.]
>
> >
> > > I don't quite follow; which are the corresponding "non-canonical"
> > accessors?
> >
> > My thought here is that there will be situations where a set of points
> > is placed into a map/set and then these points are queried using
> > values determined from some other source, such as through computations
> > of some sort.
>
> Indeed.
>
> > These query points may vary from the originally inserted
> > points by distances allowed by the Precision.DoubleEquivalence. In
> > these cases, it's useful to be able to obtain the exact value of the
> > originally inserted (i.e. "canonical") point. This is the purpose of
> > the "resolve" methods.
>
> I'm a little bit confused: Isn't it always the case that
>   getEntry(p).getKey()
> will return the originally inserted (i.e. "canonical") point (i.e. not "p")?
>
> Anyways, I'd suggest that this be illustrated in the userguide (linked
> to a working application in "commons-geometry-examples").
>
> >
> > > Is there a notion of neighbours (as in: return the "n" entries that
> > are closest to a given point)?
> >
> > I am picturing that functionality being implemented in a follow-up issue. [1]
>
> Thanks.
> However, my impression is that the API should be more general:
> ---CUT---
> public Iterable<P> closestInRange(P point, double radius);
> ---CUT---
>
> Unless I'm missing a standard use-case, the specialized methods
> "closestFirst" and "farthestFirst" don't seem useful (and wasteful
> of computing resources: If iterating over the whole set, why would
> one want to start from some particular point?).
>
> Regards,
> Gilles
>
> >
> > Regards,
> > Matt
> >
> > [1] https://issues.apache.org/jira/browse/GEOMETRY-146
> >
> > > [...]
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
> For additional commands, e-mail: dev-help@commons.apache.org
>

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org


Re: [geometry] PointMap and PointSet

Posted by Gilles Sadowski <gi...@gmail.com>.
Hello Matt.

Le dim. 13 mars 2022 à 15:41, Matt Juntunen
<ma...@gmail.com> a écrit :
>
> Hello,
>
> > Is there a gentle introduction to how it works and/or the intended
> use cases?
>
> Not specifically. The implementations are used the same way as JDK
> Maps and Sets so usage should be very familiar. As far as the internal
> implementation details, I've tried to describe that in the javadocs
> for the implementing classes.
>
> One example use case is construction of meshes from a stream of
> triangles. This is used internally in
> o.a.c.geometry.euclidean.threed.mesh.SimpleTriangleMesh. Another use
> case is finding unique entries in a cloud of points, where many points
> are close but not exactly equal to each other. This case was actually
> posted on the user mailing list (I believe) way back when I started
> implementing this feature.

I know; but as the code base provides more and more functionality
(thank you!) it would be great to keep the tutorials/userguide in sync.
A simple "How to..." is often enough (and faster than browsing the
Javadoc) in order to get at the most common usage.

>
> > Does it entail issues about some use cases or applications that
> need this functionality?  Or do they not generally care about that
> contract?
> If so, maybe this collection shouldn't implement the standard JDK
> interfaces (?).
>
> No, there shouldn't be any issues. java.util.TreeMap documents that
> it's behavior is well-defined and consistent even when a Comparator
> that doesn't match equals is given, such as
> String.CASE_INSENSITIVE_ORDER. This is the same sort of situation. The
> map/set is still quite useful even without the strict contract.
>
> > Where does the anticipation come from?
>
> The approach I used for helping to maintain somewhat balanced trees in
> Euclidean 2D and 3D and spherical 2D regardless of insertion order is
> not based on a well-known algorithm or paper since I was unable to
> find one. The literature on the subject seems to focus on situations
> where the inserted points are all known beforehand and can be inserted
> in a particular order. I did not want to enforce this condition on the
> API. What I ended up with is just an idea I had for tree balancing
> that seems to work pretty well. As such, I fully expect that there
> will be a better option discovered later on.

IMHO, the above two Q & A are worth mentioning in the userguide.
The second especially may attract some user's attention who could
provide the missing info.  [Of course, it should also appear at the
relevant places in the Javadoc.]

>
> > I don't quite follow; which are the corresponding "non-canonical"
> accessors?
>
> My thought here is that there will be situations where a set of points
> is placed into a map/set and then these points are queried using
> values determined from some other source, such as through computations
> of some sort.

Indeed.

> These query points may vary from the originally inserted
> points by distances allowed by the Precision.DoubleEquivalence. In
> these cases, it's useful to be able to obtain the exact value of the
> originally inserted (i.e. "canonical") point. This is the purpose of
> the "resolve" methods.

I'm a little bit confused: Isn't it always the case that
  getEntry(p).getKey()
will return the originally inserted (i.e. "canonical") point (i.e. not "p")?

Anyways, I'd suggest that this be illustrated in the userguide (linked
to a working application in "commons-geometry-examples").

>
> > Is there a notion of neighbours (as in: return the "n" entries that
> are closest to a given point)?
>
> I am picturing that functionality being implemented in a follow-up issue. [1]

Thanks.
However, my impression is that the API should be more general:
---CUT---
public Iterable<P> closestInRange(P point, double radius);
---CUT---

Unless I'm missing a standard use-case, the specialized methods
"closestFirst" and "farthestFirst" don't seem useful (and wasteful
of computing resources: If iterating over the whole set, why would
one want to start from some particular point?).

Regards,
Gilles

>
> Regards,
> Matt
>
> [1] https://issues.apache.org/jira/browse/GEOMETRY-146
>
> > [...]

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org


Re: [geometry] PointMap and PointSet

Posted by Matt Juntunen <ma...@gmail.com>.
Hello,

> Is there a gentle introduction to how it works and/or the intended
use cases?

Not specifically. The implementations are used the same way as JDK
Maps and Sets so usage should be very familiar. As far as the internal
implementation details, I've tried to describe that in the javadocs
for the implementing classes.

One example use case is construction of meshes from a stream of
triangles. This is used internally in
o.a.c.geometry.euclidean.threed.mesh.SimpleTriangleMesh. Another use
case is finding unique entries in a cloud of points, where many points
are close but not exactly equal to each other. This case was actually
posted on the user mailing list (I believe) way back when I started
implementing this feature.

> Does it entail issues about some use cases or applications that
need this functionality?  Or do they not generally care about that
contract?
If so, maybe this collection shouldn't implement the standard JDK
interfaces (?).

No, there shouldn't be any issues. java.util.TreeMap documents that
it's behavior is well-defined and consistent even when a Comparator
that doesn't match equals is given, such as
String.CASE_INSENSITIVE_ORDER. This is the same sort of situation. The
map/set is still quite useful even without the strict contract.

> Where does the anticipation come from?

The approach I used for helping to maintain somewhat balanced trees in
Euclidean 2D and 3D and spherical 2D regardless of insertion order is
not based on a well-known algorithm or paper since I was unable to
find one. The literature on the subject seems to focus on situations
where the inserted points are all known beforehand and can be inserted
in a particular order. I did not want to enforce this condition on the
API. What I ended up with is just an idea I had for tree balancing
that seems to work pretty well. As such, I fully expect that there
will be a better option discovered later on.

> I don't quite follow; which are the corresponding "non-canonical"
accessors?

My thought here is that there will be situations where a set of points
is placed into a map/set and then these points are queried using
values determined from some other source, such as through computations
of some sort. These query points may vary from the originally inserted
points by distances allowed by the Precision.DoubleEquivalence. In
these cases, it's useful to be able to obtain the exact value of the
originally inserted (i.e. "canonical") point. This is the purpose of
the "resolve" methods.

> Is there a notion of neighbours (as in: return the "n" entries that
are closest to a given point)?

I am picturing that functionality being implemented in a follow-up issue. [1]

Regards,
Matt

[1] https://issues.apache.org/jira/browse/GEOMETRY-146

On Sat, Mar 12, 2022 at 10:36 AM Gilles Sadowski <gi...@gmail.com> wrote:
>
> Hello.
>
> Le ven. 11 mars 2022 à 16:18, Matt Juntunen
> <ma...@gmail.com> a écrit :
> >
> > Hello,
> >
> > I recently posted a PR [1] for GEOMETRY-142 [2], which is for adding
> > PointMap and PointSet implementations. These are Map and Set
> > implementations specifically designed to use Points as keys.
>
> Is there a gentle introduction to how it works and/or the intended
> use cases?
>
> > They
> > support fuzzy key comparison, meaning that points do not have to be
> > exactly equal to each other in order to be considered equal by the
> > map/set. (Note that this means these types do not follow the strict
> > Map/Set contracts since they are not consistent with equals. This is
> > documented in the PointMap/PointSet javadocs.)
>
> Does it entail issues about some use cases or applications that
> need this functionality?  Or do they not generally care about that
> contract?
> If so, maybe this collection shouldn't implement the standard JDK
> interfaces (?).
>
> > I've completely hidden
> > the implementation details from the public API
>
> Thanks.
>
> > since I anticipate
> > changes in the future with regard to the algorithms used.
>
> Where does the anticipation come from?
>
> > Instances
> > are created through factory classes in each space. Ex:
> >
> > PointMap<Vector3D, String> map = EuclideanCollections.pointMap3D(precision);
> > PointSet<Point2S> set = SphericalCollections.pointSet2S(precision);
> >
> > Since fuzzy key comparison is used, I've added the following methods
> > to the interfaces to allow access to the exact, "canonical" version of
> > the key stored in the collection.
> >
> > PointMap<P, V>  {
> >     // return the key corresponding to pt, or null if not found
> >     P resolveKey(P pt);
> >
> >     // return the map entry corresponding to pt, or null if not found
> >     Map.Entry<P, V> resolveEntry(P pt);
> > }
> >
> > PointSet<P> {
> >     // return the key corresponding to pt, or null if not found
> >     P resolve(P pt);
> > }
>
> I don't quite follow; which are the corresponding "non-canonical"
> accessors?
>
> >
> > Reviews and comments are welcome.
>
> Is there a notion of neighbours (as in: return the "n" entries that
> are closest to a given point)?
>
> Regards,
> Gilles
>
> >
> > Regards,
> > Matt Juntunen
> >
> >
> > [1] https://github.com/apache/commons-geometry/pull/194
> > [2] https://issues.apache.org/jira/projects/GEOMETRY/issues/GEOMETRY-142
> >
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
> For additional commands, e-mail: dev-help@commons.apache.org
>

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org


Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Sumanth Rajkumar <ra...@gmail.com>.
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 <ra...@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 <al...@gmail.com>
> wrote:
>
>> On Thu, 26 May 2022 at 15:04, Gilles Sadowski <gi...@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).
>>
>

Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Gilles Sadowski <gi...@gmail.com>.
Hello.

> [...]
>
> >
> > 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() ;
> > }
> >
> >
> This has concerns for efficiency.

First thought that came to my mind, being confirmed when looking
at the "conj" example (where "applyAsDouble" is called twice)...

Gilles

> [...]

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org


Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Alex Herbert <al...@gmail.com>.
On Sat, 11 Jun 2022 at 06:30, Sumanth Rajkumar <ra...@gmail.com>
wrote:

> On Fri, Jun 10, 2022, 19:30 Gilles Sadowski <gi...@gmail.com> wrote:
>
> > The current implementation of "Complex" encapsulates two "double" fields
> > (not
> > a "double[]").
> > Should we make two, at first separate, discussions: One for the
> > implementation
> > of the "complex number" concept, and another for (n-dimensional) lists of
> > them?
> >
>
> Yes. We could also change existing Complex type from two double fields to
> double[] and even make it implement new ComplexNumber interface? This
> should maintain runtime compatibility?
>

True. It does have the following disadvantages:

- No longer immutable. Immutability would have to be enforced by
encapsulation.
- Increases memory consumption. Note this would compound the original issue
with representing Complex in a List<Complex> with inefficient memory
consumption. So any application that does not update to the new
implementation would suffer.


>
> I was thinking this would allow efficient reuse of functions processing
> lists also to be used on single numbers (Complex) which is also a list of
> size 1
>

But we have also discussed other ways to share code between a single number
and a list of them that are more safe, i.e. those that represent each entry
of the list as a type to pass to the computation method which stores the
result in the provided computation result.


>
> Jtransform has support for large arrays (size greater than integer.max with
> array index of type long.
>
> Do we need to support that now or add that support later if needed?
>

No. You have to have a very good reason to use the undocumented
sun.misc.Unsafe in code. I do not think we currently have that. Note that
~2^31 is a lot of numbers and to exceed this would be a specialist
application for example using JTransforms 3D FFT on very large 3D data
represented as a 1D array.

Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Matt Juntunen <ma...@gmail.com>.
Hello,

Good discussion here! This is great!

I lost track of what the overall goal here is while reading through
the conversation. The goal of NUMBERS-186 is to "allow operations to
be performed on lists of complex numbers". My first thought when
looking at this is "how are we going to represent lists of complex
numbers?" I don't think there is a single answer to this since the
correct format for a list of complex numbers is whatever format the
user already has them in. They could be in a file, in separate double
arrays, in a single double array (alternating real and imaginary), in
a float array, in a java.nio.Buffer, etc. So far, I haven't seen an
API that can accomodate all of these. What I would like to see us
create is an interface or abstract class like java.util.Buffer that
allows us to wrap an underlying storage mechanism with complex number
semantics. For example, if you have real and imaginary parts in two
separate arrays, you could do something like

    ComplexBuffer buf =
ComplexBuffer.fromRealAndImaginaryArrays(realArr, imArr);

Similarly, with a DoubleBuffer:

    ComplexBuffer buf = ComplexBuffer.fromDoubleBuffer(buf);

We can completely hide the classes implementing the wrappings here
from the public API so that things are straightforward for the user.
If we can first create a simple public API like this, then we can
safely focus on performance improvements within the private code.

Regards,
Matt J


On Sat, Jun 11, 2022 at 8:32 AM Gilles Sadowski <gi...@gmail.com> wrote:
>
> Hello.
>
> > [...]
> >
> > interface ComplexDoubleArray {
> >     Stream<ComplexDoubleArray> stream(int start, int length);
> > }
> >
> > ComplexDoubleArray a;
> > // Will use the Java 8 ForkJoinPool.commonPool() for parallel execution
> > a.stream(start, length).parallel().forEach(x -> ComplexFunctions.conj(x,
> > x));
> >
> > class ComplexFunctions {
> >     static void conj(ComplexDoubleArray in, ComplexDoubleArray out);
> > }
> >
> > [...]
>
> I have a hard time figuring out whether these bits of code are
> intended to become the application developer API...
> What data-structure(s) will be visible (from the application)?
> What will be hidden ("implementation details")?
> Do we have use-cases of non-trivial processing of N-dimensional
> cubes of complex numbers?  [I imagine that the same API should
> be able to also process cubes of real numbers (without storing the
> "0" imaginary parts).]
>
> Gilles
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
> For additional commands, e-mail: dev-help@commons.apache.org
>

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org


Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Alex Herbert <al...@gmail.com>.
OK. I have approved the CI build to run on the PR.

I will review when I have some more time.

Alex


On Fri, 24 Jun 2022 at 15:59, Sumanth Rajkumar <ra...@gmail.com>
wrote:

> Hi Alex, Gilles, and Matt,
>
> I have raised a PR to the complex-gsoc-22 branch and it has been linked to
> the NUMBERS-188 jira.
>
> While the PR is being reviewed, I will start working on NUMBERS-186 (adding
> support for list and matrix of Complex numbers).
>
> Thanks,
> Sumanth
>
> On Mon, 20 Jun 2022 at 09:43, Sumanth Rajkumar <rajkumar.sumanth@gmail.com
> >
> wrote:
>
> > Hello Gilles,
> > Thanks!  I will start a new thread for float support.
> >
> > Thanks
> > Sumanth
> >
> >
> > On Mon, Jun 20, 2022, 18:41 Gilles Sadowski <gi...@gmail.com> wrote
> >
> >>
> >> > Also, should we add support for float data type for complex numbers?
> >>
> >> In the "dev" ML, we customarily aim at focussing each discussion,
> >
> >
> >> Thus could you please start a new thread with this question?
> >>
> >
> >
> >
>

Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Gilles Sadowski <gi...@gmail.com>.
Hello.

> > [...]
> > I have raised PR #113 after rebasing to the master branch with Alex's
> > checkstyle changes
> >
> > As per feedback, I have made the following changes
> > a) Added javadoc comments
> > b) Ensured test coverage
> > c) Renamed accessors on the interface
> >
>
> [...]
>
> >
> >
> > > In "DComplex", I propose that the accessors be named "real()" and
> > > "imag()" (or just
> > > "re()" and "im()").  ["DComplex" is not a very satisfying name either...]
> >
> > For the interface name, shall I change it to Complex64 from DComplex?
> >
>
> In c the 'complex' keyword is a suffix:
>
> double complex c1;
> float complex c2;
> long double complex c3;
>
> In c++ the type is generic (and read as a suffix):
>
> complex<double> c1;
> complex<float> c2;
>
> Either of these would be my preference over DComplex or Complex64.

Just to be sure: Are we discussing this because "Complex" is
already taken?

> > > Are we sure that all this code needs to be part of the public API?
> > > If not, I'd suggest limiting accessibility to "package-private".
> >
> > Are you referring to the static methods in ComplexFunctions and
> > ComplexBiFunctions classes?
> > I think they would need to be public for developers to be able to compose
> > multiple operations...
> >
>
> The static helper functions have been extracted to support all the ISO c99
> operations on the list structure of complex numbers.
>
> A list will ideally implement a generic foreach operation. So to apply a
> single function only requires making the static functions public. The
> alternative is to make the list expose all the ISO c99 operations in its
> public API.
>
> To create a composite function that eventually writes back to the list can
> be implemented by writing intermediate values to a result which is then
> passed to the next operation. This can be satisfied by using the Complex
> class. This already exposes all the ISO c99 functions. So perhaps it is not
> required to make all the helper functions public for the purpose of
> composing multiple operations. But it would be helpful for all the single
> operations.
>

I may be one or more steps behind, sorry, but I still cannot figure
out how the API is supposed to be applied (IOW, the "use cases").
I'm still at "provide functions that operate on a list of complex numbers".
But the subsequent question: For what purpose?
Some weeks ago (IIRC), I asked the same and whether the only use
case was FFT...

Regards,
Gilles

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org


Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Sumanth Rajkumar <ra...@gmail.com>.
Hi,

I have made a preliminary complex list implementation class which is backed
by a single double array in interleaved format.
I made some test cases for the forEach methods as well.
This can be viewed here [1]. Please let me know what you think and if I'm
on the right track.

Thanks,

Sumanth

[1]
https://github.com/sumanth-rajkumar/commons-numbers/tree/NUMBERS-186.complex_list_support/commons-numbers-complex/src

On Tue, 2 Aug 2022 at 13:26, Sumanth Rajkumar <ra...@gmail.com>
wrote:

> Thanks Alex for the feedback,
>
> I'll work on a ComplexList class that implements the List interface and is
> backed by a single double array in interleaved format.
> I'll have something ready to look at by tomorrow.
>
> Thanks,
>
> Sumanth
>
> On Tue, 2 Aug 2022 at 03:52, Alex Herbert <al...@gmail.com>
> wrote:
>
>> On Mon, 1 Aug 2022 at 17:38, Sumanth Rajkumar <rajkumar.sumanth@gmail.com
>> >
>> wrote:
>>
>> > Hi,
>> >
>> > > I think that Alex's suggestion still holds: Better focus on one
>> > > task at a time.  Are the "list" and "matrix" features related?
>> >
>> > OK, Will focus on list features first and focus on extending all
>> existing
>> > methods on the Complex class to Lists.
>> >
>> > UnaryOperators will apply the operation (using refactored static
>> methods)
>> > to each element.
>> > BinaryOperators for lists will have two variants.
>> > For the first variant, the second operand is a single complex number
>> that
>> > is applied as second operand for all elements on List
>> > The second variant operates on two Lists. Can we look at this later?
>> >
>>
>> For an operand that is a single complex number then this should be
>> satisfied by passing a lambda function to the unary forEach. The same
>> would
>> be the same for a single double factor. So these seem redundant and can be
>> covered by documentation.
>>
>> I would suggest you create a List<Complex> implementation that only
>> satisfies the list interface in an optimal manner. If you extend
>> java.util.AbstractList the minimal implementation is often not optimal for
>> the storage (for example the spliterator and the forEach constructs).
>>
>> The support for a JDK Collection may not have to add any additional
>> methods
>> for processing. It would merely replace using ArrayList<Complex>.
>> Numerical
>> processing of complex numbers could be added instead to a more focussed
>> data structure that does not support the entire Collection API, i.e. the
>> vector and matrix concepts previously proposed.
>>
>>
>> >
>> > > Do we have one use case for the "list" feature?
>> > > I see there a lot factory methods that seem to defeat the
>> > > intended purpose (AFAIU Matt's remark) of letting the caller
>> > > decide on the input format.
>> > > Similarly, the "parse" feature should be left to the code that
>> > > handles the input.  IOW shouldn't we separate the concerns
>> > > of converting the input (array, stream, list of strings, ...) from
>> > > what can be done with a "list" instance?
>> >
>> > Since the lists can have different underlying data storage
>> implementations
>> > such as
>> >  a) single double primitive array or DoubleBuffer in interleaved format
>> >  b) single double primitive array or DoubleBuffer in sub array format
>> >  c) separate arrays/DoubleBuffers for real and imaginary parts
>> >
>> > Based on earlier discussions, Alex wanted the iteration logic over the
>> list
>> > to apply the operators to be within the List implementation
>> > So each implementation will have its own implementation of operators
>> that
>> > is optimal for its data storage
>> >
>> > For now, should we focus on more than 1 implementation? If it is just 1
>> > implementation, which one can I pick up first?
>> >
>>
>> 1 implementation is the best start point to build the API.
>>
>>
>> > Should we have a common interface (minimum methods/operations that all
>> > implementations should support)?
>> >
>>
>> That makes sense. However a known backing format for the data will allow
>> optimal computation. So once the API is established for a single data
>> structure format we can test speed of computation when mixing backing data
>> structures.
>>
>>
>> >
>> > If so, I can first focus on finalizing the List interface and then look
>> at
>> > implementations
>> >
>> > I was also looking at the EJML api's [1].
>> > They support three API styles a) Procedural b) SimpleMatrix and c)
>> > Equations (Matlab style)
>> >
>> > It looks like we will be implementing the SimpleMatrix style. Should we
>> > also consider other approaches?
>> >
>>
>> Equations requires parsing text input. I would put that out-of-scope. I
>> would say an OO style is the most natural from a Java perspective. The API
>> could be similar to the matrix functionality in Commons Math.
>>
>> Alex
>>
>

Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Sumanth Rajkumar <ra...@gmail.com>.
Thanks Alex for the feedback,

I'll work on a ComplexList class that implements the List interface and is
backed by a single double array in interleaved format.
I'll have something ready to look at by tomorrow.

Thanks,

Sumanth

On Tue, 2 Aug 2022 at 03:52, Alex Herbert <al...@gmail.com> wrote:

> On Mon, 1 Aug 2022 at 17:38, Sumanth Rajkumar <ra...@gmail.com>
> wrote:
>
> > Hi,
> >
> > > I think that Alex's suggestion still holds: Better focus on one
> > > task at a time.  Are the "list" and "matrix" features related?
> >
> > OK, Will focus on list features first and focus on extending all existing
> > methods on the Complex class to Lists.
> >
> > UnaryOperators will apply the operation (using refactored static methods)
> > to each element.
> > BinaryOperators for lists will have two variants.
> > For the first variant, the second operand is a single complex number that
> > is applied as second operand for all elements on List
> > The second variant operates on two Lists. Can we look at this later?
> >
>
> For an operand that is a single complex number then this should be
> satisfied by passing a lambda function to the unary forEach. The same would
> be the same for a single double factor. So these seem redundant and can be
> covered by documentation.
>
> I would suggest you create a List<Complex> implementation that only
> satisfies the list interface in an optimal manner. If you extend
> java.util.AbstractList the minimal implementation is often not optimal for
> the storage (for example the spliterator and the forEach constructs).
>
> The support for a JDK Collection may not have to add any additional methods
> for processing. It would merely replace using ArrayList<Complex>. Numerical
> processing of complex numbers could be added instead to a more focussed
> data structure that does not support the entire Collection API, i.e. the
> vector and matrix concepts previously proposed.
>
>
> >
> > > Do we have one use case for the "list" feature?
> > > I see there a lot factory methods that seem to defeat the
> > > intended purpose (AFAIU Matt's remark) of letting the caller
> > > decide on the input format.
> > > Similarly, the "parse" feature should be left to the code that
> > > handles the input.  IOW shouldn't we separate the concerns
> > > of converting the input (array, stream, list of strings, ...) from
> > > what can be done with a "list" instance?
> >
> > Since the lists can have different underlying data storage
> implementations
> > such as
> >  a) single double primitive array or DoubleBuffer in interleaved format
> >  b) single double primitive array or DoubleBuffer in sub array format
> >  c) separate arrays/DoubleBuffers for real and imaginary parts
> >
> > Based on earlier discussions, Alex wanted the iteration logic over the
> list
> > to apply the operators to be within the List implementation
> > So each implementation will have its own implementation of operators that
> > is optimal for its data storage
> >
> > For now, should we focus on more than 1 implementation? If it is just 1
> > implementation, which one can I pick up first?
> >
>
> 1 implementation is the best start point to build the API.
>
>
> > Should we have a common interface (minimum methods/operations that all
> > implementations should support)?
> >
>
> That makes sense. However a known backing format for the data will allow
> optimal computation. So once the API is established for a single data
> structure format we can test speed of computation when mixing backing data
> structures.
>
>
> >
> > If so, I can first focus on finalizing the List interface and then look
> at
> > implementations
> >
> > I was also looking at the EJML api's [1].
> > They support three API styles a) Procedural b) SimpleMatrix and c)
> > Equations (Matlab style)
> >
> > It looks like we will be implementing the SimpleMatrix style. Should we
> > also consider other approaches?
> >
>
> Equations requires parsing text input. I would put that out-of-scope. I
> would say an OO style is the most natural from a Java perspective. The API
> could be similar to the matrix functionality in Commons Math.
>
> Alex
>

Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Alex Herbert <al...@gmail.com>.
On Mon, 1 Aug 2022 at 17:38, Sumanth Rajkumar <ra...@gmail.com>
wrote:

> Hi,
>
> > I think that Alex's suggestion still holds: Better focus on one
> > task at a time.  Are the "list" and "matrix" features related?
>
> OK, Will focus on list features first and focus on extending all existing
> methods on the Complex class to Lists.
>
> UnaryOperators will apply the operation (using refactored static methods)
> to each element.
> BinaryOperators for lists will have two variants.
> For the first variant, the second operand is a single complex number that
> is applied as second operand for all elements on List
> The second variant operates on two Lists. Can we look at this later?
>

For an operand that is a single complex number then this should be
satisfied by passing a lambda function to the unary forEach. The same would
be the same for a single double factor. So these seem redundant and can be
covered by documentation.

I would suggest you create a List<Complex> implementation that only
satisfies the list interface in an optimal manner. If you extend
java.util.AbstractList the minimal implementation is often not optimal for
the storage (for example the spliterator and the forEach constructs).

The support for a JDK Collection may not have to add any additional methods
for processing. It would merely replace using ArrayList<Complex>. Numerical
processing of complex numbers could be added instead to a more focussed
data structure that does not support the entire Collection API, i.e. the
vector and matrix concepts previously proposed.


>
> > Do we have one use case for the "list" feature?
> > I see there a lot factory methods that seem to defeat the
> > intended purpose (AFAIU Matt's remark) of letting the caller
> > decide on the input format.
> > Similarly, the "parse" feature should be left to the code that
> > handles the input.  IOW shouldn't we separate the concerns
> > of converting the input (array, stream, list of strings, ...) from
> > what can be done with a "list" instance?
>
> Since the lists can have different underlying data storage implementations
> such as
>  a) single double primitive array or DoubleBuffer in interleaved format
>  b) single double primitive array or DoubleBuffer in sub array format
>  c) separate arrays/DoubleBuffers for real and imaginary parts
>
> Based on earlier discussions, Alex wanted the iteration logic over the list
> to apply the operators to be within the List implementation
> So each implementation will have its own implementation of operators that
> is optimal for its data storage
>
> For now, should we focus on more than 1 implementation? If it is just 1
> implementation, which one can I pick up first?
>

1 implementation is the best start point to build the API.


> Should we have a common interface (minimum methods/operations that all
> implementations should support)?
>

That makes sense. However a known backing format for the data will allow
optimal computation. So once the API is established for a single data
structure format we can test speed of computation when mixing backing data
structures.


>
> If so, I can first focus on finalizing the List interface and then look at
> implementations
>
> I was also looking at the EJML api's [1].
> They support three API styles a) Procedural b) SimpleMatrix and c)
> Equations (Matlab style)
>
> It looks like we will be implementing the SimpleMatrix style. Should we
> also consider other approaches?
>

Equations requires parsing text input. I would put that out-of-scope. I
would say an OO style is the most natural from a Java perspective. The API
could be similar to the matrix functionality in Commons Math.

Alex

Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Sumanth Rajkumar <ra...@gmail.com>.
Hi,

> I think that Alex's suggestion still holds: Better focus on one
> task at a time.  Are the "list" and "matrix" features related?

OK, Will focus on list features first and focus on extending all existing
methods on the Complex class to Lists.

UnaryOperators will apply the operation (using refactored static methods)
to each element.
BinaryOperators for lists will have two variants.
For the first variant, the second operand is a single complex number that
is applied as second operand for all elements on List
The second variant operates on two Lists. Can we look at this later?

> Do we have one use case for the "list" feature?
> I see there a lot factory methods that seem to defeat the
> intended purpose (AFAIU Matt's remark) of letting the caller
> decide on the input format.
> Similarly, the "parse" feature should be left to the code that
> handles the input.  IOW shouldn't we separate the concerns
> of converting the input (array, stream, list of strings, ...) from
> what can be done with a "list" instance?

Since the lists can have different underlying data storage implementations
such as
 a) single double primitive array or DoubleBuffer in interleaved format
 b) single double primitive array or DoubleBuffer in sub array format
 c) separate arrays/DoubleBuffers for real and imaginary parts

Based on earlier discussions, Alex wanted the iteration logic over the list
to apply the operators to be within the List implementation
So each implementation will have its own implementation of operators that
is optimal for its data storage

For now, should we focus on more than 1 implementation? If it is just 1
implementation, which one can I pick up first?

Should we have a common interface (minimum methods/operations that all
implementations should support)?

If so, I can first focus on finalizing the List interface and then look at
implementations

I was also looking at the EJML api's [1].
They support three API styles a) Procedural b) SimpleMatrix and c)
Equations (Matlab style)

It looks like we will be implementing the SimpleMatrix style. Should we
also consider other approaches?

Thanks,

Sumanth

[1]
http://ejml.org/wiki/index.php?title=Main_Page#:~:text=EJML%20has%20three,of%20writing%20equations

On Fri, 29 Jul 2022 at 10:41, Gilles Sadowski <gi...@gmail.com> wrote:

> Hello.
>
> Le jeu. 28 juil. 2022 à 22:28, Sumanth Rajkumar
> <ra...@gmail.com> a écrit :
> >
> > Hi,
> >
> > I've completed the task of refactoring all complex operations to static
> > functions and it has been merged to the complex-gsoc-2022 branch. Thank
> you
> > for all the help to make this happen.
> >
> > I wanted to talk about the next task and get some inputs from all of you
> on
> > how to start.
> >
> > Now, I'm going to be working on complex list and matrix support. I have
> > already begun working on this which can be seen in my github branch [1].
> >
> > Right now, my work is based on the idea of a mutable list representation
> of
> > a set of complex numbers (List<Complex>) using a backing double[] for
> > storage.
> >
> > I know Matt mentioned using Buffers as well and so I was hoping if you
> can
> > give a little more detail about this. If there are any other suggestions,
> > please let me know.
> >
> > Thanks,
> >
> > Sumanth
> >
> > [1]
> >
> https://github.com/sumanth-rajkumar/commons-numbers/tree/NUMBERS-186.complex_list_and_matrix_support
>
> I think that Alex's suggestion still holds: Better focus on one
> task at a time.  Are the "list" and "matrix" features related?
>
> Do we have one use case for the "list" feature?
> I see there a lot factory methods that seem to defeat the
> intended purpose (AFAIU Matt's remark) of letting the caller
> decide on the input format.
> Similarly, the "parse" feature should be left to the code that
> handles the input.  IOW shouldn't we separate the concerns
> of converting the input (array, stream, list of strings, ...) from
> what can be done with a "list" instance?
>
> Do we target linear algebra for complex numbers?
> What use-cases for the "ComplexMatrix" and "ComplexVector"
> interfaces?
>
> Regards,
> Gilles
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
> For additional commands, e-mail: dev-help@commons.apache.org
>
>

Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Gilles Sadowski <gi...@gmail.com>.
Hello.

Le jeu. 28 juil. 2022 à 22:28, Sumanth Rajkumar
<ra...@gmail.com> a écrit :
>
> Hi,
>
> I've completed the task of refactoring all complex operations to static
> functions and it has been merged to the complex-gsoc-2022 branch. Thank you
> for all the help to make this happen.
>
> I wanted to talk about the next task and get some inputs from all of you on
> how to start.
>
> Now, I'm going to be working on complex list and matrix support. I have
> already begun working on this which can be seen in my github branch [1].
>
> Right now, my work is based on the idea of a mutable list representation of
> a set of complex numbers (List<Complex>) using a backing double[] for
> storage.
>
> I know Matt mentioned using Buffers as well and so I was hoping if you can
> give a little more detail about this. If there are any other suggestions,
> please let me know.
>
> Thanks,
>
> Sumanth
>
> [1]
> https://github.com/sumanth-rajkumar/commons-numbers/tree/NUMBERS-186.complex_list_and_matrix_support

I think that Alex's suggestion still holds: Better focus on one
task at a time.  Are the "list" and "matrix" features related?

Do we have one use case for the "list" feature?
I see there a lot factory methods that seem to defeat the
intended purpose (AFAIU Matt's remark) of letting the caller
decide on the input format.
Similarly, the "parse" feature should be left to the code that
handles the input.  IOW shouldn't we separate the concerns
of converting the input (array, stream, list of strings, ...) from
what can be done with a "list" instance?

Do we target linear algebra for complex numbers?
What use-cases for the "ComplexMatrix" and "ComplexVector"
interfaces?

Regards,
Gilles

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org


Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Sumanth Rajkumar <ra...@gmail.com>.
Hi,

I've completed the task of refactoring all complex operations to static
functions and it has been merged to the complex-gsoc-2022 branch. Thank you
for all the help to make this happen.

I wanted to talk about the next task and get some inputs from all of you on
how to start.

Now, I'm going to be working on complex list and matrix support. I have
already begun working on this which can be seen in my github branch [1].

Right now, my work is based on the idea of a mutable list representation of
a set of complex numbers (List<Complex>) using a backing double[] for
storage.

I know Matt mentioned using Buffers as well and so I was hoping if you can
give a little more detail about this. If there are any other suggestions,
please let me know.

Thanks,

Sumanth

[1]
https://github.com/sumanth-rajkumar/commons-numbers/tree/NUMBERS-186.complex_list_and_matrix_support


On Tue, 12 Jul 2022 at 10:03, Sumanth Rajkumar <ra...@gmail.com>
wrote:

> Hi,
>
> I committed my changes to the test classes into the opened PR and I think
> it ran the checks by itself without needing your approval.
>
> Please let me know if there's anything else I need to change.
>
> Thanks,
>
> Sumanth
>
> On Mon, Jul 11, 2022, 7:45 PM Alex Herbert <al...@gmail.com>
> wrote:
>
>> On Mon, 11 Jul 2022 at 22:56, Gilles Sadowski <gi...@gmail.com>
>> wrote:
>>
>> > Le lun. 11 juil. 2022 à 20:03, Sumanth Rajkumar
>> > <ra...@gmail.com> a écrit :
>> > >
>> > > Hi,
>> > >
>> > > I have finished updating the test classes, but I am encountering a
>> > problem
>> > > in the ComplexEdgeCaseTest class.
>> > >
>> > > private static void assertComplex(double a, double b,
>> > >                                       String name,
>> UnaryOperator<Complex>
>> > > operation,
>> > >
>>  ComplexUnaryOperator<ComplexDouble>
>> > > operation2,
>> > >                                       double x, double y, long
>> maxUlps) {
>> > > }
>> > >
>> > >
>> > > I added my ComplexUnaryOperator as a parameter and am getting the
>> error
>> > of
>> > > having more than 7 parameters in this method.
>> > > Is there anything I can do?
>> >
>> > Assuming that the error is raised by "CheckStyle" (?), this check can
>> > be disabled
>> > on a class by class basis in this configuration file:
>> >   src/main/resources/checkstyle/checkstyle-suppressions.xml
>> >
>>
>> It may be a PMD error too [1]. The PMD error can be suppressed using the
>> file:
>>
>> src/main/resources/pmd/pmd-ruleset.xml
>>
>> However in Numbers there is a checkstyle suppression for ParameterNumber
>> but no suppressions for PMD for ExcessiveParameterList, so you may not
>> need
>> this.
>>
>> Alex
>>
>> [1]
>>
>> https://pmd.github.io/latest/pmd_rules_apex_design.html#excessiveparameterlist
>>
>

Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Sumanth Rajkumar <ra...@gmail.com>.
Hi,

I committed my changes to the test classes into the opened PR and I think
it ran the checks by itself without needing your approval.

Please let me know if there's anything else I need to change.

Thanks,

Sumanth

On Mon, Jul 11, 2022, 7:45 PM Alex Herbert <al...@gmail.com> wrote:

> On Mon, 11 Jul 2022 at 22:56, Gilles Sadowski <gi...@gmail.com>
> wrote:
>
> > Le lun. 11 juil. 2022 à 20:03, Sumanth Rajkumar
> > <ra...@gmail.com> a écrit :
> > >
> > > Hi,
> > >
> > > I have finished updating the test classes, but I am encountering a
> > problem
> > > in the ComplexEdgeCaseTest class.
> > >
> > > private static void assertComplex(double a, double b,
> > >                                       String name,
> UnaryOperator<Complex>
> > > operation,
> > >
>  ComplexUnaryOperator<ComplexDouble>
> > > operation2,
> > >                                       double x, double y, long
> maxUlps) {
> > > }
> > >
> > >
> > > I added my ComplexUnaryOperator as a parameter and am getting the error
> > of
> > > having more than 7 parameters in this method.
> > > Is there anything I can do?
> >
> > Assuming that the error is raised by "CheckStyle" (?), this check can
> > be disabled
> > on a class by class basis in this configuration file:
> >   src/main/resources/checkstyle/checkstyle-suppressions.xml
> >
>
> It may be a PMD error too [1]. The PMD error can be suppressed using the
> file:
>
> src/main/resources/pmd/pmd-ruleset.xml
>
> However in Numbers there is a checkstyle suppression for ParameterNumber
> but no suppressions for PMD for ExcessiveParameterList, so you may not need
> this.
>
> Alex
>
> [1]
>
> https://pmd.github.io/latest/pmd_rules_apex_design.html#excessiveparameterlist
>

Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Alex Herbert <al...@gmail.com>.
On Mon, 11 Jul 2022 at 22:56, Gilles Sadowski <gi...@gmail.com> wrote:

> Le lun. 11 juil. 2022 à 20:03, Sumanth Rajkumar
> <ra...@gmail.com> a écrit :
> >
> > Hi,
> >
> > I have finished updating the test classes, but I am encountering a
> problem
> > in the ComplexEdgeCaseTest class.
> >
> > private static void assertComplex(double a, double b,
> >                                       String name, UnaryOperator<Complex>
> > operation,
> >                                       ComplexUnaryOperator<ComplexDouble>
> > operation2,
> >                                       double x, double y, long maxUlps) {
> > }
> >
> >
> > I added my ComplexUnaryOperator as a parameter and am getting the error
> of
> > having more than 7 parameters in this method.
> > Is there anything I can do?
>
> Assuming that the error is raised by "CheckStyle" (?), this check can
> be disabled
> on a class by class basis in this configuration file:
>   src/main/resources/checkstyle/checkstyle-suppressions.xml
>

It may be a PMD error too [1]. The PMD error can be suppressed using the
file:

src/main/resources/pmd/pmd-ruleset.xml

However in Numbers there is a checkstyle suppression for ParameterNumber
but no suppressions for PMD for ExcessiveParameterList, so you may not need
this.

Alex

[1]
https://pmd.github.io/latest/pmd_rules_apex_design.html#excessiveparameterlist

Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Gilles Sadowski <gi...@gmail.com>.
Le lun. 11 juil. 2022 à 20:03, Sumanth Rajkumar
<ra...@gmail.com> a écrit :
>
> Hi,
>
> I have finished updating the test classes, but I am encountering a problem
> in the ComplexEdgeCaseTest class.
>
> private static void assertComplex(double a, double b,
>                                       String name, UnaryOperator<Complex>
> operation,
>                                       ComplexUnaryOperator<ComplexDouble>
> operation2,
>                                       double x, double y, long maxUlps) {
> }
>
>
> I added my ComplexUnaryOperator as a parameter and am getting the error of
> having more than 7 parameters in this method.
> Is there anything I can do?

Assuming that the error is raised by "CheckStyle" (?), this check can
be disabled
on a class by class basis in this configuration file:
  src/main/resources/checkstyle/checkstyle-suppressions.xml

Regards,
Gilles

> > [...]

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org


Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Sumanth Rajkumar <ra...@gmail.com>.
Hi,

I have finished updating the test classes, but I am encountering a problem
in the ComplexEdgeCaseTest class.

private static void assertComplex(double a, double b,
                                      String name, UnaryOperator<Complex>
operation,
                                      ComplexUnaryOperator<ComplexDouble>
operation2,
                                      double x, double y, long maxUlps) {
}


I added my ComplexUnaryOperator as a parameter and am getting the error of
having more than 7 parameters in this method.
Is there anything I can do?

Thanks,

Sumanth


On Tue, 5 Jul 2022 at 15:50, Sumanth Rajkumar <ra...@gmail.com>
wrote:

> Thanks for the feedback Alex,
>
> > Not a problem. Just write a dedicated method for Complex and a generic
> > method in ComplexFunctions. This small level of code duplication is
> > acceptable where the efficiency of the method is greatly improved by
> > rewriting it, as has been done for the scalar functions.
>
> For proj(), I had to take it out of ComplexFunctions for now to pass the
> coverage checks,
> but I have left the original code for proj() in the Complex class.
>
> > Why should I have to return a ComplexDouble? Since the ComplexConstructor
> > is typed it makes more sense to have the methods that use it to also be
> > typed. These methods should perform an operation that generates a real
> and
> > imaginary part. This is passed to the provided constructor. It should be
> up
> > to the provider of the constructor (i.e. the caller) to decide what to do
> > with the result. By typing to ComplexDouble you are removing that
> > flexibility.
>
> > However there is no requirement to specify what R is, allowing it to be
> > Void. This also decouples the interface from Complex and ComplexDouble.
> > Note that this may make ComplexDouble obsolete which would simplify the
> > current changes.
>
> > I've not applied this change to your entire current diff so there may be
> > some issues, for example with the scalar functions. I am interested to
> see
> > if this would work.
>
> So, I have made all interfaces typed and updated the method signatures
> accordingly.
> Please let me know if there are any problems with these changes.
>
> As for updating the test suite, I am currently working on that right now
> and will let you know as soon as I am done.
>
> Thanks,
>
> Sumanth
>
>
>

Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Sumanth Rajkumar <ra...@gmail.com>.
Thanks for the feedback Alex,

> Not a problem. Just write a dedicated method for Complex and a generic
> method in ComplexFunctions. This small level of code duplication is
> acceptable where the efficiency of the method is greatly improved by
> rewriting it, as has been done for the scalar functions.

For proj(), I had to take it out of ComplexFunctions for now to pass the
coverage checks,
but I have left the original code for proj() in the Complex class.

> Why should I have to return a ComplexDouble? Since the ComplexConstructor
> is typed it makes more sense to have the methods that use it to also be
> typed. These methods should perform an operation that generates a real and
> imaginary part. This is passed to the provided constructor. It should be
up
> to the provider of the constructor (i.e. the caller) to decide what to do
> with the result. By typing to ComplexDouble you are removing that
> flexibility.

> However there is no requirement to specify what R is, allowing it to be
> Void. This also decouples the interface from Complex and ComplexDouble.
> Note that this may make ComplexDouble obsolete which would simplify the
> current changes.

> I've not applied this change to your entire current diff so there may be
> some issues, for example with the scalar functions. I am interested to see
> if this would work.

So, I have made all interfaces typed and updated the method signatures
accordingly.
Please let me know if there are any problems with these changes.

As for updating the test suite, I am currently working on that right now
and will let you know as soon as I am done.

Thanks,

Sumanth

Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Alex Herbert <al...@gmail.com>.
On Mon, 4 Jul 2022 at 23:00, Sumanth Rajkumar <ra...@gmail.com>
wrote:

> Thanks Alex for PR feedback.
> I have incorporated the majority of the requested changes to the PR.
>
> I would like to discuss the remaining points here.
>
> > In
>
> commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/ComplexUnaryOperator.java:
> > Note that by making this a default that creates an instance of Complex
> you impose memory allocation overhead to any call site that just has the
> real and imaginary parts (e.g. a structure storing a list of complex
> numbers using primitive arrays).
> > The ComplexDouble apply(ComplexDouble in,
> ComplexConstructor<ComplexDouble> out) method should be a default that
> calls this function using the real and imaginary parts.
>
> The existing unit test for Complex Projection (proj) is expecting the
> returned result to be the same as the passed input Complex instance except
> for the isInfinite edge case.
> This was the main reason for making the functional interface accept a
> ComplexDouble instead of primitive real and imaginary parts.
>

Passing an existing unit test should not govern a design decision.

The proj method will return the same number if it is not an infinite
complex number. That is all that is required to pass the test, the input
and output should have the exact same real and imaginary parts. Note that
the Complex class is immutable so it can return itself. The test for object
identity can be dropped in a generic case where the output is passed to the
ComplexConstructor.


>
> The additional memory allocation for the input can be eliminated (for the
> example of primitive array backed lists), by combining it with the
> constructed result object.
> We had decided to incur the overhead for the complex Constructor
> result object in the context of functional compositions and thread safety
> discussion before.
> So, passing in a cursor iterator item (that captures the index) as both
> ComplexDouble input and ComplexResult constructor should avoid any
> additional overhead?
>

In this case yes. But in the case of usage outside of the complex package,
for example if the raw real and imaginary arrays are created by a 2D FFT.

At present I do not see a downside to the functional method being the
separate arguments of real and imaginary primitives, other than it
decouples the values and so is not encapsulated. Let's see what others
think.


>
> If we decide to use primitive types for the functional interface, then we
> cannot use it for the projection method.
>

Not a problem. Just write a dedicated method for Complex and a generic
method in ComplexFunctions. This small level of code duplication is
acceptable where the efficiency of the method is greatly improved by
rewriting it, as has been done for the scalar functions.


>
>
> > @FunctionalInterface
> >  public interface ComplexScalarFunction {
> >
> > ComplexDouble apply(ComplexDouble c, double f,
> ComplexConstructor<ComplexDouble> result);
> >
> > }
> > This interface should be typed: the result is accepted by the
> ComplexConstructor and this can be typed.
>
>
> By typed, did you mean to make this a generic interface
> ComplexScalarFunction<T>
> ?
> ComplexUnaryOperator and ComplexBinaryOperator are not generic and
> constrained to ComplexDouble types
>

Why should I have to return a ComplexDouble? Since the ComplexConstructor
is typed it makes more sense to have the methods that use it to also be
typed. These methods should perform an operation that generates a real and
imaginary part. This is passed to the provided constructor. It should be up
to the provider of the constructor (i.e. the caller) to decide what to do
with the result. By typing to ComplexDouble you are removing that
flexibility.

ComplexUnaryOperator extends UnaryOperator<ComplexDouble>

I do not see what extending UnaryOperator provides. It constrains the
interface to having to support composition imposed by Function<T, R>. Note
that composition can avoid the Complex constructor by directly chaining the
output to the next input. Consider this simplification:

public interface ComplexUnaryOperator<R> {
    R apply(double r, double i, ComplexConstructor<R> out);

    default ComplexUnaryOperator<R> andThen(ComplexUnaryOperator<R> after) {
        Objects.requireNonNull(after);
        return (r, i, out) -> apply(r, i, (x, y) -> after.apply(x, y, out));
    }
}

public interface ComplexBinaryOperator<R> {
    R apply(double r1, double i1, double r2, double i2,
ComplexConstructor<R> out);

    default ComplexBinaryOperator<R> andThen(ComplexUnaryOperator<R> after)
{
        Objects.requireNonNull(after);
        return (r1, i1, r2, i2, out) -> apply(r1, i1, r2, i2, (x, y) ->
after.apply(x, y, out));
    }
}

Note that this has some differences from java.util.function during
composition.

In the generic case of java.util.function an object is returned. This is
passed to the next function and may be changed to an object of a different
type. So a composition of Function<T, R> and Function<R, V> becomes
Function<T, V> (via T -> R -> V).

In the complex function case the output will be a (real, imaginary) pair;
this is defined by the requirement to support the ComplexConstructor<R> for
the output. All functions produce this output. Thus during composition the
generic type R specifies the terminating object type. All intermediates are
a primitive pair and no objects are created (lambda functions are created
to channel the values through the composed function). Thus each composition
maintains the same generic type <R> as that specifies the ultimate
destination of the complex number.

However there is no requirement to specify what R is, allowing it to be
Void. This also decouples the interface from Complex and ComplexDouble.
Note that this may make ComplexDouble obsolete which would simplify the
current changes.

I've not applied this change to your entire current diff so there may be
some issues, for example with the scalar functions. I am interested to see
if this would work.


>
>
> > It may be wise to update the test suite to ensure that all tests
> currently applied to Complex are applied to ComplexFunctions using a
> ComplexConstructor other than Complex, for example using a dummy
> implementation:
> >
> > class ComplexNumber implements ComplexConstructor<ComplexNumber> {
> >    // (r, i) members
> >
> >    @Override
> >    public ComplexNumber apply(double r, double i) {
> >        // store (r, i) ...
> >        return this;
> >    }
> > }
> >
> >This will detect the edge cases all pass through to the input constructor
> to create the result. The test should assert the ComplexConstructor
> received the expected values.
>
> For now, shall I copy all the tests from the CStandardTest, CReferenceTest
> and ComplexEdgeCaseTest to ComplexFunctionsTest and modify it to use a
> dummy constructor?
> Also please note as mentioned above for the complex.proj method, currently
> we are not invoking the complex constructor for normal cases.
>

The CReferenceTest is simple to modify since it loads (re, im) pairs for
the input and output. For example just update the functions to test both
Complex and ComplexFunctions at the same time:

    private static void assertOperation(String name,
            UnaryOperator<Complex> operation, long maxUlps) {

to

    private static void assertOperation(String name,
            UnaryOperator<Complex> operation,
            ComplexUnaryOperator<ComplexResult> operation2, long maxUlps) {

with a ComplexResult suitably created to check the result pair is passed to
the result constructor. These assertions should check the (real, imag) pair
in complex is an exact match for the (real, imag) pair passed to the
ComplexResult generated by the constructor, i.e. they are not just equal to
the expected result within a tolerance, as each could be a different result
and still be within tolerance of the expected.

The same can be done for the CStandardTest and the ComplexEdgeCaseTest as
these also test functions using a generic assertion method which accepts
the function to test.

As for the ComplexTest this is harder to refactor. But what is required is
that all tests currently present in ComplexTest should be applied to the
Complex and ComplexFunctions methods. Perhaps start a new test class that
copies ComplexTest and then tests the results using a generic assert method
that accepts two operations: one on Complex, and one on primitive pairs.
This would highlight the current lack of functionality in ComplexFunctions,
for example the subtractFromImaginary function.

Note that in looking for coverage it is useful to be able to run part of
the test suite and generate a report [1]:

mvn clean
mvn test -Dtest=CStandardTest
mvn test -Dtest=CStandardTest#testAcos      (single method)
mvn jacoco:report
open target/site/jacoco/index.html

You should be able to see the coverage of everything that has been run
since the last 'mvn clean' command.

Alex

[1]
https://maven.apache.org/surefire/maven-surefire-plugin/test-mojo.html#test

Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Sumanth Rajkumar <ra...@gmail.com>.
Thanks Alex for PR feedback.
I have incorporated the majority of the requested changes to the PR.

I would like to discuss the remaining points here.

> In
commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/ComplexUnaryOperator.java:
> Note that by making this a default that creates an instance of Complex
you impose memory allocation overhead to any call site that just has the
real and imaginary parts (e.g. a structure storing a list of complex
numbers using primitive arrays).
> The ComplexDouble apply(ComplexDouble in,
ComplexConstructor<ComplexDouble> out) method should be a default that
calls this function using the real and imaginary parts.

The existing unit test for Complex Projection (proj) is expecting the
returned result to be the same as the passed input Complex instance except
for the isInfinite edge case.
This was the main reason for making the functional interface accept a
ComplexDouble instead of primitive real and imaginary parts.

The additional memory allocation for the input can be eliminated (for the
example of primitive array backed lists), by combining it with the
constructed result object.
We had decided to incur the overhead for the complex Constructor
result object in the context of functional compositions and thread safety
discussion before.
So, passing in a cursor iterator item (that captures the index) as both
ComplexDouble input and ComplexResult constructor should avoid any
additional overhead?

If we decide to use primitive types for the functional interface, then we
cannot use it for the projection method.


> @FunctionalInterface
>  public interface ComplexScalarFunction {
>
> ComplexDouble apply(ComplexDouble c, double f,
ComplexConstructor<ComplexDouble> result);
>
> }
> This interface should be typed: the result is accepted by the
ComplexConstructor and this can be typed.


By typed, did you mean to make this a generic interface
ComplexScalarFunction<T>
?
ComplexUnaryOperator and ComplexBinaryOperator are not generic and
constrained to ComplexDouble types


> It may be wise to update the test suite to ensure that all tests
currently applied to Complex are applied to ComplexFunctions using a
ComplexConstructor other than Complex, for example using a dummy
implementation:
>
> class ComplexNumber implements ComplexConstructor<ComplexNumber> {
>    // (r, i) members
>
>    @Override
>    public ComplexNumber apply(double r, double i) {
>        // store (r, i) ...
>        return this;
>    }
> }
>
>This will detect the edge cases all pass through to the input constructor
to create the result. The test should assert the ComplexConstructor
received the expected values.

For now, shall I copy all the tests from the CStandardTest, CReferenceTest
and ComplexEdgeCaseTest to ComplexFunctionsTest and modify it to use a
dummy constructor?
Also please note as mentioned above for the complex.proj method, currently
we are not invoking the complex constructor for normal cases.

Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Alex Herbert <al...@gmail.com>.
On Sun, 26 Jun 2022 at 20:52, Sumanth Rajkumar <ra...@gmail.com>
wrote:

> Hi,
> I have raised PR #113 after rebasing to the master branch with Alex's
> checkstyle changes
>
> As per feedback, I have made the following changes
> a) Added javadoc comments
> b) Ensured test coverage
> c) Renamed accessors on the interface
>

Thanks for the changes. Note that a new PR is not required. You can simply
force push changes to the previous PR. It is covering the same subject.

I've not yet fully read the PR. However the level of abstraction on some of
the simple functions seems excessive. Many of the scalar operations
using applyScalarFunction are one liners that have been abstracted to
multiple layers of function references. Simple operations such as add,
subtract, conjugate, negate, arg (defined as Math.atan2) may be better left
alone. They can be duplicated into the complex functions class if the API
is for public consumption but performance may be impacted by the
abstraction. The code is definitely made less readable.

Also note that you have some double empty lines which should be a
single empty line and then some functions ending with } and no empty line
after. These can be simply fixed using a regular expression to search for
them.

I note that some javadoc is missing for private methods. I have not set
checkstyle to enforce this and the default scope is public. It should at
least be protected, but my preference would be package. I will see if the
rest of the project is OK for this and then update the rule.


>
>
> > Gilles Sadowski <gi...@gmail.com> wrote:
> > In "DComplex", I propose that the accessors be named "real()" and
> > "imag()" (or just
> > "re()" and "im()").  ["DComplex" is not a very satisfying name either...]
>
> For the interface name, shall I change it to Complex64 from DComplex?
>

In c the 'complex' keyword is a suffix:

double complex c1;
float complex c2;
long double complex c3;

In c++ the type is generic (and read as a suffix):

complex<double> c1;
complex<float> c2;

Either of these would be my preference over DComplex or Complex64.


> > Are we sure that all this code needs to be part of the public API?
> > If not, I'd suggest limiting accessibility to "package-private".
>
> Are you referring to the static methods in ComplexFunctions and
> ComplexBiFunctions classes?
> I think they would need to be public for developers to be able to compose
> multiple operations...
>

The static helper functions have been extracted to support all the ISO c99
operations on the list structure of complex numbers.

A list will ideally implement a generic foreach operation. So to apply a
single function only requires making the static functions public. The
alternative is to make the list expose all the ISO c99 operations in its
public API.

To create a composite function that eventually writes back to the list can
be implemented by writing intermediate values to a result which is then
passed to the next operation. This can be satisfied by using the Complex
class. This already exposes all the ISO c99 functions. So perhaps it is not
required to make all the helper functions public for the purpose of
composing multiple operations. But it would be helpful for all the single
operations.


>
> Thanks,
> Sumanth
>
> PS: Noticed master branch unit test failures in numbers-fraction module
>

This has been fixed. Sorry for the mistake.

Alex

Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Sumanth Rajkumar <ra...@gmail.com>.
Hi,
I have raised PR #113 after rebasing to the master branch with Alex's
checkstyle changes

As per feedback, I have made the following changes
a) Added javadoc comments
b) Ensured test coverage
c) Renamed accessors on the interface


> Gilles Sadowski <gi...@gmail.com> wrote:
> In "DComplex", I propose that the accessors be named "real()" and
> "imag()" (or just
> "re()" and "im()").  ["DComplex" is not a very satisfying name either...]

For the interface name, shall I change it to Complex64 from DComplex?

> Are we sure that all this code needs to be part of the public API?
> If not, I'd suggest limiting accessibility to "package-private".

Are you referring to the static methods in ComplexFunctions and
ComplexBiFunctions classes?
I think they would need to be public for developers to be able to compose
multiple operations...

Thanks,
Sumanth

PS: Noticed master branch unit test failures in numbers-fraction module

On Fri, 24 Jun 2022 at 19:43, Gilles Sadowski <gi...@gmail.com> wrote:

> Hello.
>
> Le ven. 24 juin 2022 à 16:59, Sumanth Rajkumar
> <ra...@gmail.com> a écrit :
> >
> > Hi Alex, Gilles, and Matt,
> >
> > I have raised a PR to the complex-gsoc-22 branch and it has been linked
> to
> > the NUMBERS-188 jira.
>
> One tenet of a project such as "Commons" is that everything must be
> documented.[1]
> For the Javadoc comments, please apply the same style as in other source
> files.
>
> Formatting should also be taken care of (to help review, and future
> maintenance).[2]
>
> Are we sure that all this code needs to be part of the public API?  If
> not, I'd suggest
> limiting accessibility to "package-private".
>
> In "DComplex", I propose that the accessors be named "real()" and
> "imag()" (or just
> "re()" and "im()").  ["DComplex" is not a very satisfying name either...]
>
> Thanks,
> Gilles
>
> [1] I'm a bit surprised that the build succeeds despite the missing
> comments.
> [2] E.g. one argument per line improves readability (IMHO).
>
> >
> > [...]
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
> For additional commands, e-mail: dev-help@commons.apache.org
>
>

Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Alex Herbert <al...@gmail.com>.
On Sat, 25 Jun 2022 at 10:07, Alex Herbert <al...@gmail.com> wrote:

>
>
> Checkstyle:
> Checkstyle plugin is configured to failOnViolation. So it is not
> complaining about lack of comments. We are using the following rules:
>
> InvalidJavadocPosition,JavadocMethod,JavadocType,JavadocVariable,JavadocStyle
>
> So the failure of checkstyle to fail is strange. If I take an existing
> file and make some formatting changes it fails the build. If I delete a tag
> from the javadoc it fails the build. If I delete the entire javadoc then
> this passes. So checkstyle checks the existing javadoc but does not fail
> when javadoc is missing. This requires further investigation.
>

This requires extra checks:

MissingJavadocMethod
MissingJavadocPackage
MissingJavadocType

I will add these to our checkstyle configs and fix any failures (since
these are scoped to private by default there may be some missing javadoc).

See:
http://checkstyle.sourceforge.net/config_javadoc.html

Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Alex Herbert <al...@gmail.com>.
On Sat, 25 Jun 2022 at 00:43, Gilles Sadowski <gi...@gmail.com> wrote:

>
> [1] I'm a bit surprised that the build succeeds despite the missing
> comments.
>

So am I. There are a lot of warnings:

Compilation has 2 warnings each about unchecked casts and unchecked method
invocation.

PMD: 115 warnings.
This plugin is not set to fail on violation due to problems throughout the
numbers build. But it does spot all the missing javadoc comments.

Javadoc:
2 warnings for incorrect tags. This plugin could be configured to fail on
warning. The default is failOnError=true and failOnWarning=false. I checked
the rest of the project and there are no warnings. So I changed this to
failOnWarning.

Jacoco:
Rule violations for classes covered and methods covered. But it does not
fail the build. I checked the old travis profile and it did not set
the build to fail when coverage is not met. The travis profile just
submitted the report to coveralls.
There is a note in the pom:

    <!-- Set to true when coverage goals are achieved -->
    <commons.jacoco.haltOnFailure>false</commons.jacoco.haltOnFailure>

I have set this to true since we have met coverage goals across the rest of
the numbers project.

Note: The coverage report from codecov is not yet complete so has not been
posted to the PR. It does show the drop in coverage:
https://codecov.io/github/apache/commons-numbers/commit/203cbc6a505a1d004297115414b2188e21427219

Checkstyle:
Checkstyle plugin is configured to failOnViolation. So it is not
complaining about lack of comments. We are using the following rules:
InvalidJavadocPosition,JavadocMethod,JavadocType,JavadocVariable,JavadocStyle

So the failure of checkstyle to fail is strange. If I take an existing file
and make some formatting changes it fails the build. If I delete a tag from
the javadoc it fails the build. If I delete the entire javadoc then this
passes. So checkstyle checks the existing javadoc but does not fail when
javadoc is missing. This requires further investigation.


You will have to rebase the PR on master to pick up the pom changes. Then
run the default maven goal (in the complex module) and fix the issues.

Alex

Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Gilles Sadowski <gi...@gmail.com>.
Hello.

Le ven. 24 juin 2022 à 16:59, Sumanth Rajkumar
<ra...@gmail.com> a écrit :
>
> Hi Alex, Gilles, and Matt,
>
> I have raised a PR to the complex-gsoc-22 branch and it has been linked to
> the NUMBERS-188 jira.

One tenet of a project such as "Commons" is that everything must be
documented.[1]
For the Javadoc comments, please apply the same style as in other source files.

Formatting should also be taken care of (to help review, and future
maintenance).[2]

Are we sure that all this code needs to be part of the public API?  If
not, I'd suggest
limiting accessibility to "package-private".

In "DComplex", I propose that the accessors be named "real()" and
"imag()" (or just
"re()" and "im()").  ["DComplex" is not a very satisfying name either...]

Thanks,
Gilles

[1] I'm a bit surprised that the build succeeds despite the missing comments.
[2] E.g. one argument per line improves readability (IMHO).

>
> [...]

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org


Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Sumanth Rajkumar <ra...@gmail.com>.
Hi Alex, Gilles, and Matt,

I have raised a PR to the complex-gsoc-22 branch and it has been linked to
the NUMBERS-188 jira.

While the PR is being reviewed, I will start working on NUMBERS-186 (adding
support for list and matrix of Complex numbers).

Thanks,
Sumanth

On Mon, 20 Jun 2022 at 09:43, Sumanth Rajkumar <ra...@gmail.com>
wrote:

> Hello Gilles,
> Thanks!  I will start a new thread for float support.
>
> Thanks
> Sumanth
>
>
> On Mon, Jun 20, 2022, 18:41 Gilles Sadowski <gi...@gmail.com> wrote
>
>>
>> > Also, should we add support for float data type for complex numbers?
>>
>> In the "dev" ML, we customarily aim at focussing each discussion,
>
>
>> Thus could you please start a new thread with this question?
>>
>
>
>

Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Sumanth Rajkumar <ra...@gmail.com>.
Hello Gilles,
Thanks!  I will start a new thread for float support.

Thanks
Sumanth


On Mon, Jun 20, 2022, 18:41 Gilles Sadowski <gi...@gmail.com> wrote

>
> > Also, should we add support for float data type for complex numbers?
>
> In the "dev" ML, we customarily aim at focussing each discussion,


> Thus could you please start a new thread with this question?
>

Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Gilles Sadowski <gi...@gmail.com>.
Hello Sumanth.

Le lun. 20 juin 2022 à 07:05, Sumanth Rajkumar
<ra...@gmail.com> a écrit :
>
> [...]
>
> Also, should we add support for float data type for complex numbers?

In the "dev" ML, we customarily aim at focussing each discussion, so
that people can easily decide (ideally, from the "Subject:" line) if they
want to voice some opinion.  [This also improves searches in the ML
archive.]
Thus could you please start a new thread with this question?
[Note that the relationships between all technical issues related to the
extension of the functionalities around "Complex" is better managed
through links between JIRA issues than by lengthy threads here.]

Thanks,
Gilles

>
> [...]

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org


Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Sumanth Rajkumar <ra...@gmail.com>.
Hi Alex, Matt, and Gilles,

As discussed on the other thread, I have made NUMBERS-186 (for complex
lists work) the child of a parent ticket (NUMBERS-187)

I have created NUMBERS-188 for the functional interfaces and static method
refactoring of the existing instance methods of Complex class.

I hurt my shoulders over the weekend and have been advised few days rest. I
expect to have the PR for NUMBERS-188 ready for review by Thursday.

Also, should we add support for float data type for complex numbers?

If yes, should we reuse static functions that operate on double types and
use Java float-double widening and narrowing conversions?

https://docs.oracle.com/javase/specs/jls/se10/html/jls-5.html#jls-5.1.2

If no, should we have separate static complex functions for float types?

I can raise a ticket for supporting float types based on feedback.

Thanks,
Sumanth


On Thu, 16 Jun 2022 at 08:11, Sumanth Rajkumar <ra...@gmail.com>
wrote:

> > EjML functional interface looks like this
> >
> >  void apply(ComplexDouble in, MutableComplexDouble out)
> >
> Similar to the mutable cursor idea we have been discussing. The Mutable
> object could be an interface?
>
>     Ok
>
> We may need to have more than one implementation of the underlying storage.
> Using interleaved real and imaginary will be more efficient for computation
> on a single number due to cache reads from memory. But the separate arrays
> are going to be useful when writing code with the vector API that requires
> extracting blocks of real or imaginary components.
>
>      Ok
>
>
> >
> >
> > I am still wondering how these functions can be composed. Here are a few
> > ideas....
> >
> > This may need more work..
>
>
> > ComplexResultInterceptor class
>
> It does require a lot of code that may have a far bigger overhead impact
> than just creating a complex result.
>
>
>
>
> I think the overhead of creating the Complex objects for the intermediates
> may be lower than a solution that tries to avoid allocation overhead with
> complexity of ThreadLocal. A performance benchmark would be able to
> determine this so we can look at that in the future.
>
>
>  Ok. Will extend java functions and create complex objects for
> intermediate results
>
>
> It may be useful here to create a branch in the numbers repository for all
> the changes. To keep it simple perhaps a 'develop' branch can be used that
> you can make your changes against.
>
>
> Ok.
> I can raise a PR to develop with the above changes. I will follow Alex's
> latest update to the Developer Guide for pull requests.
>
>
> Thanks,
>
> Sumanth
>
>
> On Wed, 15 Jun 2022 at 13:58, Alex Herbert <al...@gmail.com>
> wrote:
>
>> On Wed, 15 Jun 2022 at 17:38, Sumanth Rajkumar <
>> rajkumar.sumanth@gmail.com>
>> wrote:
>>
>> > Hi Alex,
>> >
>> > 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.
>> >
>> > a) I reviewed EJML data naming conventions, this is what they follow:
>> >
>> >
>> >
>> https://github.com/lessthanoptimal/ejml#procedural-api-matrix-and-class-names
>> >     Should we follow this approach as well?
>> >    I wasn't thinking about implementing ND right now, maybe I can during
>> > Phase 2?
>> >
>>
>> OK.
>>
>>
>> >
>> > b) EJML only supports 2D matrices with vector (1XN) being a special
>> case,
>> > should I do that too?
>> >
>>
>> OK.
>>
>>
>> >
>> > c) EJML uses a mutable ComplexResult, they use a single mutable class
>> for
>> > both input and output and return void
>> >
>> >     Here is the link to their implementation:
>> >
>> >
>> >
>> https://github.com/lessthanoptimal/ejml/blob/SNAPSHOT/main/ejml-core/src/org/ejml/ops/ComplexMath_F64.java
>> >
>> > Their functional interface looks like this
>> >
>> >  void apply(ComplexDouble in, MutableComplexDouble out)
>> >
>>
>> Similar to the mutable cursor idea we have been discussing. The Mutable
>> object could be an interface?
>>
>>
>> >
>> > d) For dense Matrix internal storage, I'm planning on using separate
>> arrays
>> > (or a single array where the first half of the array will be real and
>> the
>> > second half will be imaginary) instead of alternating real and imaginary
>> > because it allows us to optimize space for pure imaginary or real
>> matrices.
>> >
>> > Is that ok?
>> >
>>
>> We may need to have more than one implementation of the underlying
>> storage.
>> Using interleaved real and imaginary will be more efficient for
>> computation
>> on a single number due to cache reads from memory. But the separate arrays
>> are going to be useful when writing code with the vector API that requires
>> extracting blocks of real or imaginary components.
>>
>>
>> >
>> >
>> > I am still wondering how these functions can be composed. Here are a few
>> > ideas....
>> >
>> >
>> > This may need more work..
>> >
>> >
>> > I have provided an approach below using an intermediate
>> > ComplexResultInterceptor class that is thread-safe and minimizes object
>> > creation using thread local and stacks. It also doesn't require
>> > ComplexDouble constraint for the generic R result type.
>> >
>>
>> It does require a lot of code that may have a far bigger overhead impact
>> than just creating a complex result.
>>
>> Since thread safety requires an intermediate be stored then it may make
>> more sense to expand the API to allow operations on a list using a unary
>> operator of complex:
>>
>> UnaryOperator<Complex> op;
>>
>> You can then create your function to work on a complex number and chain
>> them together using the standard java 8 functions. This could be supported
>> for the ISO c99 functions by just using Complex. Unfortunately this does
>> not work as the UnaryOperator has not overridden andThen and compose for
>> the single argument. This is valid:
>>
>> UnaryOperator<Complex> op = Complex::sqrt;
>> // These compose to Function
>> Function<Complex, Complex> op2 = op.andThen(Complex::conj);
>> Function<Complex, Complex> op3 = op.compose(Complex::conj);
>>
>> // Invalid to assign back to UnaryOperator
>> op = op.andThen(Complex::conj);
>> // OK with a cast
>> op = (UnaryOperator<Complex>) op.andThen(Complex::conj);
>>
>> // In use:
>> // op could be declared as a UnaryOperator<Complex> or Function<Complex,
>> Complex> to just use the JDK functions as is with full support for
>> chaining.
>> list.forEach(op);
>>
>> To avoid the cast you can extend Function to add e.g.
>>
>>         default UnaryOperator<T> andThen(UnaryOperator<T> after) {
>>             Objects.requireNonNull(after);
>>             return (T t) -> after.apply(apply(t));
>>         }
>>
>> I do not know why the JDK has not done this. It seems to compile on my
>> machine but I've not tested it.
>>
>> I think the overhead of creating the Complex objects for the intermediates
>> may be lower than a solution that tries to avoid allocation overhead with
>> complexity of ThreadLocal. A performance benchmark would be able to
>> determine this so we can look at that in the future.
>>
>>
>> > I'm working on this right now, I've refactored most of the existing
>> > instance methods and I should be done by tomorrow
>> >
>>
>> OK.
>>
>> It may be useful here to create a branch in the numbers repository for all
>> the changes. To keep it simple perhaps a 'develop' branch can be used that
>> you can make your changes against.
>>
>> Alex
>>
>

Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Sumanth Rajkumar <ra...@gmail.com>.
> EjML functional interface looks like this
>
>  void apply(ComplexDouble in, MutableComplexDouble out)
>
Similar to the mutable cursor idea we have been discussing. The Mutable
object could be an interface?

    Ok

We may need to have more than one implementation of the underlying storage.
Using interleaved real and imaginary will be more efficient for computation
on a single number due to cache reads from memory. But the separate arrays
are going to be useful when writing code with the vector API that requires
extracting blocks of real or imaginary components.

     Ok


>
>
> I am still wondering how these functions can be composed. Here are a few
> ideas....
>
> This may need more work..


> ComplexResultInterceptor class

It does require a lot of code that may have a far bigger overhead impact
than just creating a complex result.




I think the overhead of creating the Complex objects for the intermediates
may be lower than a solution that tries to avoid allocation overhead with
complexity of ThreadLocal. A performance benchmark would be able to
determine this so we can look at that in the future.


 Ok. Will extend java functions and create complex objects for intermediate
results


It may be useful here to create a branch in the numbers repository for all
the changes. To keep it simple perhaps a 'develop' branch can be used that
you can make your changes against.


Ok.
I can raise a PR to develop with the above changes. I will follow Alex's
latest update to the Developer Guide for pull requests.


Thanks,

Sumanth


On Wed, 15 Jun 2022 at 13:58, Alex Herbert <al...@gmail.com> wrote:

> On Wed, 15 Jun 2022 at 17:38, Sumanth Rajkumar <rajkumar.sumanth@gmail.com
> >
> wrote:
>
> > Hi Alex,
> >
> > 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.
> >
> > a) I reviewed EJML data naming conventions, this is what they follow:
> >
> >
> >
> https://github.com/lessthanoptimal/ejml#procedural-api-matrix-and-class-names
> >     Should we follow this approach as well?
> >    I wasn't thinking about implementing ND right now, maybe I can during
> > Phase 2?
> >
>
> OK.
>
>
> >
> > b) EJML only supports 2D matrices with vector (1XN) being a special case,
> > should I do that too?
> >
>
> OK.
>
>
> >
> > c) EJML uses a mutable ComplexResult, they use a single mutable class for
> > both input and output and return void
> >
> >     Here is the link to their implementation:
> >
> >
> >
> https://github.com/lessthanoptimal/ejml/blob/SNAPSHOT/main/ejml-core/src/org/ejml/ops/ComplexMath_F64.java
> >
> > Their functional interface looks like this
> >
> >  void apply(ComplexDouble in, MutableComplexDouble out)
> >
>
> Similar to the mutable cursor idea we have been discussing. The Mutable
> object could be an interface?
>
>
> >
> > d) For dense Matrix internal storage, I'm planning on using separate
> arrays
> > (or a single array where the first half of the array will be real and the
> > second half will be imaginary) instead of alternating real and imaginary
> > because it allows us to optimize space for pure imaginary or real
> matrices.
> >
> > Is that ok?
> >
>
> We may need to have more than one implementation of the underlying storage.
> Using interleaved real and imaginary will be more efficient for computation
> on a single number due to cache reads from memory. But the separate arrays
> are going to be useful when writing code with the vector API that requires
> extracting blocks of real or imaginary components.
>
>
> >
> >
> > I am still wondering how these functions can be composed. Here are a few
> > ideas....
> >
> >
> > This may need more work..
> >
> >
> > I have provided an approach below using an intermediate
> > ComplexResultInterceptor class that is thread-safe and minimizes object
> > creation using thread local and stacks. It also doesn't require
> > ComplexDouble constraint for the generic R result type.
> >
>
> It does require a lot of code that may have a far bigger overhead impact
> than just creating a complex result.
>
> Since thread safety requires an intermediate be stored then it may make
> more sense to expand the API to allow operations on a list using a unary
> operator of complex:
>
> UnaryOperator<Complex> op;
>
> You can then create your function to work on a complex number and chain
> them together using the standard java 8 functions. This could be supported
> for the ISO c99 functions by just using Complex. Unfortunately this does
> not work as the UnaryOperator has not overridden andThen and compose for
> the single argument. This is valid:
>
> UnaryOperator<Complex> op = Complex::sqrt;
> // These compose to Function
> Function<Complex, Complex> op2 = op.andThen(Complex::conj);
> Function<Complex, Complex> op3 = op.compose(Complex::conj);
>
> // Invalid to assign back to UnaryOperator
> op = op.andThen(Complex::conj);
> // OK with a cast
> op = (UnaryOperator<Complex>) op.andThen(Complex::conj);
>
> // In use:
> // op could be declared as a UnaryOperator<Complex> or Function<Complex,
> Complex> to just use the JDK functions as is with full support for
> chaining.
> list.forEach(op);
>
> To avoid the cast you can extend Function to add e.g.
>
>         default UnaryOperator<T> andThen(UnaryOperator<T> after) {
>             Objects.requireNonNull(after);
>             return (T t) -> after.apply(apply(t));
>         }
>
> I do not know why the JDK has not done this. It seems to compile on my
> machine but I've not tested it.
>
> I think the overhead of creating the Complex objects for the intermediates
> may be lower than a solution that tries to avoid allocation overhead with
> complexity of ThreadLocal. A performance benchmark would be able to
> determine this so we can look at that in the future.
>
>
> > I'm working on this right now, I've refactored most of the existing
> > instance methods and I should be done by tomorrow
> >
>
> OK.
>
> It may be useful here to create a branch in the numbers repository for all
> the changes. To keep it simple perhaps a 'develop' branch can be used that
> you can make your changes against.
>
> Alex
>

Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Alex Herbert <al...@gmail.com>.
On Wed, 15 Jun 2022 at 17:38, Sumanth Rajkumar <ra...@gmail.com>
wrote:

> Hi Alex,
>
> 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.
>
> a) I reviewed EJML data naming conventions, this is what they follow:
>
>
> https://github.com/lessthanoptimal/ejml#procedural-api-matrix-and-class-names
>     Should we follow this approach as well?
>    I wasn't thinking about implementing ND right now, maybe I can during
> Phase 2?
>

OK.


>
> b) EJML only supports 2D matrices with vector (1XN) being a special case,
> should I do that too?
>

OK.


>
> c) EJML uses a mutable ComplexResult, they use a single mutable class for
> both input and output and return void
>
>     Here is the link to their implementation:
>
>
> https://github.com/lessthanoptimal/ejml/blob/SNAPSHOT/main/ejml-core/src/org/ejml/ops/ComplexMath_F64.java
>
> Their functional interface looks like this
>
>  void apply(ComplexDouble in, MutableComplexDouble out)
>

Similar to the mutable cursor idea we have been discussing. The Mutable
object could be an interface?


>
> d) For dense Matrix internal storage, I'm planning on using separate arrays
> (or a single array where the first half of the array will be real and the
> second half will be imaginary) instead of alternating real and imaginary
> because it allows us to optimize space for pure imaginary or real matrices.
>
> Is that ok?
>

We may need to have more than one implementation of the underlying storage.
Using interleaved real and imaginary will be more efficient for computation
on a single number due to cache reads from memory. But the separate arrays
are going to be useful when writing code with the vector API that requires
extracting blocks of real or imaginary components.


>
>
> I am still wondering how these functions can be composed. Here are a few
> ideas....
>
>
> This may need more work..
>
>
> I have provided an approach below using an intermediate
> ComplexResultInterceptor class that is thread-safe and minimizes object
> creation using thread local and stacks. It also doesn't require
> ComplexDouble constraint for the generic R result type.
>

It does require a lot of code that may have a far bigger overhead impact
than just creating a complex result.

Since thread safety requires an intermediate be stored then it may make
more sense to expand the API to allow operations on a list using a unary
operator of complex:

UnaryOperator<Complex> op;

You can then create your function to work on a complex number and chain
them together using the standard java 8 functions. This could be supported
for the ISO c99 functions by just using Complex. Unfortunately this does
not work as the UnaryOperator has not overridden andThen and compose for
the single argument. This is valid:

UnaryOperator<Complex> op = Complex::sqrt;
// These compose to Function
Function<Complex, Complex> op2 = op.andThen(Complex::conj);
Function<Complex, Complex> op3 = op.compose(Complex::conj);

// Invalid to assign back to UnaryOperator
op = op.andThen(Complex::conj);
// OK with a cast
op = (UnaryOperator<Complex>) op.andThen(Complex::conj);

// In use:
// op could be declared as a UnaryOperator<Complex> or Function<Complex,
Complex> to just use the JDK functions as is with full support for chaining.
list.forEach(op);

To avoid the cast you can extend Function to add e.g.

        default UnaryOperator<T> andThen(UnaryOperator<T> after) {
            Objects.requireNonNull(after);
            return (T t) -> after.apply(apply(t));
        }

I do not know why the JDK has not done this. It seems to compile on my
machine but I've not tested it.

I think the overhead of creating the Complex objects for the intermediates
may be lower than a solution that tries to avoid allocation overhead with
complexity of ThreadLocal. A performance benchmark would be able to
determine this so we can look at that in the future.


> I'm working on this right now, I've refactored most of the existing
> instance methods and I should be done by tomorrow
>

OK.

It may be useful here to create a branch in the numbers repository for all
the changes. To keep it simple perhaps a 'develop' branch can be used that
you can make your changes against.

Alex

Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Sumanth Rajkumar <ra...@gmail.com>.
Hi Alex,

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.

a) I reviewed EJML data naming conventions, this is what they follow:

https://github.com/lessthanoptimal/ejml#procedural-api-matrix-and-class-names
    Should we follow this approach as well?
   I wasn't thinking about implementing ND right now, maybe I can during
Phase 2?

b) EJML only supports 2D matrices with vector (1XN) being a special case,
should I do that too?

c) EJML uses a mutable ComplexResult, they use a single mutable class for
both input and output and return void

    Here is the link to their implementation:

https://github.com/lessthanoptimal/ejml/blob/SNAPSHOT/main/ejml-core/src/org/ejml/ops/ComplexMath_F64.java

Their functional interface looks like this

 void apply(ComplexDouble in, MutableComplexDouble out)

d) For dense Matrix internal storage, I'm planning on using separate arrays
(or a single array where the first half of the array will be real and the
second half will be imaginary) instead of alternating real and imaginary
because it allows us to optimize space for pure imaginary or real matrices.

Is that ok?


I am still wondering how these functions can be composed. Here are a few
ideas....


This may need more work..


I have provided an approach below using an intermediate
ComplexResultInterceptor class that is thread-safe and minimizes object
creation using thread local and stacks. It also doesn't require
ComplexDouble constraint for the generic R result type.

@FunctionalInterface
public interface ComplexFunction<R> {

    default R apply(Complex c, ComplexResult<R> result) {
        return apply(c.real(), c.imag(), result);
    }
    R apply(double r, double i, ComplexResult<R> result);

    default <U> ComplexFunction<U> thenApply(ComplexFunction<U>
afterFunction) {
        return (x, y, afterResult) -> {
            ComplexResultInterceptor<R> interceptor =
ComplexResultInterceptor.TLOCAL_ResultInterceptor.get();
            interceptor.pushResultProvider(afterFunction, afterResult);
            apply(x, y, interceptor);
            return interceptor.popResult();
        };
    }
}

public class ComplexResultInterceptor<R> implements ComplexResult<R> {

    private Stack<ComplexResult<?>> afterResultProviderStack = new
Stack<>();
    private Stack<ComplexFunction<?>> afterFunctionStack= new Stack<>();
    private Stack<Object> afterResult = new Stack<>();

    public static final ThreadLocal<ComplexResultInterceptor>
TLOCAL_ResultInterceptor = ThreadLocal.withInitial(() -> new
ComplexResultInterceptor());

    private static final AtomicLong counter = new AtomicLong();

    private ComplexResultInterceptor() {
        System.out.println("Allocating ComplexResultInterceptor # " +
counter.incrementAndGet());
    }

    public <U> void pushResultProvider(ComplexFunction<U> func,
ComplexResult<U> provider) {
        afterFunctionStack.push(func);
        afterResultProviderStack.push(provider);
    }

    @Override
    public R apply(double r, double i) {
        ComplexFunction after = afterFunctionStack.pop();
        ComplexResult resultProvider = afterResultProviderStack.pop();
        afterResult.push(after.apply(r, i, resultProvider));
        return null;
    }

    public <U> U popResult() {
        return (U) afterResult.pop();
    }
}

Here is a link to the unit test class that ran successfully and the
ComplexResultInterceptor class:
https://github.com/sumanth-rajkumar/commons-numbers/blob/sumanth-gsoc-22-array_refactor/commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/ComplexComposeTest.java
https://github.com/sumanth-rajkumar/commons-numbers/blob/sumanth-gsoc-22-array_refactor/commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/ComplexResultInterceptor.java


> 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.


I'm working on this right now, I've refactored most of the existing
instance methods and I should be done by tomorrow

Thanks,

Sumanth


On Mon, 13 Jun 2022 at 06:26, Alex Herbert <al...@gmail.com> wrote:

> On Mon, 13 Jun 2022 at 07:47, Sumanth Rajkumar <rajkumar.sumanth@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
>

Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Alex Herbert <al...@gmail.com>.
On Mon, 13 Jun 2022 at 07:47, Sumanth Rajkumar <ra...@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

Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Sumanth Rajkumar <ra...@gmail.com>.
Thanks Alex for the detailed feedback.

As GSOC Phase 1 has started, for the existing C99 complex functions in
Complex class, I will refactor using the item based functional interface
approach.

I can look at a Range based approach later if I have time.
As you mentioned the C99 functions cannot take advantage of the limited
Java 16 Vector API features.
However, it may be possible to speed up C99 functions using GPUs based on
quick look at documentation here

https://aparapi.com/introduction/getting-started.html
https://aparapi.com/documentation/kernel-guidelines.html
https://aparapi.com/documentation/aparapi-patterns.html
https://git.qoto.org/aparapi/aparapi-examples/-/blob/master/examples/movie/src/com/amd/aparapi/examples/movie/AparapiSolution.java


For Phase 1, I propose to do the following


1) Introduce ComplexDouble, ComplexDoubleVector and ComplexDoubleMatrix
interfaces
    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);

}



3) Add ComplexDouble interface to Complex class.
   Add ComplexDoubleVectorImpl (primitive double array backing)
implementation for ComplexDoubleVector interface
   Add ComplexDoubleMatrixImpl (primitive double array backing)
implementation for ComplexDoubleMatrix interface

4) Refactor existing instance methods in Complex class as static functions
in ComplexDoubleFunctions class using functional interface signatures


5) Add concurrency support for operations on Vector and Matrix objects


interface ComplexDoubleVector {

      default ComplexDoubleVector apply(ComplexDoubleFunction<Void>
function) {
return apply(function, 0, size(), 1);
 }

     default ComplexDoubleVector apply(ComplexDoubleFunction<Void>
function, int concurrency) {
         return apply(function, 0, size(), 1);
 }

    ComplexDoubleVector apply(ComplexDoubleFunction<Void> function, int
start, int length, int concurrency)
}

ComplexDoubleMatrixImpl and ComplexDoubleVectorImpl implement concurrency
(if concurrency > 1) by partitioning the data and execute using fork join
thread pool (No use of streams)


6) Unit tests for Vector and Matrix implementations

Let me know if you need anything else or reduce the scope for phase 1

Thanks
Sumanth

On Sun, 12 Jun 2022 at 20:08, Alex Herbert <al...@gmail.com> wrote:

> On Sun, 12 Jun 2022 at 16:55, Sumanth Rajkumar <rajkumar.sumanth@gmail.com
> >
> wrote:
>
> >
> >
> > I have provided both approaches. Functional interfaces that operate on
> > single complex and on Arrays
> >
>
> Thanks for the examples.
>
>
> > <-- SNIP -->
> >
> >   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)
> >
>
> Possible mix up between lambda and method reference? I certainly would not
> want to write a lambda function (anonymous method) that is responsible for
> iteration; I would stick to providing a simple unary operator that can be
> passed to a list to act on each member.
>
> Re: Iteration logic in the array method. I am not sure about this. If the
> underlying data is not linear (e.g. a 2D double[][]) then performing a
> range based method on it may not be optimal. It should be up to the
> implementation to decide the optimal iteration over the underlying data,
> possibly specialising a spliterator to split based on stripes of the
> underlying data.
>
> Anyway, this set of examples for item-based or range-based approaches
> confers no benefit to the range based approach. It just does a potentially
> sub-optimal iteration of a unary operator on each item. But this is due to
> the example not being a true range based operation. I think the range based
> API should be based around operations that require access to more than one
> element in the range in order to function. For example this would be
> transform operations.
>
> All the other examples we have act on single complex numbers. In this case
> an operator is provided to act on the complex and create a complex result.
> This can be passed to the container which will optimally iterate over the
> data, or sub-range of, and apply the operation.
>
> One issue is parallelisation using a stream. By its nature the stream acts
> on single items; the results are then collated at the end. The stream does
> not hold indices for each item. So you cannot use a terminal foreach()
> method on each sub-range created by splitting and write back to the
> original storage. The stream would have to be declared as ordered to
> maintain the original order for the output and this can be collated at the
> end to new storage.
>
> To use the stream API with a parallel implementation and zero-allocation
> for the terminal operation would require the stream contains an item that
> allows read and write. So you can use ComplexDoubleArray:
>
> Stream<ComplexDoubleArray> s;
> Consumer<ComplexDoubleArray> op;
> s.forEach(op);
>
> Here the operator has to have an array that knows its start and length.
> This would be more like a buffer. Each terminal forEach would require a
> different instance of ComplexDoubleArray that holds its own start and
> length. These may be accessed directly or via an iterator that provides
> read and write to each element in the range. The ComplexDoubleArray can
> wrap the same underlying data so the only allocation is the object in the
> stream wrapping the data and a range to act on. This would be N objects
> allocated equal to the number of threads.
>
> Then you write a function that accepts a ComplexDoubleArray and writes back
> to the same array. I do not see how this is any better than streaming a
> mutable complex:
>
> Stream<ComplexNumber> s;
> Consumer<ComplexNumber> op;
> s.forEach(op);
>
> ComplexNumber simply has read/write for real and imaginary parts and your
> operator can do what it requires. Here ComplexNumber is some object
> allocated once in the forEach method that acts as a cursor over the
> underlying data.
>
> However the stream examples can be misused. The ultimate method can accept
> the stream objects and use them for something else. For example the
> ComplexDoubleArray covering part of the range can then be added to
> (assuming the ComplexDoubleArray is also a List<Complex>). So the sub-range
> should be a correct subList implementation. The ComplexNumber cursor could
> be added to a List<ComplexNumber> which would have many entries but
> ultimately only N actual numbers (N=parallel threads) if the ComplexNumber
> was a recycled cursor object. So the specialised stream methods should not
> be public.
>
> Instead you could create your operators using factory methods. Here are
> examples for single-thread and parallel implementation. The parallel
> implementation uses the stream API to efficiently divide the range.
>
> public class ComplexFunctions {
>     public static ComplexDoubleUnaryOperator create(ComplexFunction<Void>
> operator) {
>         return new ComplexDoubleUnaryOperator() {
>             @Override
>             public ComplexDoubleArray apply(ComplexDoubleArray input,
> ComplexDoubleArray result) {
>                 for (int i = 0; i < input.size(); i++) {
>                     final int index = i;
>                     ComplexDouble c = input.get(i);
>                     operator.apply(c.real(), c.imag(), (re, im) -> {
>                         result.setValue(index, re, im);
>                         return null;
>                     });
>                 }
>                 return result;
>             }
>         };
>     }
>
>     public static ComplexDoubleUnaryOperator parallel(ComplexFunction<Void>
> operator) {
>         return new ComplexDoubleUnaryOperator() {
>             @Override
>             public ComplexDoubleArray apply(ComplexDoubleArray input,
> ComplexDoubleArray result) {
>                 IntStream.range(0, input.size()).parallel().forEach(i -> {
>                     ComplexDouble c = input.get(i);
>                     operator.apply(c.real(), c.imag(), (re, im) -> {
>                         result.setValue(i, re, im);
>                         return null;
>                     });
>                 });
>                 return result;
>             }
>         };
>     }
> }
>
> All this requires is the ComplexDoubleArray to provide efficient get/set
> methods with an index.
>
> ComplexDoubleArray a;
> a.apply(ComplexFunctions.create(ComplexFunctions::conj));
> a.apply(ComplexFunctions.parallel(ComplexFunctions::conj));
> a.apply(ComplexFunctions2.parallel(
>     (r, i, result) -> result.apply(r, 0)));
>
> Note that here we can reuse all the ISO c99 functions that operate on a
> single number. They need not be re-coded for each
> ComplexDoubleUnaryOperator.
>
> So I am seeing a requirement to have the item based API. The range based
> API needs more concrete implementation examples to solidify how it should
> work.
>
>
>
> >
> > 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
> >
>
> There may be issues with streams regarding the choice of the ranges to
> stream in parallel. Ideally the container should control how the stream can
> be split. In my example above using a simple range split with an index in
> an IntStream the split may be sub-optimal.
>
>
> >
> > The developer could choose explicitly to call
> > list.apply(ComplexFunctions::conj) or
> > list.apply(ComplexParallelFunctions::conj)
> >
>
> I am not sure we want to provide all the ISO c99 implementations in
> duplicate. They are defined on Complex and should be implemented once. The
> list is just a container to allow efficient storage. I do not think it
> should define the ISO c99 functions in the interface.
>
>
> >
> >
> > 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
> >
>
> It is unusual to have setters return a new object. This is what is required
> for Complex to implement ComplexDoubleArray setters to allow the operation
> to set the result. Essentially this requires the immutable final class
> Complex to implement an interface which is for a mutable object. So code
> reuse here is polluting the API.
>
>
> > 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
>
>
> But the DoubleFFT_1D object is created with n = size of data. This uses
> precomputation for the size of arguments that will be transformed. Note
> that the object is in fact thread safe and can be reused after construction
> in concurrent threads. But the data passed to it must be of the correct
> size. In this case you do not process in parallel using a stream. The
> sub-ranges are carefully chosen and processed using a common thread pool
> created by JTransforms. The parallelism is configured based on the size of
> the data, below a certain size only a single thread is used.
>
>
> >
> > We can add more methods to ComplexDoubleArray to convert/to from for
> other
> > providers as needed such as DoubleBuffer formats etc..
> >
>
> A utility class to map the ComplexDoubleArray to various output formats,
> perhaps specified by an enum (since there are not that many possible
> formats in 1D). But what of ND data? Thus far we have only considered how
> to operate on a 1D linear range. Conversions would require a way to specify
> the current format and the output format regarding striped (reals then
> imaginary), interleaved, paired arrays, etc. for all dimensions. If ND is
> represented in a linear array, this requires knowledge of the packing of
> the data, e.g.: index = y * maxx + x.
>
> There are typically such data structures in image libraries, often dealing
> with 5D data, that do this that could form inspiration. Typically data is
> arranged in a way that is optimal for the application and there are many
> possible combinations. E.g. image data is often multiples of 2D XY data, so
> may be a float[][] representing XYZ, Channel and Time. In the case of 2D
> image a FFT is performed by 2 1D transforms so you extract strips of X
> data, transform, then extract strips of Y from the partially transformed
> XY, and repeat.
>
> It may be that specifying a generic API for range operations on ND data may
> not be possible. It would require further reading to narrow down possible
> scope.
>
>
> >
> >
> > > 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.
> >
>
> The Vector API is for more than 1. The javadoc examples note that the
> number of lanes is typically 4 or 8. Any elements remaining after
> processing multiples of the lane count will be processed in a sequential
> loop using non-Vector API code, i.e. the method is coded twice. So a
> length=1 array is never going to use the Vector API.
>
> Alex
>

Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Alex Herbert <al...@gmail.com>.
On Sun, 12 Jun 2022 at 16:55, Sumanth Rajkumar <ra...@gmail.com>
wrote:

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

Thanks for the examples.


> <-- SNIP -->
>
>   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)
>

Possible mix up between lambda and method reference? I certainly would not
want to write a lambda function (anonymous method) that is responsible for
iteration; I would stick to providing a simple unary operator that can be
passed to a list to act on each member.

Re: Iteration logic in the array method. I am not sure about this. If the
underlying data is not linear (e.g. a 2D double[][]) then performing a
range based method on it may not be optimal. It should be up to the
implementation to decide the optimal iteration over the underlying data,
possibly specialising a spliterator to split based on stripes of the
underlying data.

Anyway, this set of examples for item-based or range-based approaches
confers no benefit to the range based approach. It just does a potentially
sub-optimal iteration of a unary operator on each item. But this is due to
the example not being a true range based operation. I think the range based
API should be based around operations that require access to more than one
element in the range in order to function. For example this would be
transform operations.

All the other examples we have act on single complex numbers. In this case
an operator is provided to act on the complex and create a complex result.
This can be passed to the container which will optimally iterate over the
data, or sub-range of, and apply the operation.

One issue is parallelisation using a stream. By its nature the stream acts
on single items; the results are then collated at the end. The stream does
not hold indices for each item. So you cannot use a terminal foreach()
method on each sub-range created by splitting and write back to the
original storage. The stream would have to be declared as ordered to
maintain the original order for the output and this can be collated at the
end to new storage.

To use the stream API with a parallel implementation and zero-allocation
for the terminal operation would require the stream contains an item that
allows read and write. So you can use ComplexDoubleArray:

Stream<ComplexDoubleArray> s;
Consumer<ComplexDoubleArray> op;
s.forEach(op);

Here the operator has to have an array that knows its start and length.
This would be more like a buffer. Each terminal forEach would require a
different instance of ComplexDoubleArray that holds its own start and
length. These may be accessed directly or via an iterator that provides
read and write to each element in the range. The ComplexDoubleArray can
wrap the same underlying data so the only allocation is the object in the
stream wrapping the data and a range to act on. This would be N objects
allocated equal to the number of threads.

Then you write a function that accepts a ComplexDoubleArray and writes back
to the same array. I do not see how this is any better than streaming a
mutable complex:

Stream<ComplexNumber> s;
Consumer<ComplexNumber> op;
s.forEach(op);

ComplexNumber simply has read/write for real and imaginary parts and your
operator can do what it requires. Here ComplexNumber is some object
allocated once in the forEach method that acts as a cursor over the
underlying data.

However the stream examples can be misused. The ultimate method can accept
the stream objects and use them for something else. For example the
ComplexDoubleArray covering part of the range can then be added to
(assuming the ComplexDoubleArray is also a List<Complex>). So the sub-range
should be a correct subList implementation. The ComplexNumber cursor could
be added to a List<ComplexNumber> which would have many entries but
ultimately only N actual numbers (N=parallel threads) if the ComplexNumber
was a recycled cursor object. So the specialised stream methods should not
be public.

Instead you could create your operators using factory methods. Here are
examples for single-thread and parallel implementation. The parallel
implementation uses the stream API to efficiently divide the range.

public class ComplexFunctions {
    public static ComplexDoubleUnaryOperator create(ComplexFunction<Void>
operator) {
        return new ComplexDoubleUnaryOperator() {
            @Override
            public ComplexDoubleArray apply(ComplexDoubleArray input,
ComplexDoubleArray result) {
                for (int i = 0; i < input.size(); i++) {
                    final int index = i;
                    ComplexDouble c = input.get(i);
                    operator.apply(c.real(), c.imag(), (re, im) -> {
                        result.setValue(index, re, im);
                        return null;
                    });
                }
                return result;
            }
        };
    }

    public static ComplexDoubleUnaryOperator parallel(ComplexFunction<Void>
operator) {
        return new ComplexDoubleUnaryOperator() {
            @Override
            public ComplexDoubleArray apply(ComplexDoubleArray input,
ComplexDoubleArray result) {
                IntStream.range(0, input.size()).parallel().forEach(i -> {
                    ComplexDouble c = input.get(i);
                    operator.apply(c.real(), c.imag(), (re, im) -> {
                        result.setValue(i, re, im);
                        return null;
                    });
                });
                return result;
            }
        };
    }
}

All this requires is the ComplexDoubleArray to provide efficient get/set
methods with an index.

ComplexDoubleArray a;
a.apply(ComplexFunctions.create(ComplexFunctions::conj));
a.apply(ComplexFunctions.parallel(ComplexFunctions::conj));
a.apply(ComplexFunctions2.parallel(
    (r, i, result) -> result.apply(r, 0)));

Note that here we can reuse all the ISO c99 functions that operate on a
single number. They need not be re-coded for each
ComplexDoubleUnaryOperator.

So I am seeing a requirement to have the item based API. The range based
API needs more concrete implementation examples to solidify how it should
work.



>
> 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
>

There may be issues with streams regarding the choice of the ranges to
stream in parallel. Ideally the container should control how the stream can
be split. In my example above using a simple range split with an index in
an IntStream the split may be sub-optimal.


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

I am not sure we want to provide all the ISO c99 implementations in
duplicate. They are defined on Complex and should be implemented once. The
list is just a container to allow efficient storage. I do not think it
should define the ISO c99 functions in the interface.


>
>
> 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
>

It is unusual to have setters return a new object. This is what is required
for Complex to implement ComplexDoubleArray setters to allow the operation
to set the result. Essentially this requires the immutable final class
Complex to implement an interface which is for a mutable object. So code
reuse here is polluting the API.


> 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


But the DoubleFFT_1D object is created with n = size of data. This uses
precomputation for the size of arguments that will be transformed. Note
that the object is in fact thread safe and can be reused after construction
in concurrent threads. But the data passed to it must be of the correct
size. In this case you do not process in parallel using a stream. The
sub-ranges are carefully chosen and processed using a common thread pool
created by JTransforms. The parallelism is configured based on the size of
the data, below a certain size only a single thread is used.


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

A utility class to map the ComplexDoubleArray to various output formats,
perhaps specified by an enum (since there are not that many possible
formats in 1D). But what of ND data? Thus far we have only considered how
to operate on a 1D linear range. Conversions would require a way to specify
the current format and the output format regarding striped (reals then
imaginary), interleaved, paired arrays, etc. for all dimensions. If ND is
represented in a linear array, this requires knowledge of the packing of
the data, e.g.: index = y * maxx + x.

There are typically such data structures in image libraries, often dealing
with 5D data, that do this that could form inspiration. Typically data is
arranged in a way that is optimal for the application and there are many
possible combinations. E.g. image data is often multiples of 2D XY data, so
may be a float[][] representing XYZ, Channel and Time. In the case of 2D
image a FFT is performed by 2 1D transforms so you extract strips of X
data, transform, then extract strips of Y from the partially transformed
XY, and repeat.

It may be that specifying a generic API for range operations on ND data may
not be possible. It would require further reading to narrow down possible
scope.


>
>
> > 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.
>

The Vector API is for more than 1. The javadoc examples note that the
number of lanes is typically 4 or 8. Any elements remaining after
processing multiples of the lane count will be processed in a sequential
loop using non-Vector API code, i.e. the method is coded twice. So a
length=1 array is never going to use the Vector API.

Alex

Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Sumanth Rajkumar <ra...@gmail.com>.
>
>
> 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

Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Alex Herbert <al...@gmail.com>.
On Sun, 12 Jun 2022 at 04:37, Sumanth Rajkumar <ra...@gmail.com>
wrote:

> On Sat, Jun 11, 2022, 18:02 Gilles Sadowski <gi...@gmail.com> wrote:
>
> > I have a hard time figuring out whether these bits of code are
> > intended to become the application developer API...
> > What data-structure(s) will be visible (from the application)?
> > What will be hidden ("implementation details")?
> >
>
> Developer API will look something like this
>
> //1) Static methods to initialize array from external forms
>
> ComplexDoubleArray a = ComplexDoubleArray.fromBuffer(Double Buffer);
> ComplexDoubleArray b = ComplexDoubleArray.fromArray(double[]);
>
> ComplexDoubleArray c = ComplexDoubleArray.parseFile(File);
>
>
> //2)  Methods on Array interface for complex operations. One for each
> functional interface type.. unary, binary, scalar operations
>
> @FunctionalInterface
> interface ComplexDoubleUnaryOperator {
>
> ComplexDoubleArray apply(ComplexDoubleArray input, ComplexDoubleArray
> result);
> }
>
> interface ComplexDoubleArray {
>
> int size();
>
> //MutableComplexDoubleArray extends ComplexDoubleArray with setter,
> mutation and static methods to allocate capacity
>
> MutableComplexDoubleArray asMutable();
>
> ComplexDoubleArray asImmutable();
>
> default boolean isImmutable() { return true;}
>
>
>   default ComplexDoubleArray apply(ComplexDoubleUnaryOperator op){
>
> return op.apply(this, result);
>
> }
>
> . . .
>
> }
>

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.
- a functional interface should be created that allows generic
specification of the operation to perform on each atomic unit (i.e. a
single complex number) as a lambda function.
- two arrays (a and b) should be able to be paired for an operation on all
pairs of (a, b), specified by a functional interface that can be written as
a lambda function. This method will write in-place to a or an optional
output array c. This could be preallocated, supplied dynamically using a
T[]::new lambda function or some other variant.

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).


>
> //Function interfaces behavior - If output array is immutable, operations
> return a copy, else result is applied in place on the passed in output and
> returned
>
> //3 Developer API usage of complex operations on complex arrays
>
> ComplexDoubleArray a = init();
> a = a.asImmutable();
>
> ComplexDoubleArray r1  = a.apply(ComplexFunctions::exp);
>
> ComplexDoubleArray r2  = a.apply(ComplexParrallelStreamFunctions::exp);
>

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


>
> ComplexDoubleArray r3  = a.apply(ComplexVectorFunctions::exp);
>
> ComplexDoubleArray r4  = a.apply(ComplexJTransformFunctions::forward_fft);
>
>
> // 4  ComplexFunctions can provide commons C99 reference implementations
> for supported complex operations as static methods that match the
> functional interfaces. ( Refactored methods from existing Complex class).
> Third parties can provide alternate implementations such as parallel stream
> or vector based implementations and used by developers as above.
>
>
> //5 the above functions also reused with existing Complex class?
>
> Complex implements ComplexDouble, ComplexDoubleArray {
>
> @Override
> public int size() {return 1;}
>
> @Override
> double [] toDoubleArray() { . . . }
>
> . . .
>
> //Refactored instance methods of Complex class
> public Complex exp() {
> return apply(ComplexFunctions::exp);
>
> }
> . . .
>
> }
>
> Complex c = Complex.ofCartesian(r,i);
>
> //Backward compatible C99 implementation
> Complex expc = c.exp();
>
> Complex thirdparty_exp = c.apply(SomeThirdPartyFunctions::exp);
>

My concern with this approach is that if we store the minimum of a final
real and imaginary part then any operation will have to:

- create a ComplexDoubleArray for the result
- Write to the ComplexDoubleArray
- Convert the result ComplexDoubleArray back to a Complex

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

IIUC the reason to have ComplexFunctions operate on arrays is to allow
Vector optimisations. In this case the data would have to be extracted into
a Vector<Double> species from a double[]. This would be more easily served
by allowing the ComplexBuffer to be written to a double[]:

DoubleComplexBuffer {
    // May be a reference to underlying data, or new allocation
    double[] getReal();
    // Copied to the provided array
    double[] getReal(double[] b);
}

By providing a ComplexBuffer implementation that uses separate real and
imaginary double[] arrays this is just a pass through method. The
ComplexVectorFunctions can be written to act on double[] directly as IIUC
the ISO c99 functions would have to be rewritten to use the Vector API
functions for multiply, add, etc. You cannot reuse existing functions that
take in and return double, e.g. Math.exp.

Is there any other reason to operate on arrays or buffers as abstractions?
Otherwise operating on a single complex number seems more appropriate for
other use cases which would ultimately loop over the data and compute on
single input complex numbers.

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. So it is only any new functionality we are to write based on more than
one complex number that would require the API to be designed around ranges
of input and output numbers.

So shall we revisit Gille's question:

Do we have use-cases of non-trivial processing of N-dimensional cubes of
complex numbers?

- Transforms such as FFT
- Multiply of two arrays of complex numbers (e.g. conjugate multiply of FFT
data is a correlation, multiply is a convolution)

Any others?


> //Require Java16+
> Complex vectorexp = c.apply(Complex VectorFunctions::exp)
>

Note: Vector functions on a single complex number is not applicable.


>
> Thanks,
> Sumanth
>

Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Sumanth Rajkumar <ra...@gmail.com>.
On Sat, Jun 11, 2022, 18:02 Gilles Sadowski <gi...@gmail.com> wrote:

> I have a hard time figuring out whether these bits of code are
> intended to become the application developer API...
> What data-structure(s) will be visible (from the application)?
> What will be hidden ("implementation details")?
>

Developer API will look something like this

//1) Static methods to initialize array from external forms

ComplexDoubleArray a = ComplexDoubleArray.fromBuffer(Double Buffer);
ComplexDoubleArray b = ComplexDoubleArray.fromArray(double[]);

ComplexDoubleArray c = ComplexDoubleArray.parseFile(File);


//2)  Methods on Array interface for complex operations. One for each
functional interface type.. unary, binary, scalar operations

@FunctionalInterface
interface ComplexDoubleUnaryOperator {

ComplexDoubleArray apply(ComplexDoubleArray input, ComplexDoubleArray
result);
}

interface ComplexDoubleArray {

int size();

//MutableComplexDoubleArray extends ComplexDoubleArray with setter,
mutation and static methods to allocate capacity

MutableComplexDoubleArray asMutable();

ComplexDoubleArray asImmutable();

default boolean isImmutable() { return true;}


  default ComplexDoubleArray apply(ComplexDoubleUnaryOperator op){

return op.apply(this, result);

}

. . .

}

//Function interfaces behavior - If output array is immutable, operations
return a copy, else result is applied in place on the passed in output and
returned

//3 Developer API usage of complex operations on complex arrays

ComplexDoubleArray a = init();
a = a.asImmutable();

ComplexDoubleArray r1  = a.apply(ComplexFunctions::exp);

ComplexDoubleArray r2  = a.apply(ComplexParrallelStreamFunctions::exp);

ComplexDoubleArray r3  = a.apply(ComplexVectorFunctions::exp);

ComplexDoubleArray r4  = a.apply(ComplexJTransformFunctions::forward_fft);


// 4  ComplexFunctions can provide commons C99 reference implementations
for supported complex operations as static methods that match the
functional interfaces. ( Refactored methods from existing Complex class).
Third parties can provide alternate implementations such as parallel stream
or vector based implementations and used by developers as above.


//5 the above functions also reused with existing Complex class?

Complex implements ComplexDouble, ComplexDoubleArray {

@Override
public int size() {return 1;}

@Override
double [] toDoubleArray() { . . . }

. . .

//Refactored instance methods of Complex class
public Complex exp() {
return apply(ComplexFunctions::exp);

}
. . .

}

Complex c = Complex.ofCartesian(r,i);

//Backward compatible C99 implementation
Complex expc = c.exp();

Complex thirdparty_exp = c.apply(SomeThirdPartyFunctions::exp);

//Require Java16+
Complex vectorexp = c.apply(Complex VectorFunctions::exp)

Thanks,
Sumanth

Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Gilles Sadowski <gi...@gmail.com>.
Hello.

> [...]
>
> interface ComplexDoubleArray {
>     Stream<ComplexDoubleArray> stream(int start, int length);
> }
>
> ComplexDoubleArray a;
> // Will use the Java 8 ForkJoinPool.commonPool() for parallel execution
> a.stream(start, length).parallel().forEach(x -> ComplexFunctions.conj(x,
> x));
>
> class ComplexFunctions {
>     static void conj(ComplexDoubleArray in, ComplexDoubleArray out);
> }
>
> [...]

I have a hard time figuring out whether these bits of code are
intended to become the application developer API...
What data-structure(s) will be visible (from the application)?
What will be hidden ("implementation details")?
Do we have use-cases of non-trivial processing of N-dimensional
cubes of complex numbers?  [I imagine that the same API should
be able to also process cubes of real numbers (without storing the
"0" imaginary parts).]

Gilles

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org


Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Alex Herbert <al...@gmail.com>.
On Sat, 11 Jun 2022 at 07:09, Sumanth Rajkumar <ra...@gmail.com>
wrote:

> Hi Alex and Gilles,
>
> For the complex functional interfaces, we have so far been working on
> different options but all operate on single complex numbers one at a
> time...
>
> Instead can we design around Functional Interfaces that work on List/Arrays
> instead?
>

An interesting approach. IIUC this approach is to allow parallel
optimisation. So the question is what computations would be performed in
parallel on a large set of numbers? I am wondering if all the ISO c99
functions would ever be used in this way. And if they are, how well they
would optimise given the high number of branch statements in many of the
functions to handle edge cases. The optimisation is performing:

for i in range:
    x(i) = f(x(i))

So you have to provide a function to operate on a number to obtain another
number. How is this coded in the Java Vector API for simd support?

Would a single layer of abstraction mapping a double[] (in the list) to
ComplexNumber (for the function input) and back (from the function output)
impact performance?

<-- SNIP -->

>
> Interface ComplexDoubleArray{
>

> DoubleStream realAndImgPairs (int index, int length)
>
> <-- SNIP -->

A stream would have to have the pair of [real, imag] as the unit that the
stream operates on. It cannot be a (possibly interleaved) stream of each
part otherwise no function can operate on the whole complex number. Any API
would require:

Stream<T> stream(int index, int length)

with T the type of the complex number.

...
>
> }
>
> @FunctionalInterface
> Interface ComplexDoubleUnaryOperator{
>
> void apply(ComplexDoubleArray input, ComplexDoubleArray output, int
> inputoffset, int outputoffset, int length);
>
> }
>
> This has following advantages
>
> 1) iterating implementations over lists will now be in the complex function
> instead of ComplexList data structures allowing for different
> implementations. For example we could have multi threaded java.util.stream
> based operation on lists and single threaded cursor while loop based
> implementation as suggested before by Alex.
>

Thinking about streams...

For multi-threaded stream based operation this would:

- Create the stream with a start and end point
- Create a custom Spliterator holding the range limits
- Allow division of the Spliterator which handles dividing the range until
no more divisions are required (max threads reached)

Thus the final unit to operate on is an unknown range (this is known to the
Spliterator) of numbers. The unit for the functional interface is then
ComplexDoubleArray:

interface ComplexDoubleArray {
    Stream<ComplexDoubleArray> stream(int start, int length);
}

ComplexDoubleArray a;
// Will use the Java 8 ForkJoinPool.commonPool() for parallel execution
a.stream(start, length).parallel().forEach(x -> ComplexFunctions.conj(x,
x));

class ComplexFunctions {
    static void conj(ComplexDoubleArray in, ComplexDoubleArray out);
}

So for streams the operations are required to operate on a range that is
unknown. Internally the method must then have a way to access each entry of
the ComplexDoubleArray, which is effectively a List<Complex>.subList(...).

Did you envision a different implementation?


> 2) it will be required to take advantage of simd parallelism as discussed
> before. For example conjugate operation on Complex list (double[] of real
> and imaginary pairs) would be vector multiplication with array [
> 1,-1,1-1,..]
>
> If functional interface enforce operation on one complex number at a time,
> we might not be able take advantage of simd optimizations?
>

IIUC the Java vector API is for multiplications, additions, and other
arithmetic on primitives. The API is not finalised but it appears to have
support for pow and sqrt elementary functions. So this would not allow full
support for the ISO c99 elementary functions in Complex. Also the API
requires a primitive array to be converted to a Vector species and operated
on.

So perhaps to support this only requires that any list of complex numbers
can return the real and imaginary parts:

interface ComplexList {
    void getReal(int start, int length, double[] dest);
    void getImaginary(int start, int length, double[] dest);
    // set methods
}

Then the functions can be written to extract the parts from the list,
operate on them using the Java vector API and return them to the list.

These functions would have an advantage on simple primitive operations. But
any functions that require conditional logic may not be optimised (this is
not clear from the examples I found). So for instance the multiplication of
two complex numbers performed using:

(a + i b)(c + i d) = (ac - bd) + i (ad + bc)

final double ac = a * c;
final double bd = b * d;
final double ad = a * d;
final double bc = b * c;
double x = ac - bd;
double y = ad + bc;

The result x and y are then checked for NaN and suitable infinities
recovered which requires a lot of logic. This logic may have to be moved
out of the vector optimised loop and performed after on the result arrays.


>
> 3) operation on single complex number is now just operation on list/array
> of size 1. We can make existing Complex class implement ComplexDoubleArray
> interface
>

Currently the abstraction to an array does not seem like an approach that
fits all requirements. It may be that the api should operate on some type
of ComplexNumber unit and have a separate support for operating on vector
arrays within the limitations of the Java Vector API.

Alex

Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Sumanth Rajkumar <ra...@gmail.com>.
Hi Alex and Gilles,

For the complex functional interfaces, we have so far been working on
different options but all operate on single complex numbers one at a time...

Instead can we design around Functional Interfaces that work on List/Arrays
instead?

Interface ComplexDoubleArray{

/* Methods for set/get by index as well as set/get  array ranges on
primitive double arrays*/

void set(int index, int sourceIndex, int len, double[] realAndImgPairs)

void get(int index, int destIndex, int len, double[] realAndImgPairs)

void set(int index, ComplexDouble c)

ComplexDouble get(int index)

Iterator<ComplexDouble> iterator( int index, int length)

DoubleStream realAndImgPairs (int index, int length)

...

}

@FunctionalInterface
Interface ComplexDoubleUnaryOperator{

void apply(ComplexDoubleArray input, ComplexDoubleArray output, int
inputoffset, int outputoffset, int length);

}

This has following advantages

1) iterating implementations over lists will now be in the complex function
instead of ComplexList data structures allowing for different
implementations. For example we could have multi threaded java.util.stream
based operation on lists and single threaded cursor while loop based
implementation as suggested before by Alex.

2) it will be required to take advantage of simd parallelism as discussed
before. For example conjugate operation on Complex list (double[] of real
and imaginary pairs) would be vector multiplication with array [
1,-1,1-1,..]

If functional interface enforce operation on one complex number at a time,
we might not be able take advantage of simd optimizations?

3) operation on single complex number is now just operation on list/array
of size 1. We can make existing Complex class implement ComplexDoubleArray
interface



Thanks
Sumanth



On Sat, Jun 11, 2022, 11:00 Sumanth Rajkumar <ra...@gmail.com>
wrote:

>
>
> On Fri, Jun 10, 2022, 19:30 Gilles Sadowski <gi...@gmail.com> wrote:
>
>> The current implementation of "Complex" encapsulates two "double" fields
>> (not
>> a "double[]").
>> Should we make two, at first separate, discussions: One for the
>> implementation
>> of the "complex number" concept, and another for (n-dimensional) lists of
>> them?
>>
>
> Yes. We could also change existing Complex type from two double fields to
> double[] and even make it implement new ComplexNumber interface? This
> should maintain runtime compatibility?
>
> For the interface, I suggest ComplexDouble (and ComplexFloat) intead of
> ComplexNumber?
>
>
> > A) ComplexCartesianImpl data structure will change to double[]
>> > realAndImgPair
>>
>> What gain do you expect from involving an array here?
>>
>
>  We can make Complex class implement new ComplexList and ComplexNumber
> interface..
>
> I was thinking this would allow efficient reuse of functions processing
> lists also to be used on single numbers (Complex) which is also a list of
> size 1
>
> > B) ComplexList can use single double[] for realAndImg parts (similar to
>> all
>> > external implementations such as jtransform)
>>
>> Yes (because of the gain in RAM usage).
>> But AFAICT, the goal would be to make the "double[]" an "implementation
>> detail" of "ComplexList" and have all operators handle "ComplexList" (or
>> any n-dimensional "cube") as their input/output (?).
>>
>>
> Yes. Also looking at jtransform naming, I suggest ComplexDoubleArray (and
> ComplexFloatArray) instead of ComplexList?
>
> Jtransform has support for large arrays (size greater than integer.max
> with array index of type long.
>
> Do we need to support that now or add that support later if needed?
>
> Thanks
> Sumanth
>
>

Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Sumanth Rajkumar <ra...@gmail.com>.
On Fri, Jun 10, 2022, 19:30 Gilles Sadowski <gi...@gmail.com> wrote:

> The current implementation of "Complex" encapsulates two "double" fields
> (not
> a "double[]").
> Should we make two, at first separate, discussions: One for the
> implementation
> of the "complex number" concept, and another for (n-dimensional) lists of
> them?
>

Yes. We could also change existing Complex type from two double fields to
double[] and even make it implement new ComplexNumber interface? This
should maintain runtime compatibility?

For the interface, I suggest ComplexDouble (and ComplexFloat) intead of
ComplexNumber?


> A) ComplexCartesianImpl data structure will change to double[]
> > realAndImgPair
>
> What gain do you expect from involving an array here?
>

 We can make Complex class implement new ComplexList and ComplexNumber
interface..

I was thinking this would allow efficient reuse of functions processing
lists also to be used on single numbers (Complex) which is also a list of
size 1

> B) ComplexList can use single double[] for realAndImg parts (similar to
> all
> > external implementations such as jtransform)
>
> Yes (because of the gain in RAM usage).
> But AFAICT, the goal would be to make the "double[]" an "implementation
> detail" of "ComplexList" and have all operators handle "ComplexList" (or
> any n-dimensional "cube") as their input/output (?).
>
>
Yes. Also looking at jtransform naming, I suggest ComplexDoubleArray (and
ComplexFloatArray) instead of ComplexList?

Jtransform has support for large arrays (size greater than integer.max with
array index of type long.

Do we need to support that now or add that support later if needed?

Thanks
Sumanth

Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Gilles Sadowski <gi...@gmail.com>.
Hello.

Le ven. 10 juin 2022 à 15:10, Sumanth Rajkumar
<ra...@gmail.com> a écrit :
>
> For 1) I ran mvn verify and found the following errors
>
> org.apache.commons.numbers.complex.Complex.getImaginary():METHOD_NOW_ABSTRACT,
> org.apache.commons.numbers.complex.Complex.getReal():METHOD_NOW_ABSTRACT,
> org.apache.commons.numbers.complex.Complex:CLASS_NOW_ABSTRACT,
> org.apache.commons.numbers.complex.Complex:CLASS_TYPE_CHANGED,
> org.apache.commons.numbers.complex.ComplexCartesianImpl:METHOD_ABSTRACT_ADDED_IN_IMPLEMENTED_INTERFACE,
> org.apache.commons.numbers.complex.ComplexList:METHOD_ABSTRACT_ADDED_IN_IMPLEMENTED_INTERFACE,
> org.apache.commons.numbers.complex.ImmutableComplexList:METHOD_ABSTRACT_ADDED_IN_IMPLEMENTED_INTERFACE
>
> These are expected changes... Is there a compatibility issue here?
>
> 2) Regarding usage of arrays in functional interfaces
> @FunctionalInterface
> public interface ComplexFunction3 {
>   void apply(Complex input, int offset, double[] result);
> }
>
> The underlying implementations of Complex and ComplexList all use arrays
> and there would be no additional array instantiation /RAM allocation just
> to apply the functional interface functions

The current implementation of "Complex" encapsulates two "double" fields (not
a "double[]").
Should we make two, at first separate, discussions: One for the implementation
of the "complex number" concept, and another for (n-dimensional) lists of them?

> A) ComplexCartesianImpl data structure will change to double[]
> realAndImgPair

What gain do you expect from involving an array here?

> B) ComplexList can use single double[] for realAndImg parts (similar to all
> external implementations such as jtransform)

Yes (because of the gain in RAM usage).
But AFAICT, the goal would be to make the "double[]" an "implementation
detail" of "ComplexList" and have all operators handle "ComplexList" (or
any n-dimensional "cube") as their input/output (?).

Regards,
Gilles

>
> Thanks
> Sumanth
>
> On Fri, 10 Jun 2022 at 08:58, Gilles Sadowski <gi...@gmail.com> wrote:
>
> > Hello.
> >
> > Le ven. 10 juin 2022 à 14:43, Sumanth Rajkumar
> > <ra...@gmail.com> a écrit :
> > >
> > > Thanks for the quick response
> > >
> > > 1) I will run the mvn checks as suggested and get back to you
> > >
> > > 2) Yes, I realized the inefficiency and would not work.. I was following
> > up
> > > on another alternative but the email got sent prematurely
> > >
> > > Please ignore my previous email and consider this approach or some
> > > variation of it?
> > >
> > > @FunctionalInterface
> > > public interface ComplexFunction {
> > >   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();
> > >  }
> >
> > My first feeling would be to steer away from (ab)using array as a pair.
> > We may have to use arrays for interfacing with external tools (or perhaps
> > internally too, e.g. to minimize RAM usage when processing a large list
> > of complex numbers) but from a OO point of view, we should come up
> > with an encapsulation that ensures robustness (e.g. featuring
> > immutability).
> > Also the type(s) should be easily usable in functional programming style.
> >
> > Gilles
> >
> > >
> > > [...]

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org


Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Alex Herbert <al...@gmail.com>.
On Sat, 11 Jun 2022 at 06:08, Sumanth Rajkumar <ra...@gmail.com>
wrote:

>
> The type change would have been source compatible where existing
> applications would have to recompile  (without any code changes) to run
> with the new version?
>

Yes.


>
> So yes, we cannot change Complex type to interface to maintain binary
> runtime compatibility
>
> I assume, binary compatibility would be maintained if we add additional
> interfaces with new methods to Complex class?
>

Yes. To use new methods another application would have to recompile source.
Those applications that do now recompile source are ignorant of any new
methods.

Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Sumanth Rajkumar <ra...@gmail.com>.
Thanks for the detailed explanation with example. Now I understand the
difference between binary vs source backward compatibility.

The type change would have been source compatible where existing
applications would have to recompile  (without any code changes) to run
with the new version?

So yes, we cannot change Complex type to interface to maintain binary
runtime compatibility

I assume, binary compatibility would be maintained if we add additional
interfaces with new methods to Complex class?

For example, we should be ok if we add  a new ComplexDouble interface with
possibly new methods to Complex class?



On Fri, Jun 10, 2022, 20:45 Alex Herbert <al...@gmail.com> wrote:

>
> > These are expected changes... Is there a compatibility issue here?
> >
>
> Yes. These are reported as errors.
>
> This is the main issue:
>
> https://revapi.org/revapi-java/differences.html#java.class.kindChanged
>
> Changing a class to an interface. In this case methods on the object are
> invoked using a different instruction.
>

So if you change class A to an interface then Test will have a runtime
> linkage error as it cannot find the method
>

Thus we have to keep Complex as a class.
>

Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Alex Herbert <al...@gmail.com>.
On Fri, 10 Jun 2022 at 14:10, Sumanth Rajkumar <ra...@gmail.com>
wrote:

> For 1) I ran mvn verify and found the following errors
>
>
> org.apache.commons.numbers.complex.Complex.getImaginary():METHOD_NOW_ABSTRACT,
> org.apache.commons.numbers.complex.Complex.getReal():METHOD_NOW_ABSTRACT,
> org.apache.commons.numbers.complex.Complex:CLASS_NOW_ABSTRACT,
> org.apache.commons.numbers.complex.Complex:CLASS_TYPE_CHANGED,
>
> org.apache.commons.numbers.complex.ComplexCartesianImpl:METHOD_ABSTRACT_ADDED_IN_IMPLEMENTED_INTERFACE,
>
> org.apache.commons.numbers.complex.ComplexList:METHOD_ABSTRACT_ADDED_IN_IMPLEMENTED_INTERFACE,
>
> org.apache.commons.numbers.complex.ImmutableComplexList:METHOD_ABSTRACT_ADDED_IN_IMPLEMENTED_INTERFACE
>
> These are expected changes... Is there a compatibility issue here?
>

Yes. These are reported as errors.

One point here is that Complex is a final class. So some of the errors
regarding inheritance are non-issues. However binary compatibility requires
that a class compiled against the old version can run against the new one.
The main issue is changing a class to an interface (type change).

The japicmp plugin does not have a lot of documentation. You can try revapi
instead. Put this in the plugins section of the pom.xml:

      <plugin>
        <groupId>org.revapi</groupId>
        <artifactId>revapi-maven-plugin</artifactId>
        <version>0.14.6</version>
        <dependencies>
          <dependency>
            <groupId>org.revapi</groupId>
            <artifactId>revapi-java</artifactId>
            <version>0.26.1</version>
          </dependency>
        </dependencies>
      </plugin>

Then run:

mvn revapi:check  (if you have the jar package)
mvn package revapi:check -DskipTests -Djavadoc.skip      (if you don't)

This will output some similar compatibility errors and link to online
documentation that explains why it is a problem.

This is the main issue:

https://revapi.org/revapi-java/differences.html#java.class.kindChanged

Changing a class to an interface. In this case methods on the object are
invoked using a different instruction.

Save this as Test.java:

public class Test {
  static class A {
    public int getAnswer() { return 42; }
  }

  interface B {
    int getAnswer();
  }

  static class Bimp implements B {
    public int getAnswer() { return 13; }
  }

  public static void main(String[] args) {
    A a = new A();
    System.out.println(a.getAnswer());
    B b = new Bimp();
    System.out.println(b.getAnswer());
  }
}

> javac Test.java
> java Test
42
13
> javap -c Test
...

12: invokevirtual #16                 // Method Test$A.getAnswer:()I
...

30: invokeinterface #29,  1           // InterfaceMethod
Test$B.getAnswer:()I
...

Here we see the interface method uses 'invokeinterface' and the class
method uses 'invokevirtual'.

So if you change class A to an interface then Test will have a runtime
linkage error as it cannot find the method (this is all assuming that A, B
and Bimp are in their own class files which I didn't do here for
simplicity). Basically if a class thinks the object with a method is a
class and it is changed on the classpath at runtime to an interface this
will not work.


Thus we have to keep Complex as a class. However it does not mean the
methods cannot be moved to e.g.

public final class ComplexFunctions {
    interface ComplexResult<R> {
        R accept(double real, double imag);
    }

    public static <R> R conj(double real, double imaginary,
ComplexResult<R> result) {
        return result.accept(real, -imaginary);
    }
}

And Complex changed to e.g:

    ComplexFunctions conj() {
        return ComplexMath.conj(real(), imag(), Complex::ofCartesian);
    }

Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Sumanth Rajkumar <ra...@gmail.com>.
For 1) I ran mvn verify and found the following errors

org.apache.commons.numbers.complex.Complex.getImaginary():METHOD_NOW_ABSTRACT,
org.apache.commons.numbers.complex.Complex.getReal():METHOD_NOW_ABSTRACT,
org.apache.commons.numbers.complex.Complex:CLASS_NOW_ABSTRACT,
org.apache.commons.numbers.complex.Complex:CLASS_TYPE_CHANGED,
org.apache.commons.numbers.complex.ComplexCartesianImpl:METHOD_ABSTRACT_ADDED_IN_IMPLEMENTED_INTERFACE,
org.apache.commons.numbers.complex.ComplexList:METHOD_ABSTRACT_ADDED_IN_IMPLEMENTED_INTERFACE,
org.apache.commons.numbers.complex.ImmutableComplexList:METHOD_ABSTRACT_ADDED_IN_IMPLEMENTED_INTERFACE

These are expected changes... Is there a compatibility issue here?

2) Regarding usage of arrays in functional interfaces
@FunctionalInterface
public interface ComplexFunction3 {
  void apply(Complex input, int offset, double[] result);
}

The underlying implementations of Complex and ComplexList all use arrays
and there would be no additional array instantiation /RAM allocation just
to apply the functional interface functions

A) ComplexCartesianImpl data structure will change to double[]
realAndImgPair
B) ComplexList can use single double[] for realAndImg parts (similar to all
external implementations such as jtransform)

Thanks
Sumanth

On Fri, 10 Jun 2022 at 08:58, Gilles Sadowski <gi...@gmail.com> wrote:

> Hello.
>
> Le ven. 10 juin 2022 à 14:43, Sumanth Rajkumar
> <ra...@gmail.com> a écrit :
> >
> > Thanks for the quick response
> >
> > 1) I will run the mvn checks as suggested and get back to you
> >
> > 2) Yes, I realized the inefficiency and would not work.. I was following
> up
> > on another alternative but the email got sent prematurely
> >
> > Please ignore my previous email and consider this approach or some
> > variation of it?
> >
> > @FunctionalInterface
> > public interface ComplexFunction {
> >   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();
> >  }
>
> My first feeling would be to steer away from (ab)using array as a pair.
> We may have to use arrays for interfacing with external tools (or perhaps
> internally too, e.g. to minimize RAM usage when processing a large list
> of complex numbers) but from a OO point of view, we should come up
> with an encapsulation that ensures robustness (e.g. featuring
> immutability).
> Also the type(s) should be easily usable in functional programming style.
>
> Gilles
>
> >
> > [...]
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
> For additional commands, e-mail: dev-help@commons.apache.org
>
>

Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Gilles Sadowski <gi...@gmail.com>.
Hello.

Le ven. 10 juin 2022 à 14:43, Sumanth Rajkumar
<ra...@gmail.com> a écrit :
>
> Thanks for the quick response
>
> 1) I will run the mvn checks as suggested and get back to you
>
> 2) Yes, I realized the inefficiency and would not work.. I was following up
> on another alternative but the email got sent prematurely
>
> Please ignore my previous email and consider this approach or some
> variation of it?
>
> @FunctionalInterface
> public interface ComplexFunction {
>   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();
>  }

My first feeling would be to steer away from (ab)using array as a pair.
We may have to use arrays for interfacing with external tools (or perhaps
internally too, e.g. to minimize RAM usage when processing a large list
of complex numbers) but from a OO point of view, we should come up
with an encapsulation that ensures robustness (e.g. featuring
immutability).
Also the type(s) should be easily usable in functional programming style.

Gilles

>
> [...]

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org


Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Sumanth Rajkumar <ra...@gmail.com>.
Thanks for the quick response

1) I will run the mvn checks as suggested and get back to you

2) Yes, I realized the inefficiency and would not work.. I was following up
on another alternative but the email got sent prematurely

Please ignore my previous email and consider this approach or some
variation of it?

@FunctionalInterface
public interface ComplexFunction {
  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 data structure will change to single double[] realAndImgPair
ComplexList {
..
 private double[] realAndImaginaryPairs;

// applies changes in place

void forEach(ComplexFunction<Void> fun) {

    for(int i=0; i < realAndImaginaryPairs.length; i++){
        fun.apply(cursor, i,realAndImaginaryPairs);
    }
}

Also, all the external list implementations I have reviewed, are using
single double[] with real/imag parts interleaved or stored in the first and
second half of the array.

Thanks
Sumanth

On Fri, 10 Jun 2022 at 08:31, Alex Herbert <al...@gmail.com> wrote:

> Hi,
>
> Thanks for the design update. I will review and get back to you with more
> detailed feedback. Here are some quick thoughts:
>
> On Fri, 10 Jun 2022 at 12:55, Sumanth Rajkumar <rajkumar.sumanth@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
> >
>
> 'mvn test' will not run binary compatibility checks. You should try
> building with the default goal: 'mvn'. This will run a binary
> compatibility check japicmp:cmp in the 'verify' phase as it requires the
> packaged jar file.
>
>
> >
> > 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() ;
> > }
> >
> >
> This has concerns for efficiency. Look at some of the more involved
> functions and you will see that there will be a lot of repeat computation
> if you pass in the same complex number twice.
>
> 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();
> >         }
> >
> >         };
> >    };
> >
>
> Quite verbose. However, can you even use a lambda function here?
>
>
> >
> > 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>?
> >
>
> I think a function for a complex number requires a real and imaginary input
> and somewhere to put the real and imaginary answer. How to express this in
> an interface that allows chaining is the tricky part.
>
> Alex
>

Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Alex Herbert <al...@gmail.com>.
Hi,

Thanks for the design update. I will review and get back to you with more
detailed feedback. Here are some quick thoughts:

On Fri, 10 Jun 2022 at 12:55, Sumanth Rajkumar <ra...@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
>

'mvn test' will not run binary compatibility checks. You should try
building with the default goal: 'mvn'. This will run a binary
compatibility check japicmp:cmp in the 'verify' phase as it requires the
packaged jar file.


>
> 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() ;
> }
>
>
This has concerns for efficiency. Look at some of the more involved
functions and you will see that there will be a lot of repeat computation
if you pass in the same complex number twice.

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();
>         }
>
>         };
>    };
>

Quite verbose. However, can you even use a lambda function here?


>
> 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>?
>

I think a function for a complex number requires a real and imaginary input
and somewhere to put the real and imaginary answer. How to express this in
an interface that allows chaining is the tricky part.

Alex

Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Sumanth Rajkumar <ra...@gmail.com>.
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 <al...@gmail.com> wrote:

> On Thu, 26 May 2022 at 15:04, Gilles Sadowski <gi...@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).
>

Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Alex Herbert <al...@gmail.com>.
On Thu, 26 May 2022 at 15:04, Gilles Sadowski <gi...@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).

Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Gilles Sadowski <gi...@gmail.com>.
Hello.

Le jeu. 26 mai 2022 à 07:04, Sumanth Rajkumar
<ra...@gmail.com> a écrit :
>
> Hi,
>
> My proposal was accepted into GSoC 2022 to work on the Numbers-186 [1] Jira
> of the Commons Numbers project.
>
> I first want to ask if I can be assigned to this Jira

Done.

> since my GSoC
> proposal was accepted.
>
> 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.

For each of the mentioned functionalities, types and operations,
please provide a concrete example of what you propose, indicating
whether it is a new feature or a change and whethe it is backwards
comptable (BC) or not.  [These details can be further discussed on
the JIRA page.]

Whenever possible, please ensure that the proposed changes do
not break the build.  [You can test this by opening a PR, which should
trigger an automated build.]

>
> Working on the API, am I on the right track to start with refactoring all
> the existing methods in the Complex class as static functions for use as
> lambdas?
>
> I already refactored some methods which can be viewed here [2]

Renaming is typically something to be first discussed here and/or
on JIRA.
Unless other non BC changes are foreseen for the next release,
such "cosmetic" changes must be avoided.

Thanks,
Gilles

>
> Thanks,
>
> Sumanth
>
> [1] https://issues.apache.org/jira/browse/NUMBERS-186
>
> [2]
> https://github.com/sumanth-rajkumar/commons-numbers/tree/sumanth-gsoc-22/commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex
>
> On Mon, Mar 28, 2022, 7:01 PM Gilles Sadowski <gi...@gmail.com> wrote:
>
> > Hello.
> >
> > Le lun. 28 mars 2022 à 00:32, Sumanth Rajkumar
> > <ra...@gmail.com> a écrit :
> > >
> > > Thanks Alex and Gilles for your feedback
> > >
> > > So currently Commons Math transform depends on Common complex numbers,
> > and
> > > the API involves usage of Complex Object Arrays instead of primitive
> > array
> > > data structures
> > >
> > > I also briefly looked into other library implementations besides
> > Jtransform
> > > and EJML that are not pure java but have java bindings such as JBLAS[1]
> > and
> > > NAG[2]
> > > All of the implementation use single array data structures to represent
> > > Complex Lists and higher dimensional matrices
> > >
> > > Since these involve parallel data pipelines I looked into libraries that
> > > use SIMD [3] operations that use GP GPU (jcuda [4][5] /aparapi [6]) and
> > CPU
> > > (Java 17 Vector API [7])
> >
> > Thanks for the investigation!
> >
> > >
> > > Given all the alternative implementations, I agree it does not make sense
> > > to re implement transforms here.
> >
> > Some transforms are (already) implemented here.
> > Of course, it makes sense to wonder whether to keep maintaining those
> > codes, or rely on external dependencies.  The decision would depend on
> > performance comparisons and whether users are able (or allowed) to
> > interface with native libraries.  [I know of one project where "pure Java"
> > was a requirement...]
> >
> > >
> > > So instead would it be useful to provide users with
> > > 1) a complex numbers Linear Algebra and transforms API to compile against
> > > and run with any of existing providers (apache commons, jtransform, EJML,
> > > jblas)
> > > AND
> > > 2) a service provider interface to allow adapter implementations to
> > > integrate existing and future providers such as jcudas/aparapi/vector
> > APIs
> > >
> > > Do the commons library modularization dependency requirements apply to
> > > compile time dependencies only or runtime also?
> >
> > Which "dependency requirements" are you referring to?
> >
> > > To minimize bloat, the runtime dependencies could be made optional and
> > need
> > > not be transitively included by default
> >
> > Flexibility would be ideal, indeed.
> >
> > >
> > > Providing a Complex linear algebra and transforms API that can run with
> > > different runtime providers would allow users to take advantage of
> > hardware
> > > capabilities and gracefully fallback to reference implementations
> > > It could allow users to take advantage of Java 17 Vector APIs when
> > > available without refactoring their existing libraries
> >
> > That looks great.
> >
> > >
> > > Also do the Apache projects/ license allow for integration with non
> > Apache
> > > software (jtransform /jblas do not use Apache license, jcuda uses MIT
> > > license) ?
> >
> > Licence issues are detailed here:
> >   https://www.apache.org/legal/resolved.html
> >
> > Best regards,
> > Gilles
> >
> > >
> > > Thanks
> > > Sumanth
> > >
> > > [1] http://jblas.org/
> > >
> > > [2]
> > https://www.nag.com/numeric/nl/nagdoc_latest/clhtml/c06/c06conts.html
> > >
> > > [3]
> > https://blogs.oracle.com/javamagazine/post/programming-the-gpu-in-java
> > >
> > > [4] http://jcuda.org/jcuda/jcufft/JCufft.html
> > >
> > > [5]
> > >
> > https://github.com/jcuda/jcuda-samples/tree/master/JCudaSamples/src/main/java/jcuda/jcufft/samples
> > >
> > > [6] http://aparapi.github.io/
> > >
> > > [7] https://openjdk.java.net/jeps/414
> > >
> > >
> > > On Tue, 22 Mar 2022 at 10:07, Gilles Sadowski <gi...@gmail.com>
> > wrote:
> > >
> > > > Hello.
> > > >
> > > > > [...]
> > > > > >
> > > > > > 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)?
> > > > > >
> > > > >
> > > > > Numbers should be seen as a toolbox to be used by other Java
> > > > applications.
> > > > > The best location for routines is something to discuss on the mailing
> > > > list.
> > > > > In the example of DCT, I am not aware if imaging currently has an
> > encoder
> > > > > implementation for this. There is a decoder:
> > > > > org/apache/commons/imaging/formats/jpeg/decoder/Dct.jav
> > > >
> > > > Also:
> > > >
> > > >
> > https://gitbox.apache.org/repos/asf?p=commons-math.git;a=blob;f=commons-math-transform/src/main/java/org/apache/commons/math4/transform/FastCosineTransform.java
> > > >
> > > > It would be a maintenance boon if "Commons" could come up with
> > > > a consensus about which components must be dependency-free and
> > > > which could depend on other (lower-level) "Commons" components.
> > > >
> > > > [Imaging] is clearly higher-level than [Math] and that such non-obvious
> > > > algorithms should be maintained in a single place.  Through the process
> > > > of modularizing [Math], we have "commons-math-transform" module,
> > > > with zero dependency, so it would bring zero bloat to [Imaging] users
> > if
> > > > we consolidate usage.
> > > >
> > > > Of course, that would imply testing and benchmarking all current
> > > > implementations, and retain the best (taking various axes into account:
> > > > performance, robustness, flexibility).
> > > >
> > > > Regards,
> > > > Gilles
> > > >
> > > > > [...]
> >
> > ---------------------------------------------------------------------
> > To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
> > For additional commands, e-mail: dev-help@commons.apache.org
> >
> >

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org


Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Sumanth Rajkumar <ra...@gmail.com>.
Hi,

My proposal was accepted into GSoC 2022 to work on the Numbers-186 [1] Jira
of the Commons Numbers project.

I first want to ask if I can be assigned to this Jira since my GSoC
proposal was accepted.

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.


Working on the API, am I on the right track to start with refactoring all
the existing methods in the Complex class as static functions for use as
lambdas?

I already refactored some methods which can be viewed here [2]

Thanks,

Sumanth

[1] https://issues.apache.org/jira/browse/NUMBERS-186

[2]
https://github.com/sumanth-rajkumar/commons-numbers/tree/sumanth-gsoc-22/commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex

On Mon, Mar 28, 2022, 7:01 PM Gilles Sadowski <gi...@gmail.com> wrote:

> Hello.
>
> Le lun. 28 mars 2022 à 00:32, Sumanth Rajkumar
> <ra...@gmail.com> a écrit :
> >
> > Thanks Alex and Gilles for your feedback
> >
> > So currently Commons Math transform depends on Common complex numbers,
> and
> > the API involves usage of Complex Object Arrays instead of primitive
> array
> > data structures
> >
> > I also briefly looked into other library implementations besides
> Jtransform
> > and EJML that are not pure java but have java bindings such as JBLAS[1]
> and
> > NAG[2]
> > All of the implementation use single array data structures to represent
> > Complex Lists and higher dimensional matrices
> >
> > Since these involve parallel data pipelines I looked into libraries that
> > use SIMD [3] operations that use GP GPU (jcuda [4][5] /aparapi [6]) and
> CPU
> > (Java 17 Vector API [7])
>
> Thanks for the investigation!
>
> >
> > Given all the alternative implementations, I agree it does not make sense
> > to re implement transforms here.
>
> Some transforms are (already) implemented here.
> Of course, it makes sense to wonder whether to keep maintaining those
> codes, or rely on external dependencies.  The decision would depend on
> performance comparisons and whether users are able (or allowed) to
> interface with native libraries.  [I know of one project where "pure Java"
> was a requirement...]
>
> >
> > So instead would it be useful to provide users with
> > 1) a complex numbers Linear Algebra and transforms API to compile against
> > and run with any of existing providers (apache commons, jtransform, EJML,
> > jblas)
> > AND
> > 2) a service provider interface to allow adapter implementations to
> > integrate existing and future providers such as jcudas/aparapi/vector
> APIs
> >
> > Do the commons library modularization dependency requirements apply to
> > compile time dependencies only or runtime also?
>
> Which "dependency requirements" are you referring to?
>
> > To minimize bloat, the runtime dependencies could be made optional and
> need
> > not be transitively included by default
>
> Flexibility would be ideal, indeed.
>
> >
> > Providing a Complex linear algebra and transforms API that can run with
> > different runtime providers would allow users to take advantage of
> hardware
> > capabilities and gracefully fallback to reference implementations
> > It could allow users to take advantage of Java 17 Vector APIs when
> > available without refactoring their existing libraries
>
> That looks great.
>
> >
> > Also do the Apache projects/ license allow for integration with non
> Apache
> > software (jtransform /jblas do not use Apache license, jcuda uses MIT
> > license) ?
>
> Licence issues are detailed here:
>   https://www.apache.org/legal/resolved.html
>
> Best regards,
> Gilles
>
> >
> > Thanks
> > Sumanth
> >
> > [1] http://jblas.org/
> >
> > [2]
> https://www.nag.com/numeric/nl/nagdoc_latest/clhtml/c06/c06conts.html
> >
> > [3]
> https://blogs.oracle.com/javamagazine/post/programming-the-gpu-in-java
> >
> > [4] http://jcuda.org/jcuda/jcufft/JCufft.html
> >
> > [5]
> >
> https://github.com/jcuda/jcuda-samples/tree/master/JCudaSamples/src/main/java/jcuda/jcufft/samples
> >
> > [6] http://aparapi.github.io/
> >
> > [7] https://openjdk.java.net/jeps/414
> >
> >
> > On Tue, 22 Mar 2022 at 10:07, Gilles Sadowski <gi...@gmail.com>
> wrote:
> >
> > > Hello.
> > >
> > > > [...]
> > > > >
> > > > > 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)?
> > > > >
> > > >
> > > > Numbers should be seen as a toolbox to be used by other Java
> > > applications.
> > > > The best location for routines is something to discuss on the mailing
> > > list.
> > > > In the example of DCT, I am not aware if imaging currently has an
> encoder
> > > > implementation for this. There is a decoder:
> > > > org/apache/commons/imaging/formats/jpeg/decoder/Dct.jav
> > >
> > > Also:
> > >
> > >
> https://gitbox.apache.org/repos/asf?p=commons-math.git;a=blob;f=commons-math-transform/src/main/java/org/apache/commons/math4/transform/FastCosineTransform.java
> > >
> > > It would be a maintenance boon if "Commons" could come up with
> > > a consensus about which components must be dependency-free and
> > > which could depend on other (lower-level) "Commons" components.
> > >
> > > [Imaging] is clearly higher-level than [Math] and that such non-obvious
> > > algorithms should be maintained in a single place.  Through the process
> > > of modularizing [Math], we have "commons-math-transform" module,
> > > with zero dependency, so it would bring zero bloat to [Imaging] users
> if
> > > we consolidate usage.
> > >
> > > Of course, that would imply testing and benchmarking all current
> > > implementations, and retain the best (taking various axes into account:
> > > performance, robustness, flexibility).
> > >
> > > Regards,
> > > Gilles
> > >
> > > > [...]
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
> For additional commands, e-mail: dev-help@commons.apache.org
>
>

Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Gilles Sadowski <gi...@gmail.com>.
Hello.

Le lun. 28 mars 2022 à 00:32, Sumanth Rajkumar
<ra...@gmail.com> a écrit :
>
> Thanks Alex and Gilles for your feedback
>
> So currently Commons Math transform depends on Common complex numbers, and
> the API involves usage of Complex Object Arrays instead of primitive array
> data structures
>
> I also briefly looked into other library implementations besides Jtransform
> and EJML that are not pure java but have java bindings such as JBLAS[1] and
> NAG[2]
> All of the implementation use single array data structures to represent
> Complex Lists and higher dimensional matrices
>
> Since these involve parallel data pipelines I looked into libraries that
> use SIMD [3] operations that use GP GPU (jcuda [4][5] /aparapi [6]) and CPU
> (Java 17 Vector API [7])

Thanks for the investigation!

>
> Given all the alternative implementations, I agree it does not make sense
> to re implement transforms here.

Some transforms are (already) implemented here.
Of course, it makes sense to wonder whether to keep maintaining those
codes, or rely on external dependencies.  The decision would depend on
performance comparisons and whether users are able (or allowed) to
interface with native libraries.  [I know of one project where "pure Java"
was a requirement...]

>
> So instead would it be useful to provide users with
> 1) a complex numbers Linear Algebra and transforms API to compile against
> and run with any of existing providers (apache commons, jtransform, EJML,
> jblas)
> AND
> 2) a service provider interface to allow adapter implementations to
> integrate existing and future providers such as jcudas/aparapi/vector APIs
>
> Do the commons library modularization dependency requirements apply to
> compile time dependencies only or runtime also?

Which "dependency requirements" are you referring to?

> To minimize bloat, the runtime dependencies could be made optional and need
> not be transitively included by default

Flexibility would be ideal, indeed.

>
> Providing a Complex linear algebra and transforms API that can run with
> different runtime providers would allow users to take advantage of hardware
> capabilities and gracefully fallback to reference implementations
> It could allow users to take advantage of Java 17 Vector APIs when
> available without refactoring their existing libraries

That looks great.

>
> Also do the Apache projects/ license allow for integration with non Apache
> software (jtransform /jblas do not use Apache license, jcuda uses MIT
> license) ?

Licence issues are detailed here:
  https://www.apache.org/legal/resolved.html

Best regards,
Gilles

>
> Thanks
> Sumanth
>
> [1] http://jblas.org/
>
> [2] https://www.nag.com/numeric/nl/nagdoc_latest/clhtml/c06/c06conts.html
>
> [3] https://blogs.oracle.com/javamagazine/post/programming-the-gpu-in-java
>
> [4] http://jcuda.org/jcuda/jcufft/JCufft.html
>
> [5]
> https://github.com/jcuda/jcuda-samples/tree/master/JCudaSamples/src/main/java/jcuda/jcufft/samples
>
> [6] http://aparapi.github.io/
>
> [7] https://openjdk.java.net/jeps/414
>
>
> On Tue, 22 Mar 2022 at 10:07, Gilles Sadowski <gi...@gmail.com> wrote:
>
> > Hello.
> >
> > > [...]
> > > >
> > > > 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)?
> > > >
> > >
> > > Numbers should be seen as a toolbox to be used by other Java
> > applications.
> > > The best location for routines is something to discuss on the mailing
> > list.
> > > In the example of DCT, I am not aware if imaging currently has an encoder
> > > implementation for this. There is a decoder:
> > > org/apache/commons/imaging/formats/jpeg/decoder/Dct.jav
> >
> > Also:
> >
> > https://gitbox.apache.org/repos/asf?p=commons-math.git;a=blob;f=commons-math-transform/src/main/java/org/apache/commons/math4/transform/FastCosineTransform.java
> >
> > It would be a maintenance boon if "Commons" could come up with
> > a consensus about which components must be dependency-free and
> > which could depend on other (lower-level) "Commons" components.
> >
> > [Imaging] is clearly higher-level than [Math] and that such non-obvious
> > algorithms should be maintained in a single place.  Through the process
> > of modularizing [Math], we have "commons-math-transform" module,
> > with zero dependency, so it would bring zero bloat to [Imaging] users if
> > we consolidate usage.
> >
> > Of course, that would imply testing and benchmarking all current
> > implementations, and retain the best (taking various axes into account:
> > performance, robustness, flexibility).
> >
> > Regards,
> > Gilles
> >
> > > [...]

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org


Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Sumanth Rajkumar <ra...@gmail.com>.
Thanks Alex and Gilles for your feedback

So currently Commons Math transform depends on Common complex numbers, and
the API involves usage of Complex Object Arrays instead of primitive array
data structures

I also briefly looked into other library implementations besides Jtransform
and EJML that are not pure java but have java bindings such as JBLAS[1] and
NAG[2]
All of the implementation use single array data structures to represent
Complex Lists and higher dimensional matrices

Since these involve parallel data pipelines I looked into libraries that
use SIMD [3] operations that use GP GPU (jcuda [4][5] /aparapi [6]) and CPU
(Java 17 Vector API [7])

Given all the alternative implementations, I agree it does not make sense
to re implement transforms here.

So instead would it be useful to provide users with
1) a complex numbers Linear Algebra and transforms API to compile against
and run with any of existing providers (apache commons, jtransform, EJML,
jblas)
AND
2) a service provider interface to allow adapter implementations to
integrate existing and future providers such as jcudas/aparapi/vector APIs

Do the commons library modularization dependency requirements apply to
compile time dependencies only or runtime also?
To minimize bloat, the runtime dependencies could be made optional and need
not be transitively included by default

Providing a Complex linear algebra and transforms API that can run with
different runtime providers would allow users to take advantage of hardware
capabilities and gracefully fallback to reference implementations
It could allow users to take advantage of Java 17 Vector APIs when
available without refactoring their existing libraries

Also do the Apache projects/ license allow for integration with non Apache
software (jtransform /jblas do not use Apache license, jcuda uses MIT
license) ?

Thanks
Sumanth

[1] http://jblas.org/

[2] https://www.nag.com/numeric/nl/nagdoc_latest/clhtml/c06/c06conts.html

[3] https://blogs.oracle.com/javamagazine/post/programming-the-gpu-in-java

[4] http://jcuda.org/jcuda/jcufft/JCufft.html

[5]
https://github.com/jcuda/jcuda-samples/tree/master/JCudaSamples/src/main/java/jcuda/jcufft/samples

[6] http://aparapi.github.io/

[7] https://openjdk.java.net/jeps/414


On Tue, 22 Mar 2022 at 10:07, Gilles Sadowski <gi...@gmail.com> wrote:

> Hello.
>
> > [...]
> > >
> > > 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)?
> > >
> >
> > Numbers should be seen as a toolbox to be used by other Java
> applications.
> > The best location for routines is something to discuss on the mailing
> list.
> > In the example of DCT, I am not aware if imaging currently has an encoder
> > implementation for this. There is a decoder:
> > org/apache/commons/imaging/formats/jpeg/decoder/Dct.jav
>
> Also:
>
> https://gitbox.apache.org/repos/asf?p=commons-math.git;a=blob;f=commons-math-transform/src/main/java/org/apache/commons/math4/transform/FastCosineTransform.java
>
> It would be a maintenance boon if "Commons" could come up with
> a consensus about which components must be dependency-free and
> which could depend on other (lower-level) "Commons" components.
>
> [Imaging] is clearly higher-level than [Math] and that such non-obvious
> algorithms should be maintained in a single place.  Through the process
> of modularizing [Math], we have "commons-math-transform" module,
> with zero dependency, so it would bring zero bloat to [Imaging] users if
> we consolidate usage.
>
> Of course, that would imply testing and benchmarking all current
> implementations, and retain the best (taking various axes into account:
> performance, robustness, flexibility).
>
> Regards,
> Gilles
>
> > [...]
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
> For additional commands, e-mail: dev-help@commons.apache.org
>
>

Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Gilles Sadowski <gi...@gmail.com>.
Hello.

> [...]
> >
> > 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)?
> >
>
> Numbers should be seen as a toolbox to be used by other Java applications.
> The best location for routines is something to discuss on the mailing list.
> In the example of DCT, I am not aware if imaging currently has an encoder
> implementation for this. There is a decoder:
> org/apache/commons/imaging/formats/jpeg/decoder/Dct.jav

Also:
https://gitbox.apache.org/repos/asf?p=commons-math.git;a=blob;f=commons-math-transform/src/main/java/org/apache/commons/math4/transform/FastCosineTransform.java

It would be a maintenance boon if "Commons" could come up with
a consensus about which components must be dependency-free and
which could depend on other (lower-level) "Commons" components.

[Imaging] is clearly higher-level than [Math] and that such non-obvious
algorithms should be maintained in a single place.  Through the process
of modularizing [Math], we have "commons-math-transform" module,
with zero dependency, so it would bring zero bloat to [Imaging] users if
we consolidate usage.

Of course, that would imply testing and benchmarking all current
implementations, and retain the best (taking various axes into account:
performance, robustness, flexibility).

Regards,
Gilles

> [...]

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org


Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Alex Herbert <al...@gmail.com>.
On Sun, 20 Mar 2022 at 16:49, Sumanth Rajkumar <ra...@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
>

Support for the output of Jtransforms ideally should not copy any data.
This would be a wrapper to accept the array in place and allow further
operations on the set of array complex data. At the end the same array can
then be transformed back by JTransforms.

However accepting a stream of Complex objects and performing a collect
operation as a Stream termination operation to different backing data
structures would be something to investigate:

Complex[] c = ...
ComplexList<Complex> list =
Arrays.stream(c).collect(ComplexCollectors.toList());
double[] data = list.flatten();

Ideally the ComplexCollectors would create a collector to generate data in
a suitable format for any other library, e.g. JTransforms, so that the
flatten operation simply returns the underlying data. Both methods would
require arguments to allow the user to specify the organisation of the
underlying data structure. The flatten method would then have to support
transformation if necessary. The types of structure could be for a 1D
example:

double[] real
double[] imag

// Packed as real = i*2, imag = i*2+1
double[] data

// Packed as real = i, imag = len/2 + i
double[] data

Then support for 2, 3, ND equivalents. In this case the class Gilles
mentioned is useful:
org.apache.commons.numbers.arrays.MultidimensionCounter

Data transformation this way via streams may not be the optimal way to
perform this with regard to memory usage. A transform utility class may be
better suited as it can validate the dimensions of the input size before
any allocation is made for the output data structure. For example you
cannot create a 512*512 2D array if you do not have the correct size input.


>
> 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
>

This would be repeating a lot of work from JTransforms which already
implements support for parallel processing during the transform. I am not
sure what the gain would be.

Note that a FFT operation must operate on a vector of data, typically a
power of size 2. So that would limit the use in a stream to stripes of
data. Is this your intention? If so how would it differ from a parallel
striped implementation in JTransforms?


>
>
> 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
>

I do not have metrics for the usage of numbers. This is something that
requires an analysis of the dependencies of a large number of projects to
create a histogram of the frequency for library usage. I am not aware of a
recent meta analysis where we can obtain such data for Numbers which had
its first release in July 2021.

Typically there is no roadmap for Commons beyond accepting contributions
that add functionality that has general applicability. Future additions to
Numbers could be further refactoring of existing functionality in Commons
Math. There are already many parts of Commons Math that now depend on
Commons Numbers. Since this is a community supported project the direction
is really depicted by the input efforts of the community.


>
> 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)?
>

Numbers should be seen as a toolbox to be used by other Java applications.
The best location for routines is something to discuss on the mailing list.
In the example of DCT, I am not aware if imaging currently has an encoder
implementation for this. There is a decoder:
org/apache/commons/imaging/formats/jpeg/decoder/Dct.jav


>
> 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?
>

Complex numbers are a requirement for a C library that conforms to ISO C99.
So there are implementations of the functions, for example in GNU glibc.
There you can use a complex number just as a real number simply by:

#include <complex.h>
complex double a = 3 + 4 * I;
complex double b = -2 - 5 * I;
complex double c = sin(a * b);

#include <complex>
complex<double> a = complex<double>(3, 4);
...

But support for extended data structures would be either just simple arrays
or lists, or require a matrix library that can be typed to complex.

Matlab has complex number support for single and matrix operations. However
implementation of a matrix library is a significant effort. One approach
would be again to provide data structures that can interoperate with other
existing libraries such as EJML [1].

I have not used the complex matrices in EJML. The overview
indicates support for operators such as addition and multiplication. You
could investigate if the extension to Complex to allow calling the
functions as static methods on real and imaginary parts could be applied to
EJML complex matrices allowing workflows to be created for example with the
trigonomic operations in Complex.

Alex

[1] http://ejml.org/wiki/index.php?title=Main_Page

Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Gilles Sadowski <gi...@gmail.com>.
Le lun. 21 mars 2022 à 23:12, Alex Herbert <al...@gmail.com> a écrit :
>
> Hi,
>
> This lost the dev@commons in the to address. I am forwarding to the list to
> include the history.

From a quick read of the quoted messages below, I believe I must
point out that there is an FFT implementation in Commons Math.[1]
It could be construed as a (high priority) use case.  Thus, it should
be included in benchmarks and possibly adapted to work with the
proposed data-structure(s).

Regards,
Gilles

[1] https://gitbox.apache.org/repos/asf?p=commons-math.git;a=blob;f=commons-math-transform/src/main/java/org/apache/commons/math4/transform/FastFourierTransform.java

>
> On Sun, 20 Mar 2022 at 16:49, Sumanth Rajkumar <ra...@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 <al...@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.sumanth@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 <al...@gmail.com>
> >>> wrote:
> >>>
> >>>> Hi,
> >>>>
> >>>> Thanks for your interest in Commons Numbers.
> >>>>
> >>>> On Mon, 14 Mar 2022 at 00:09, Gilles Sadowski <gi...@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
> >>>>
> >>>

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org


Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Alex Herbert <al...@gmail.com>.
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 <ra...@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 <al...@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.sumanth@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 <al...@gmail.com>
>>> wrote:
>>>
>>>> Hi,
>>>>
>>>> Thanks for your interest in Commons Numbers.
>>>>
>>>> On Mon, 14 Mar 2022 at 00:09, Gilles Sadowski <gi...@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
>>>>
>>>

Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Sumanth Rajkumar <ra...@gmail.com>.
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 <al...@gmail.com> wrote:

> Hi,
>
> Thanks for your interest in Commons Numbers.
>
> On Mon, 14 Mar 2022 at 00:09, Gilles Sadowski <gi...@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
>

Re: [numbers][gsoc] GSoC 2022 - NUMBERS-186 Proposal

Posted by Alex Herbert <al...@gmail.com>.
Hi,

Thanks for your interest in Commons Numbers.

On Mon, 14 Mar 2022 at 00:09, Gilles Sadowski <gi...@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

Re: [numbers] GSoC 2022 - NUMBERS-186 Proposal

Posted by Gilles Sadowski <gi...@gmail.com>.
Hello.

Le dim. 13 mars 2022 à 02:08, Sumanth Rajkumar
<ra...@gmail.com> a écrit :
>
> SUBJECT: A proposal for Commons numbers  (NUMBERS-186)
>
> Hi Alex,
>
> I am Sumanth and new to Open source development. I would like to start by
> participating in GSOC 22.
>
> I came across your NUMBERS-186 GSOC mini project idea and took an initial
> stab at it

Thanks for your interest.

>
> If I understand the wish list correctly, we need to implement an efficient
> Complex List collection that supports all operations defined in the Complex
> class
>
> In my first attempt, I have implemented a ComplexList that is backed by two
> primitive double arrays for real and imaginary parts. The backing arrays
> grow as needed similar to the ArrayList<Complex> implementation and
> supports all operations of the List<Complex> interface
>
> I have added methods for operations from the Complex class with the
> following variations for each operation
>   a) Operation for a single complex number at given index
>   b) Operation for range of complex numbers with startIndex and length
>
> ComplexList applies all the operations in place modifying the backing
> arrays as per the requirement. I have also added support for an
> ImmutableComplexList that returns a copy of the List for each of the
> operations.

Preliminary remarks/questions:
* In this project, we avoid "import static".
* What is the purpose of having "protected" methods/fields?
* "final" should be used to declare every constant.

>
> 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

The many "TODO" methods should be left out to ease review.
[Better than having to wonder about code that looks (hopefully
temporarily) wrong.]

Unit tests should provide full coverage, also to simplify review.

> I would like to re-use the implementations from Complex class. For some of
> the simpler operations I exposed the private static methods from Complex
> class. However, in order to fully reuse all operations, the Complex class
>  will require more refactoring...

What do you suggest (provide one concrete example)?

>
> Am I on the right track?
> Will it be ok to refactor the Complex class to allow reuse of operation
> methods between Complex and ComplexList classes or should I just copy the
> implementations to List class?

No, code duplication should be avoided.

>
> I have also added a sample unit test for ComplexList similar to the
> existing ComplexTest.
>
> Based on feedback, I plan to complete all the TODO implementations &
> comments, add full unit test coverage and any other tasks required to raise
> a PR
>
> Further, if there is interest, I also plan to extend the ComplexList for
> higher dimensions (2D, 3D, 4D etc..)

This class could be useful:
  https://commons.apache.org/proper/commons-numbers/commons-numbers-arrays/apidocs/org/apache/commons/numbers/arrays/MultidimensionalCounter.html

Alex will probably further comment on whether this is going in the
right direction.  In particular, we should look for a way to "apply"
the various complex functions to all the numbers in a list, without
repeating the loop "boiler-plate" code.

Regards,
Gilles

>
> -Sumanth
>
> [1] https://issues.apache.org/jira/browse/NUMBERS-186
> [2]
> https://markmail.org/message/n4zpcxh7d7knq5tb?q=NUMBERS-186+list:org%2Eapache%2Ecommons%2Edev/
>
>
> I have posted two ideas for GSoC mini projects under:
> >
> > https://issues.apache.org/jira/browse/STATISTICS-54
> > https://issues.apache.org/jira/browse/NUMBERS-186
> >
> > Alex
> >

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org


[numbers] GSoC 2022 - NUMBERS-186 Proposal

Posted by Sumanth Rajkumar <ra...@gmail.com>.
SUBJECT: A proposal for Commons numbers  (NUMBERS-186)

Hi Alex,

I am Sumanth and new to Open source development. I would like to start by
participating in GSOC 22.

I came across your NUMBERS-186 GSOC mini project idea and took an initial
stab at it

If I understand the wish list correctly, we need to implement an efficient
Complex List collection that supports all operations defined in the Complex
class

In my first attempt, I have implemented a ComplexList that is backed by two
primitive double arrays for real and imaginary parts. The backing arrays
grow as needed similar to the ArrayList<Complex> implementation and
supports all operations of the List<Complex> interface

I have added methods for operations from the Complex class with the
following variations for each operation
  a) Operation for a single complex number at given index
  b) Operation for range of complex numbers with startIndex and length

ComplexList applies all the operations in place modifying the backing
arrays as per the requirement. I have also added support for an
ImmutableComplexList that returns a copy of the List for each of the
operations.

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

I would like to re-use the implementations from Complex class. For some of
the simpler operations I exposed the private static methods from Complex
class. However, in order to fully reuse all operations, the Complex class
 will require more refactoring...

Am I on the right track?
Will it be ok to refactor the Complex class to allow reuse of operation
methods between Complex and ComplexList classes or should I just copy the
implementations to List class?

I have also added a sample unit test for ComplexList similar to the
existing ComplexTest.

Based on feedback, I plan to complete all the TODO implementations &
comments, add full unit test coverage and any other tasks required to raise
a PR

Further, if there is interest, I also plan to extend the ComplexList for
higher dimensions (2D, 3D, 4D etc..)

-Sumanth

[1] https://issues.apache.org/jira/browse/NUMBERS-186
[2]
https://markmail.org/message/n4zpcxh7d7knq5tb?q=NUMBERS-186+list:org%2Eapache%2Ecommons%2Edev/


I have posted two ideas for GSoC mini projects under:
>
> https://issues.apache.org/jira/browse/STATISTICS-54
> https://issues.apache.org/jira/browse/NUMBERS-186
>
> Alex
>
>

Re: [geometry] PointMap and PointSet

Posted by Gilles Sadowski <gi...@gmail.com>.
Hello.

Le ven. 11 mars 2022 à 16:18, Matt Juntunen
<ma...@gmail.com> a écrit :
>
> Hello,
>
> I recently posted a PR [1] for GEOMETRY-142 [2], which is for adding
> PointMap and PointSet implementations. These are Map and Set
> implementations specifically designed to use Points as keys.

Is there a gentle introduction to how it works and/or the intended
use cases?

> They
> support fuzzy key comparison, meaning that points do not have to be
> exactly equal to each other in order to be considered equal by the
> map/set. (Note that this means these types do not follow the strict
> Map/Set contracts since they are not consistent with equals. This is
> documented in the PointMap/PointSet javadocs.)

Does it entail issues about some use cases or applications that
need this functionality?  Or do they not generally care about that
contract?
If so, maybe this collection shouldn't implement the standard JDK
interfaces (?).

> I've completely hidden
> the implementation details from the public API

Thanks.

> since I anticipate
> changes in the future with regard to the algorithms used.

Where does the anticipation come from?

> Instances
> are created through factory classes in each space. Ex:
>
> PointMap<Vector3D, String> map = EuclideanCollections.pointMap3D(precision);
> PointSet<Point2S> set = SphericalCollections.pointSet2S(precision);
>
> Since fuzzy key comparison is used, I've added the following methods
> to the interfaces to allow access to the exact, "canonical" version of
> the key stored in the collection.
>
> PointMap<P, V>  {
>     // return the key corresponding to pt, or null if not found
>     P resolveKey(P pt);
>
>     // return the map entry corresponding to pt, or null if not found
>     Map.Entry<P, V> resolveEntry(P pt);
> }
>
> PointSet<P> {
>     // return the key corresponding to pt, or null if not found
>     P resolve(P pt);
> }

I don't quite follow; which are the corresponding "non-canonical"
accessors?

>
> Reviews and comments are welcome.

Is there a notion of neighbours (as in: return the "n" entries that
are closest to a given point)?

Regards,
Gilles

>
> Regards,
> Matt Juntunen
>
>
> [1] https://github.com/apache/commons-geometry/pull/194
> [2] https://issues.apache.org/jira/projects/GEOMETRY/issues/GEOMETRY-142
>

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org