You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by er...@apache.org on 2022/01/25 00:37:52 UTC

[commons-math] branch master updated (645d85a -> 06301d0)

This is an automated email from the ASF dual-hosted git repository.

erans pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/commons-math.git.


    from 645d85a  Avoid array declarations written in C-style syntax and replace it with java.
     new c6b4ca9  MATH-1640: Do not try to outguess the caller.
     new 4844e3a  Upgrade dependencies.
     new e18ac1b  Replace deprecated calls.
     new f862efe  MATH-1589: Remove spurious "throws" clause.
     new 0223328  Upgrade dependency.
     new 49a38cd  Minor cosmetic change (POM).
     new 134d731  Unused "import".
     new 565b896  MATH-1580: "K-Means" clustering example.
     new 74a851b  MATH-1371: Elkan's enhancement to "K-Means" algorithm.
     new 9ce9f49  Add "ElkanKMeansPlusPlusClusterer" to example application.
     new 06301d0  Track changes.

The 11 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../examples-kmeans}/LICENCE                       |   0
 .../examples-kmeans}/NOTICE                        |   0
 .../examples-kmeans/image}/LICENCE                 |   0
 .../examples-kmeans/image}/NOTICE                  |   0
 .../image}/pom.xml                                 |  23 +-
 .../math4/examples/kmeans/image/ImageData.java     | 151 ++++++++
 .../math4/examples/kmeans/image/StandAlone.java    | 117 ++++++
 .../math4/examples/kmeans/image}/package-info.java |   4 +-
 .../{examples-sofm => examples-kmeans}/pom.xml     |  11 +-
 .../examples-sofm/chinese-rings/pom.xml            |   2 +-
 .../examples/sofm/chineserings/ChineseRings.java   |   2 +-
 .../sofm/chineserings/ChineseRingsClassifier.java  |   4 +-
 commons-math-examples/examples-sofm/tsp/pom.xml    |   2 +-
 .../math4/examples/sofm/tsp/StandAlone.java        |   2 +-
 commons-math-examples/pom.xml                      |  18 +-
 .../math4/legacy/ml/clustering/Clusterer.java      |  15 +-
 .../clustering/ElkanKMeansPlusPlusClusterer.java   | 409 +++++++++++++++++++++
 .../ml/clustering/KMeansPlusPlusClusterer.java     |  24 +-
 .../ElkanKMeansPlusPlusClustererTest.java          | 137 +++++++
 .../ml/clustering/KMeansPlusPlusClustererTest.java |  28 +-
 .../clustering/MiniBatchKMeansClustererTest.java   |   4 +-
 .../evaluation/CalinskiHarabaszTest.java           |   4 +-
 pom.xml                                            |   2 +-
 src/changes/changes.xml                            |   7 +
 .../clustering/ImageClusteringExample.java         | 203 ----------
 src/userguide/resources/ColorfulBird.jpg           | Bin 98227 -> 0 bytes
 26 files changed, 901 insertions(+), 268 deletions(-)
 copy {commons-math-neuralnet => commons-math-examples/examples-kmeans}/LICENCE (100%)
 copy {commons-math-neuralnet => commons-math-examples/examples-kmeans}/NOTICE (100%)
 copy {commons-math-neuralnet => commons-math-examples/examples-kmeans/image}/LICENCE (100%)
 copy {commons-math-neuralnet => commons-math-examples/examples-kmeans/image}/NOTICE (100%)
 copy commons-math-examples/{examples-sofm/chinese-rings => examples-kmeans/image}/pom.xml (75%)
 create mode 100644 commons-math-examples/examples-kmeans/image/src/main/java/org/apache/commons/math4/examples/kmeans/image/ImageData.java
 create mode 100644 commons-math-examples/examples-kmeans/image/src/main/java/org/apache/commons/math4/examples/kmeans/image/StandAlone.java
 copy commons-math-examples/{examples-sofm/tsp/src/main/java/org/apache/commons/math4/examples/sofm/tsp => examples-kmeans/image/src/main/java/org/apache/commons/math4/examples/kmeans/image}/package-info.java (89%)
 copy commons-math-examples/{examples-sofm => examples-kmeans}/pom.xml (87%)
 create mode 100644 commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/ml/clustering/ElkanKMeansPlusPlusClusterer.java
 create mode 100644 commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/ml/clustering/ElkanKMeansPlusPlusClustererTest.java
 delete mode 100644 src/userguide/java/org/apache/commons/math4/userguide/clustering/ImageClusteringExample.java
 delete mode 100644 src/userguide/resources/ColorfulBird.jpg

[commons-math] 01/11: MATH-1640: Do not try to outguess the caller.

Posted by er...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

erans pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-math.git

commit c6b4ca908cc53c6d5d89e911391337086062e74a
Author: Gilles Sadowski <gi...@gmail.com>
AuthorDate: Sat Jan 22 18:53:17 2022 +0100

    MATH-1640: Do not try to outguess the caller.
---
 .../ml/clustering/KMeansPlusPlusClusterer.java     | 24 +++++++++++++------
 .../ml/clustering/KMeansPlusPlusClustererTest.java | 28 +++++++++-------------
 .../clustering/MiniBatchKMeansClustererTest.java   |  4 ++--
 .../evaluation/CalinskiHarabaszTest.java           |  4 ++--
 4 files changed, 32 insertions(+), 28 deletions(-)

diff --git a/commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/ml/clustering/KMeansPlusPlusClusterer.java b/commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/ml/clustering/KMeansPlusPlusClusterer.java
index 9890194..57ab663 100644
--- a/commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/ml/clustering/KMeansPlusPlusClusterer.java
+++ b/commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/ml/clustering/KMeansPlusPlusClusterer.java
@@ -19,6 +19,7 @@ package org.apache.commons.math4.legacy.ml.clustering;
 
 import org.apache.commons.math4.legacy.exception.NullArgumentException;
 import org.apache.commons.math4.legacy.exception.ConvergenceException;
+import org.apache.commons.math4.legacy.exception.NotStrictlyPositiveException;
 import org.apache.commons.math4.legacy.exception.NumberIsTooSmallException;
 import org.apache.commons.math4.legacy.exception.util.LocalizedFormats;
 import org.apache.commons.math4.legacy.ml.distance.DistanceMeasure;
@@ -79,7 +80,7 @@ public class KMeansPlusPlusClusterer<T extends Clusterable> extends Clusterer<T>
      * @param k the number of clusters to split the data into
      */
     public KMeansPlusPlusClusterer(final int k) {
-        this(k, -1);
+        this(k, Integer.MAX_VALUE);
     }
 
     /** Build a clusterer.
@@ -104,8 +105,8 @@ public class KMeansPlusPlusClusterer<T extends Clusterable> extends Clusterer<T>
      *
      * @param k the number of clusters to split the data into
      * @param maxIterations the maximum number of iterations to run the algorithm for.
-     *   If negative, no maximum will be used.
      * @param measure the distance measure to use
+     * @throws NotStrictlyPositiveException if {@code k <= 0}.
      */
     public KMeansPlusPlusClusterer(final int k, final int maxIterations, final DistanceMeasure measure) {
         this(k, maxIterations, measure, RandomSource.MT_64.create());
@@ -132,20 +133,30 @@ public class KMeansPlusPlusClusterer<T extends Clusterable> extends Clusterer<T>
      *
      * @param k the number of clusters to split the data into
      * @param maxIterations the maximum number of iterations to run the algorithm for.
-     *   If negative, no maximum will be used.
      * @param measure the distance measure to use
      * @param random random generator to use for choosing initial centers
      * @param emptyStrategy strategy to use for handling empty clusters that
      * may appear during algorithm iterations
+     * @throws NotStrictlyPositiveException if {@code k <= 0} or
+     * {@code maxIterations <= 0}.
      */
-    public KMeansPlusPlusClusterer(final int k, final int maxIterations,
+    public KMeansPlusPlusClusterer(final int k,
+                                   final int maxIterations,
                                    final DistanceMeasure measure,
                                    final UniformRandomProvider random,
                                    final EmptyClusterStrategy emptyStrategy) {
         super(measure);
+
+        if (k <= 0) {
+            throw new NotStrictlyPositiveException(k);
+        }
+        if (maxIterations <= 0) {
+            throw new NotStrictlyPositiveException(maxIterations);
+        }
+
         this.numberOfClusters = k;
         this.maxIterations = maxIterations;
-        this.random        = random;
+        this.random = random;
         this.emptyStrategy = emptyStrategy;
     }
 
@@ -195,8 +206,7 @@ public class KMeansPlusPlusClusterer<T extends Clusterable> extends Clusterer<T>
         assignPointsToClusters(clusters, points, assignments);
 
         // iterate through updating the centers until we're done
-        final int max = (maxIterations < 0) ? Integer.MAX_VALUE : maxIterations;
-        for (int count = 0; count < max; count++) {
+        for (int count = 0; count < maxIterations; count++) {
             boolean hasEmptyCluster = clusters.stream().anyMatch(cluster->cluster.getPoints().isEmpty());
             List<CentroidCluster<T>> newClusters = adjustClustersCenters(clusters);
             int changes = assignPointsToClusters(newClusters, points, assignments);
diff --git a/commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/ml/clustering/KMeansPlusPlusClustererTest.java b/commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/ml/clustering/KMeansPlusPlusClustererTest.java
index a9e4979..a7f63b7 100644
--- a/commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/ml/clustering/KMeansPlusPlusClustererTest.java
+++ b/commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/ml/clustering/KMeansPlusPlusClustererTest.java
@@ -133,27 +133,21 @@ public class KMeansPlusPlusClustererTest {
     public void testSmallDistances() {
         // Create a bunch of CloseDoublePoints. Most are identical, but one is different by a
         // small distance.
-        int[] repeatedArray = { 0 };
-        int[] uniqueArray = { 1 };
-        DoublePoint repeatedPoint = new DoublePoint(repeatedArray);
-        DoublePoint uniquePoint = new DoublePoint(uniqueArray);
-
-        Collection<DoublePoint> points = new ArrayList<>();
-        final int NUM_REPEATED_POINTS = 10 * 1000;
-        for (int i = 0; i < NUM_REPEATED_POINTS; ++i) {
+        final int[] repeatedArray = { 0 };
+        final int[] uniqueArray = { 1 };
+        final DoublePoint repeatedPoint = new DoublePoint(repeatedArray);
+        final DoublePoint uniquePoint = new DoublePoint(uniqueArray);
+
+        final Collection<DoublePoint> points = new ArrayList<>();
+        final int numRepeated = 10000;
+        for (int i = 0; i < numRepeated; i++) {
             points.add(repeatedPoint);
         }
         points.add(uniquePoint);
 
-        // Ask a KMeansPlusPlusClusterer to run zero iterations (i.e., to simply choose initial
-        // cluster centers).
-        final int NUM_CLUSTERS = 2;
-        final int NUM_ITERATIONS = 0;
-
-        KMeansPlusPlusClusterer<DoublePoint> clusterer =
-            new KMeansPlusPlusClusterer<>(NUM_CLUSTERS, NUM_ITERATIONS,
-                    new CloseDistance(), random);
-        List<CentroidCluster<DoublePoint>> clusters = clusterer.cluster(points);
+        final KMeansPlusPlusClusterer<DoublePoint> clusterer =
+            new KMeansPlusPlusClusterer<>(2, 1, new CloseDistance(), random);
+        final List<CentroidCluster<DoublePoint>> clusters = clusterer.cluster(points);
 
         // Check that one of the chosen centers is the unique point.
         boolean uniquePointIsCenter = false;
diff --git a/commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/ml/clustering/MiniBatchKMeansClustererTest.java b/commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/ml/clustering/MiniBatchKMeansClustererTest.java
index ca6c7d1..8b9c222 100644
--- a/commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/ml/clustering/MiniBatchKMeansClustererTest.java
+++ b/commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/ml/clustering/MiniBatchKMeansClustererTest.java
@@ -58,9 +58,9 @@ public class MiniBatchKMeansClustererTest {
         final UniformRandomProvider rng = RandomSource.MT_64.create();
         List<DoublePoint> data = generateCircles(rng);
         KMeansPlusPlusClusterer<DoublePoint> kMeans =
-            new KMeansPlusPlusClusterer<>(4, -1, DEFAULT_MEASURE, rng);
+            new KMeansPlusPlusClusterer<>(4, Integer.MAX_VALUE, DEFAULT_MEASURE, rng);
         MiniBatchKMeansClusterer<DoublePoint> miniBatchKMeans =
-            new MiniBatchKMeansClusterer<>(4, -1, 100, 3, 300, 10, DEFAULT_MEASURE, rng,
+            new MiniBatchKMeansClusterer<>(4, Integer.MAX_VALUE, 100, 3, 300, 10, DEFAULT_MEASURE, rng,
                                            KMeansPlusPlusClusterer.EmptyClusterStrategy.LARGEST_VARIANCE);
         // Test 100 times between KMeansPlusPlusClusterer and MiniBatchKMeansClusterer
         for (int i = 0; i < 100; i++) {
diff --git a/commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/ml/clustering/evaluation/CalinskiHarabaszTest.java b/commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/ml/clustering/evaluation/CalinskiHarabaszTest.java
index 03f5295..f92c18d 100644
--- a/commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/ml/clustering/evaluation/CalinskiHarabaszTest.java
+++ b/commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/ml/clustering/evaluation/CalinskiHarabaszTest.java
@@ -63,7 +63,7 @@ public class CalinskiHarabaszTest {
         double actualBestScore = 0.0;
         for (int i = 0; i < 5; i++) {
             final int k = i + 2;
-            KMeansPlusPlusClusterer<DoublePoint> kMeans = new KMeansPlusPlusClusterer<>(k, -1, distanceMeasure, rnd);
+            KMeansPlusPlusClusterer<DoublePoint> kMeans = new KMeansPlusPlusClusterer<>(k, Integer.MAX_VALUE, distanceMeasure, rnd);
             List<CentroidCluster<DoublePoint>> clusters = kMeans.cluster(points);
             double score = evaluator.score(clusters);
             if (score > expectBestScore) {
@@ -89,7 +89,7 @@ public class CalinskiHarabaszTest {
         double actualBestScore = 0.0;
         for (int i = 0; i < 5; i++) {
             final int k = i + 2;
-            KMeansPlusPlusClusterer<DoublePoint> kMeans = new KMeansPlusPlusClusterer<>(k, -1, distanceMeasure, rnd);
+            KMeansPlusPlusClusterer<DoublePoint> kMeans = new KMeansPlusPlusClusterer<>(k, Integer.MAX_VALUE, distanceMeasure, rnd);
             List<CentroidCluster<DoublePoint>> clusters = kMeans.cluster(points);
             double score = evaluator.score(clusters);
             if (score > expectBestScore) {

[commons-math] 11/11: Track changes.

Posted by er...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

erans pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-math.git

commit 06301d00d5813f7dc0fbe0169a3ea837753a227f
Author: Gilles Sadowski <gi...@gmail.com>
AuthorDate: Tue Jan 25 01:30:07 2022 +0100

    Track changes.
---
 src/changes/changes.xml | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 572e9e2..e96a3ab 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -97,6 +97,13 @@ Caveat:
  nightmare was one of the main reasons for creating more focused
  components.]
 ">
+      <action dev="erans" type="updte" issue="MATH-1640">
+        Dot not change caller's arguments in "KMeansPlusPlusClusterer":
+        A negative value for "maxIterations" will raise an exception.
+      </action>
+      <action dev="erans" type="add" issue="MATH-1371" due-to="Artem Barger">
+        Add "ElkanKMeansPlusPlusClusterer" clustering algorithm.
+      </action>
       <action dev="erans" type="update" issue="MATH-1629">
         Throw "ArithmeticException" instead of "MathArithmeticException".
       </action>

[commons-math] 08/11: MATH-1580: "K-Means" clustering example.

Posted by er...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

erans pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-math.git

commit 565b896873048416f611c12e8667bd76a83f47a6
Author: Gilles Sadowski <gi...@gmail.com>
AuthorDate: Mon Jan 24 18:14:59 2022 +0100

    MATH-1580: "K-Means" clustering example.
    
    Adapted application moved to the "commons-math-examples" module.
    
    * GUI dependencies have been removed.
    * Any format supported by "Commons Imaging" can be used as input.
    * Clustered image saved in PNG format.
---
 commons-math-examples/examples-kmeans/LICENCE      | 201 ++++++++++++++++++++
 commons-math-examples/examples-kmeans/NOTICE       |   5 +
 .../examples-kmeans/image/LICENCE                  | 201 ++++++++++++++++++++
 commons-math-examples/examples-kmeans/image/NOTICE |   5 +
 .../examples-kmeans/image/pom.xml                  |  69 +++++++
 .../math4/examples/kmeans/image/ImageData.java     | 151 +++++++++++++++
 .../math4/examples/kmeans/image/StandAlone.java    |  80 ++++++++
 .../math4/examples/kmeans/image/package-info.java  |  22 +++
 commons-math-examples/examples-kmeans/pom.xml      |  57 ++++++
 commons-math-examples/pom.xml                      |  14 +-
 .../clustering/ImageClusteringExample.java         | 203 ---------------------
 src/userguide/resources/ColorfulBird.jpg           | Bin 98227 -> 0 bytes
 12 files changed, 803 insertions(+), 205 deletions(-)

diff --git a/commons-math-examples/examples-kmeans/LICENCE b/commons-math-examples/examples-kmeans/LICENCE
new file mode 100644
index 0000000..261eeb9
--- /dev/null
+++ b/commons-math-examples/examples-kmeans/LICENCE
@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/commons-math-examples/examples-kmeans/NOTICE b/commons-math-examples/examples-kmeans/NOTICE
new file mode 100644
index 0000000..28031e8
--- /dev/null
+++ b/commons-math-examples/examples-kmeans/NOTICE
@@ -0,0 +1,5 @@
+Apache Commons Math
+Copyright 2001-2022 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
diff --git a/commons-math-examples/examples-kmeans/image/LICENCE b/commons-math-examples/examples-kmeans/image/LICENCE
new file mode 100644
index 0000000..261eeb9
--- /dev/null
+++ b/commons-math-examples/examples-kmeans/image/LICENCE
@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/commons-math-examples/examples-kmeans/image/NOTICE b/commons-math-examples/examples-kmeans/image/NOTICE
new file mode 100644
index 0000000..28031e8
--- /dev/null
+++ b/commons-math-examples/examples-kmeans/image/NOTICE
@@ -0,0 +1,5 @@
+Apache Commons Math
+Copyright 2001-2022 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
diff --git a/commons-math-examples/examples-kmeans/image/pom.xml b/commons-math-examples/examples-kmeans/image/pom.xml
new file mode 100644
index 0000000..1239e5b
--- /dev/null
+++ b/commons-math-examples/examples-kmeans/image/pom.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0"?>
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.commons</groupId>
+    <artifactId>examples-kmeans</artifactId>
+    <version>4.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>examples-kmeans-image</artifactId>
+  <version>4.0-SNAPSHOT</version>
+  <!-- This name is used in the shaded jar to provide the application title for the version information. -->
+  <name>K-Means: Image Clustering</name>
+
+  <description>Clustering applied to 2D graphics data (image).</description>
+
+  <properties>
+    <maven.compiler.source>1.8</maven.compiler.source>
+    <maven.compiler.target>1.8</maven.compiler.target>
+
+    <!-- OSGi -->
+    <commons.osgi.symbolicName>org.apache.commons.math4.examples.kmeans.image</commons.osgi.symbolicName>
+    <commons.osgi.export>org.apache.commons.math4.examples.kmeans.image</commons.osgi.export>
+    <!-- Java 9+ -->
+    <commons.automatic.module.name>org.apache.commons.math4.examples.kmeans.image</commons.automatic.module.name>
+    <!-- Workaround to avoid duplicating config files. -->
+    <math.parent.dir>${basedir}/../../..</math.parent.dir>
+
+    <uberjar.name>examples-kmeans-image</uberjar.name>
+    <project.mainClass>org.apache.commons.math4.examples.kmeans.image.StandAlone</project.mainClass>
+  </properties>
+
+  <dependencies>
+
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-geometry-euclidean</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-math4-legacy</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-imaging</artifactId>
+    </dependency>
+
+  </dependencies>
+
+</project>
diff --git a/commons-math-examples/examples-kmeans/image/src/main/java/org/apache/commons/math4/examples/kmeans/image/ImageData.java b/commons-math-examples/examples-kmeans/image/src/main/java/org/apache/commons/math4/examples/kmeans/image/ImageData.java
new file mode 100644
index 0000000..44b98ec
--- /dev/null
+++ b/commons-math-examples/examples-kmeans/image/src/main/java/org/apache/commons/math4/examples/kmeans/image/ImageData.java
@@ -0,0 +1,151 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math4.examples.kmeans.image;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.awt.image.BufferedImage;
+import java.awt.image.Raster;
+import java.awt.image.WritableRaster;
+import org.apache.commons.imaging.Imaging;
+import org.apache.commons.imaging.ImageFormat;
+import org.apache.commons.imaging.ImageFormats;
+import org.apache.commons.imaging.ImageReadException;
+import org.apache.commons.imaging.ImageWriteException;
+import org.apache.commons.math4.legacy.ml.clustering.Clusterable;
+import org.apache.commons.math4.legacy.ml.clustering.Cluster;
+
+/**
+ * Retrieve pixel contents from an image file.
+ */
+final class ImageData {
+    /** Image data. */
+    private final Raster data;
+    /** Pixel dataset. */
+    private final List<PixelClusterable> pixels = new ArrayList<>();
+
+    /**
+     * @param image Image.
+     */
+    private ImageData(BufferedImage image) {
+        data = image.getData();
+
+        // Build dataset.
+        for (int row = 0; row < image.getHeight(); row++) {
+            for (int col = 0; col < image.getWidth(); col++) {
+                pixels.add(new PixelClusterable(col, row));
+            }
+        }
+    }
+
+    /**
+     * Load from file.
+     *
+     * @param file Graphics file.
+     * @return a new instance.
+     */
+    static ImageData load(String file) {
+        try {
+            return new ImageData(Imaging.getBufferedImage(new File(file)));
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        } catch (ImageReadException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Create clustered image and write it to a file in PNG format.
+     *
+     * @param clusters Clusters.
+     * @param outputPrefix Prefix of the output file.
+     * Graphics format extension will be appended.
+     */
+    void write(List<? extends Cluster<PixelClusterable>> clusters,
+               String outputPrefix) {
+        final BufferedImage imageC = new BufferedImage(data.getWidth(),
+                                                       data.getHeight(),
+                                                       BufferedImage.TYPE_INT_RGB);
+
+        final WritableRaster raster = imageC.getRaster();
+
+        for (Cluster<PixelClusterable> cluster : clusters) {
+            final double[] color = cluster.centroid().getPoint();
+            for (PixelClusterable pixel : cluster.getPoints()) {
+                raster.setPixel(pixel.x, pixel.y, color);
+            }
+        }
+
+        try {
+            final ImageFormat format = ImageFormats.PNG;
+            Imaging.writeImage(imageC,
+                               new File(outputPrefix + format.getDefaultExtension()),
+                               format);
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        } catch (ImageWriteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * @return the dataset.
+     */
+    Collection<PixelClusterable> getPixels() {
+        return Collections.unmodifiableCollection(pixels);
+    }
+
+    /**
+     * Bridge that presents a pixel as clusterable data.
+     * Instances are mutable; they keep a reference to the original
+     * image data.
+     */
+    class PixelClusterable implements Clusterable {
+        /** Pixel abscissa. */
+        private final int x;
+        /** Pixel ordinate. */
+        private final int y;
+        /** Pixel color. */
+        private double[] color;
+
+        /**
+         * @param x Abscissa.
+         * @param y Ordinate.
+         */
+        PixelClusterable(int x,
+                         int y) {
+            this.x = x;
+            this.y = y;
+            this.color = null;
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public double[] getPoint() {
+            if (color == null) {
+                color = data.getPixel(x, y, (double[]) null);
+            }
+            return color;
+        }
+    }
+}
diff --git a/commons-math-examples/examples-kmeans/image/src/main/java/org/apache/commons/math4/examples/kmeans/image/StandAlone.java b/commons-math-examples/examples-kmeans/image/src/main/java/org/apache/commons/math4/examples/kmeans/image/StandAlone.java
new file mode 100644
index 0000000..9f84a4b
--- /dev/null
+++ b/commons-math-examples/examples-kmeans/image/src/main/java/org/apache/commons/math4/examples/kmeans/image/StandAlone.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math4.examples.kmeans.image;
+
+import java.util.concurrent.Callable;
+
+import picocli.CommandLine;
+import picocli.CommandLine.Option;
+import picocli.CommandLine.Command;
+
+import org.apache.commons.rng.UniformRandomProvider;
+import org.apache.commons.rng.simple.RandomSource;
+import org.apache.commons.math4.legacy.ml.distance.DistanceMeasure;
+import org.apache.commons.math4.legacy.ml.distance.EuclideanDistance;
+import org.apache.commons.math4.legacy.ml.clustering.Clusterer;
+import org.apache.commons.math4.legacy.ml.clustering.KMeansPlusPlusClusterer;
+
+/**
+ * Application class.
+ */
+@Command(description = "Run the application",
+         mixinStandardHelpOptions = true)
+public final class StandAlone implements Callable<Void> {
+    /** The k parameter. */
+    @Option(names = { "-k" }, paramLabel = "K", required = true,
+            description = "Number of clusters.")
+    private int numClusters;
+    /** The maximal number of iterations. */
+    @Option(names = { "-i", "--iterations" }, paramLabel = "N",
+            description = "Allowed number of iterations (default: ${DEFAULT-VALUE}).")
+    private int maxIter = 2000;
+    /** The input file. */
+    @Option(names = { "--image" }, paramLabel = "FILE", required = true,
+            description = "Input file name.")
+    private String inputFile;
+    /** The output prefix. */
+    @Option(names = { "-o", "--output" }, paramLabel = "PREFIX", required = true,
+            description = "Prefix (path) for the output files.")
+    private String outputPrefix;
+
+    /**
+     * Program entry point.
+     *
+     * @param args Command line arguments and options.
+     */
+    public static void main(String[] args) {
+        CommandLine.call(new StandAlone(), args);
+    }
+
+    @Override
+    public Void call() {
+        final ImageData image = ImageData.load(inputFile);
+        final UniformRandomProvider rng = RandomSource.MWC_256.create();
+        final DistanceMeasure distance = new EuclideanDistance();
+        final Clusterer<ImageData.PixelClusterable> algo =
+            new KMeansPlusPlusClusterer<>(numClusters,
+                                          maxIter,
+                                          distance,
+                                          rng);
+        image.write(algo.cluster(image.getPixels()),
+                    outputPrefix + ".k_" + numClusters + ".kmeans.");
+
+        return null;
+    }
+}
diff --git a/commons-math-examples/examples-kmeans/image/src/main/java/org/apache/commons/math4/examples/kmeans/image/package-info.java b/commons-math-examples/examples-kmeans/image/src/main/java/org/apache/commons/math4/examples/kmeans/image/package-info.java
new file mode 100644
index 0000000..df8e6b2
--- /dev/null
+++ b/commons-math-examples/examples-kmeans/image/src/main/java/org/apache/commons/math4/examples/kmeans/image/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Clustering a 2D graphics (image).
+ */
+
+package org.apache.commons.math4.examples.kmeans.image;
diff --git a/commons-math-examples/examples-kmeans/pom.xml b/commons-math-examples/examples-kmeans/pom.xml
new file mode 100644
index 0000000..b40f5b9
--- /dev/null
+++ b/commons-math-examples/examples-kmeans/pom.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0"?>
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.commons</groupId>
+    <artifactId>commons-math-examples</artifactId>
+    <version>4.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>examples-kmeans</artifactId>
+  <version>4.0-SNAPSHOT</version>
+  <packaging>pom</packaging>
+  <name>K-Means</name>
+
+  <description>K-Means++ clustering sample codes.</description>
+
+  <properties>
+      <!-- Workaround to avoid duplicating config files. -->
+    <math.parent.dir>${basedir}/../..</math.parent.dir>
+  </properties>
+
+  <dependencies>
+
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-math4-legacy</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>info.picocli</groupId>
+      <artifactId>picocli</artifactId>
+    </dependency>
+
+  </dependencies>
+
+  <modules>
+    <module>image</module>
+  </modules>
+
+</project>
diff --git a/commons-math-examples/pom.xml b/commons-math-examples/pom.xml
index e89d8f6..ccef4d6 100644
--- a/commons-math-examples/pom.xml
+++ b/commons-math-examples/pom.xml
@@ -29,8 +29,10 @@
   <packaging>pom</packaging>
   <name>Example applications</name>
 
-  <description>Examples of use of the "Commons Math" library.
-  Codes in this module and its sub-modules are not part of the public API.
+  <description>
+    Usage examples of the "Commons Math" library.
+    Codes in this module and its sub-modules are not part of the library's API;
+    they can be updated or removed at any time.
   </description>
 
   <properties>
@@ -49,6 +51,7 @@
     <math.picocli.version>3.9.5</math.picocli.version>
     <math.rng.version>1.4</math.rng.version>
     <math.geometry.version>1.0</math.geometry.version>
+    <math.imaging.version>1.0-alpha3-SNAPSHOT</math.imaging.version>
 
     <!-- Disable JApiCmp failures (but keep the report). The examples API is allowed to change. -->
     <commons.japicmp.breakBuildOnBinaryIncompatibleModifications>false</commons.japicmp.breakBuildOnBinaryIncompatibleModifications>
@@ -90,6 +93,12 @@
       </dependency>
 
       <dependency>
+        <groupId>org.apache.commons</groupId>
+        <artifactId>commons-imaging</artifactId>
+        <version>${math.imaging.version}</version>
+      </dependency>
+
+      <dependency>
         <groupId>info.picocli</groupId>
         <artifactId>picocli</artifactId>
         <version>${math.picocli.version}</version>
@@ -158,6 +167,7 @@
 
   <modules>
     <module>examples-sofm</module>
+    <module>examples-kmeans</module>
   </modules>
 
 </project>
diff --git a/src/userguide/java/org/apache/commons/math4/userguide/clustering/ImageClusteringExample.java b/src/userguide/java/org/apache/commons/math4/userguide/clustering/ImageClusteringExample.java
deleted file mode 100644
index 67e197a..0000000
--- a/src/userguide/java/org/apache/commons/math4/userguide/clustering/ImageClusteringExample.java
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.commons.math4.userguide.clustering;
-
-import java.awt.BorderLayout;
-import java.awt.Color;
-import java.awt.Component;
-import java.awt.Dimension;
-import java.awt.FlowLayout;
-import java.awt.Graphics;
-import java.awt.GridLayout;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.awt.image.BufferedImage;
-import java.awt.image.Raster;
-import java.awt.image.WritableRaster;
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.imageio.ImageIO;
-import javax.swing.BorderFactory;
-import javax.swing.Box;
-import javax.swing.ImageIcon;
-import javax.swing.JButton;
-import javax.swing.JLabel;
-import javax.swing.JPanel;
-import javax.swing.JSpinner;
-import javax.swing.SpinnerNumberModel;
-
-import org.apache.commons.math4.ml.clustering.CentroidCluster;
-import org.apache.commons.math4.ml.clustering.Clusterable;
-import org.apache.commons.math4.ml.clustering.KMeansPlusPlusClusterer;
-import org.apache.commons.math4.userguide.ExampleUtils;
-import org.apache.commons.math4.userguide.ExampleUtils.ExampleFrame;
-
-/**
- * This example shows how clustering can be applied to images.
- */
-@SuppressWarnings("serial")
-public class ImageClusteringExample {
-
-    public static class Display extends ExampleFrame {
-        
-        private BufferedImage referenceImage;
-        private BufferedImage clusterImage;
-        
-        private Raster referenceRaster;
-        
-        private ImagePainter painter;
-        
-        private JSpinner clusterSizeSpinner;
-
-        public Display() throws Exception {
-            setTitle("Commons-Math: Image Clustering Example");
-            setSize(900, 350);
-            
-            setLayout(new FlowLayout());
-
-            Box bar = Box.createHorizontalBox();
-
-            referenceImage = ExampleUtils.resizeImage(
-                    ImageIO.read(new File("resources/ColorfulBird.jpg")),
-                    350,
-                    240,
-                    BufferedImage.TYPE_INT_RGB);
-
-            referenceRaster = referenceImage.getData();
-
-            clusterImage = new BufferedImage(referenceImage.getWidth(),
-                                             referenceImage.getHeight(),
-                                             BufferedImage.TYPE_INT_RGB);
-
-            JLabel picLabel = new JLabel(new ImageIcon(referenceImage));
-            bar.add(picLabel);
-
-            painter = new ImagePainter(clusterImage.getWidth(), clusterImage.getHeight());
-            bar.add(painter);
-
-            JPanel controlBox = new JPanel();
-            controlBox.setLayout(new GridLayout(5, 1));
-            controlBox.setBorder(BorderFactory.createLineBorder(Color.black, 1));
-            
-            JPanel sizeBox = new JPanel();
-            JLabel sizeLabel = new JLabel("Clusters:");
-            sizeBox.add(sizeLabel);
-            
-            SpinnerNumberModel model = new SpinnerNumberModel(3, 2, 10, 1);
-            clusterSizeSpinner = new JSpinner(model);
-            
-            sizeLabel.setLabelFor(clusterSizeSpinner);
-            sizeBox.add(clusterSizeSpinner);
-            controlBox.add(sizeBox, BorderLayout.NORTH);
-
-            JButton startButton = new JButton("Cluster");
-            startButton.setActionCommand("cluster");
-            controlBox.add(startButton, BorderLayout.CENTER);
-
-            bar.add(controlBox);
-
-            add(bar);
-
-            startButton.addActionListener(new ActionListener() {
-                public void actionPerformed(ActionEvent e) {
-                    clusterImage();
-                }
-            });
-        }
-        
-        private void clusterImage() {
-            List<PixelClusterable> pixels = new ArrayList<PixelClusterable>();
-            for (int row = 0; row < referenceImage.getHeight(); row++) {
-                for (int col = 0; col < referenceImage.getWidth(); col++) {
-                    pixels.add(new PixelClusterable(col, row));
-                }
-            }
-            
-            int clusterSize = ((Number) clusterSizeSpinner.getValue()).intValue();
-            KMeansPlusPlusClusterer<PixelClusterable> clusterer =
-                    new KMeansPlusPlusClusterer<PixelClusterable>(clusterSize);
-            List<CentroidCluster<PixelClusterable>> clusters = clusterer.cluster(pixels);
-            
-            WritableRaster raster = clusterImage.getRaster();
-            for (CentroidCluster<PixelClusterable> cluster : clusters) {
-                double[] color = cluster.getCenter().getPoint();
-                for (PixelClusterable pixel : cluster.getPoints()) {
-                    raster.setPixel(pixel.x, pixel.y, color);
-                }
-            }
-            
-            Display.this.repaint();
-        }
-
-        private class PixelClusterable implements Clusterable {
-            
-            private final int x;
-            private final int y;
-            private double[] color;
-            
-            public PixelClusterable(int x, int y) {
-                this.x = x;
-                this.y = y;
-                this.color = null;
-            }
-
-            @Override
-            public double[] getPoint() {
-                if (color == null) {
-                    color = referenceRaster.getPixel(x, y, (double[]) null);
-                }
-                return color;
-            }
-
-        }
-
-        private class ImagePainter extends Component {
-            
-            private int width;
-            private int height;
-
-            public ImagePainter(int width, int height) {
-                this.width = width;
-                this.height = height;
-            }
-
-            public Dimension getPreferredSize() {
-                return new Dimension(width, height);
-            }
-
-            @Override
-            public Dimension getMinimumSize() {
-                return getPreferredSize();
-            }
-
-            @Override
-            public Dimension getMaximumSize() {
-                return getPreferredSize();
-            }
-
-            public void paint(Graphics g) {
-                g.drawImage(clusterImage, 0, 0, this);
-            }
-        }
-    }
-
-    public static void main(String[] args) throws Exception {
-        ExampleUtils.showExampleFrame(new Display());
-    }
-}
diff --git a/src/userguide/resources/ColorfulBird.jpg b/src/userguide/resources/ColorfulBird.jpg
deleted file mode 100644
index 5753596..0000000
Binary files a/src/userguide/resources/ColorfulBird.jpg and /dev/null differ

[commons-math] 05/11: Upgrade dependency.

Posted by er...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

erans pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-math.git

commit 022332820ab5b228d508fc5d25d9169062512821
Author: Gilles Sadowski <gi...@gmail.com>
AuthorDate: Mon Jan 24 05:54:10 2022 +0100

    Upgrade dependency.
    
    Commons Statistics 1.0-SNAPSHOT requires Commons Numbers 1.1-SNAPSHOT.
---
 pom.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pom.xml b/pom.xml
index d763cb5..095cb6a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -62,7 +62,7 @@
     <math.checkstyle.version>3.1.0</math.checkstyle.version>
     <math.checkstyle.dep.version>8.29</math.checkstyle.dep.version>
     <math.mathjax.version>2.7.2</math.mathjax.version>
-    <math.commons.numbers.version>1.0</math.commons.numbers.version>
+    <math.commons.numbers.version>1.1-SNAPSHOT</math.commons.numbers.version>
     <math.commons.rng.version>1.4</math.commons.rng.version>
     <math.commons.geometry.version>1.0</math.commons.geometry.version>
     <math.commons.statistics.version>1.0-SNAPSHOT</math.commons.statistics.version>

[commons-math] 09/11: MATH-1371: Elkan's enhancement to "K-Means" algorithm.

Posted by er...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

erans pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-math.git

commit 74a851b611bf6db1c6177217f1a88b71352e3faf
Author: Gilles Sadowski <gi...@gmail.com>
AuthorDate: Mon Jan 24 19:05:14 2022 +0100

    MATH-1371: Elkan's enhancement to "K-Means" algorithm.
    
    Original implementation is a old PR provided by Artem Barger.
    Updated and changed to pass the code style checks.
    
    I did not review how this algorithm departs from the other
    implementations available in the same package, and whether
    some other design could minimize duplicate codes (but it
    would have been a pity to drop a potential performance
    improvement).
    A thorough examination should be considered, when tackling
    the refactoring of the "clustering" package.
    
    Closes #35.
---
 .../clustering/ElkanKMeansPlusPlusClusterer.java   | 409 +++++++++++++++++++++
 .../ElkanKMeansPlusPlusClustererTest.java          | 137 +++++++
 2 files changed, 546 insertions(+)

diff --git a/commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/ml/clustering/ElkanKMeansPlusPlusClusterer.java b/commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/ml/clustering/ElkanKMeansPlusPlusClusterer.java
new file mode 100644
index 0000000..e5c28ec
--- /dev/null
+++ b/commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/ml/clustering/ElkanKMeansPlusPlusClusterer.java
@@ -0,0 +1,409 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math4.legacy.ml.clustering;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.commons.rng.UniformRandomProvider;
+import org.apache.commons.math4.legacy.exception.NumberIsTooSmallException;
+import org.apache.commons.math4.legacy.ml.distance.DistanceMeasure;
+import org.apache.commons.math4.legacy.stat.descriptive.moment.VectorialMean;
+
+/**
+ * Implementation of k-means++ algorithm.
+ * It is based on
+ * <blockquote>
+ *  Elkan, Charles.
+ *  "Using the triangle inequality to accelerate k-means."
+ *  ICML. Vol. 3. 2003.
+ * </blockquote>
+ *
+ * <p>
+ * Algorithm uses triangle inequality to speed up computation, by reducing
+ * the amount of distances calculations.  Towards the last iterations of
+ * the algorithm, points which already assigned to some cluster are unlikely
+ * to move to a new cluster; updates of cluster centers are also usually
+ * relatively small.
+ * Triangle inequality is thus used to determine the cases where distance
+ * computation could be skipped since center move only a little, without
+ * affecting points partitioning.
+ *
+ * <p>
+ * For initial centers seeding, we apply the algorithm described in
+ * <blockquote>
+ *  Arthur, David, and Sergei Vassilvitskii.
+ *  "k-means++: The advantages of careful seeding."
+ *  Proceedings of the eighteenth annual ACM-SIAM symposium on Discrete algorithms.
+ *  Society for Industrial and Applied Mathematics, 2007.
+ * </blockquote>
+ *
+ * @param <T> Type of the points to cluster.
+ */
+public class ElkanKMeansPlusPlusClusterer<T extends Clusterable>
+    extends KMeansPlusPlusClusterer<T> {
+
+    /**
+     * @param k Clustering parameter.
+     */
+    public ElkanKMeansPlusPlusClusterer(int k) {
+        super(k);
+    }
+
+    /**
+     * @param k Clustering parameter.
+     * @param maxIterations Allowed number of iterations.
+     * @param measure Distance measure.
+     * @param random Random generator.
+     */
+    public ElkanKMeansPlusPlusClusterer(int k,
+                                        int maxIterations,
+                                        DistanceMeasure measure,
+                                        UniformRandomProvider random) {
+        super(k, maxIterations, measure, random);
+    }
+
+    /**
+     * @param k Clustering parameter.
+     * @param maxIterations Allowed number of iterations.
+     * @param measure Distance measure.
+     * @param random Random generator.
+     * @param emptyStrategy Strategy for handling empty clusters that
+     * may appear during algorithm progress.
+     */
+    public ElkanKMeansPlusPlusClusterer(int k,
+                                        int maxIterations,
+                                        DistanceMeasure measure,
+                                        UniformRandomProvider random,
+                                        EmptyClusterStrategy emptyStrategy) {
+        super(k, maxIterations, measure, random, emptyStrategy);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public List<CentroidCluster<T>> cluster(final Collection<T> points) {
+        final int k = getNumberOfClusters();
+
+        // Number of clusters has to be smaller or equal the number of data points.
+        if (points.size() < k) {
+            throw new NumberIsTooSmallException(points.size(), k, false);
+        }
+
+        final List<T> pointsList = new ArrayList<>(points);
+        final int n = points.size();
+        final int dim = pointsList.get(0).getPoint().length;
+
+        // Keep minimum intra cluster distance, e.g. for given cluster c s[c] is
+        // the distance to the closest cluster c' or s[c] = 1/2 * min_{c'!=c} dist(c', c)
+        final double[] s = new double[k];
+        Arrays.fill(s, Double.MAX_VALUE);
+        // Store the matrix of distances between all cluster centers, e.g. dcc[c1][c2] = distance(c1, c2)
+        final double[][] dcc = new double[k][k];
+
+        // For each point keeps the upper bound distance to the cluster center.
+        final double[] u = new double[n];
+        Arrays.fill(u, Double.MAX_VALUE);
+
+        // For each point and for each cluster keeps the lower bound for the distance between the point and cluster
+        final double[][] l = new double[n][k];
+
+        // Seed initial set of cluster centers.
+        final double[][] centers = seed(pointsList);
+
+        // Points partitioning induced by cluster centers, e.g. for point xi the value of partitions[xi] indicates
+        // the cluster or index of the cluster center which is closest to xi. partitions[xi] = min_{c} distance(xi, c).
+        final int[] partitions = partitionPoints(pointsList, centers, u, l);
+
+        final double[] deltas = new double[k];
+        VectorialMean[] means = new VectorialMean[k];
+        for (int it = 0, changes = 0, max = getMaxIterations();
+             it < max;
+             it++, changes = 0) {
+            // Step I.
+            // Compute inter-cluster distances.
+            updateIntraCentersDistances(centers, dcc, s);
+
+            for (int xi = 0; xi < n; xi++) {
+                boolean r = true;
+
+                // Step II.
+                if (u[xi] <= s[partitions[xi]]) {
+                    continue;
+                }
+
+                for (int c = 0; c < k; c++) {
+                    // Check condition III.
+                    if (isSkipNext(partitions, u, l, dcc, xi, c)) {
+                        continue;
+                    }
+
+                    final double[] x = pointsList.get(xi).getPoint();
+
+                    // III(a)
+                    if (r) {
+                        u[xi] = distance(x, centers[partitions[xi]]);
+                        l[xi][partitions[xi]] = u[xi];
+                        r = false;
+                    }
+                    // III(b)
+                    if (u[xi] > l[xi][c] || u[xi] > dcc[partitions[xi]][c]) {
+                        l[xi][c] = distance(x, centers[c]);
+                        if (l[xi][c] < u[xi]) {
+                            partitions[xi] = c;
+                            u[xi] = l[xi][c];
+                            ++changes;
+                        }
+                    }
+                }
+            }
+
+            // Stopping criterion.
+            if (changes == 0 &&
+                it != 0) { // First iteration needed (to update bounds).
+                break;
+            }
+
+            // Step IV.
+            Arrays.fill(means, null);
+            for (int i = 0; i < n; i++) {
+                if (means[partitions[i]] == null) {
+                    means[partitions[i]] = new VectorialMean(dim);
+                }
+                means[partitions[i]].increment(pointsList.get(i).getPoint());
+            }
+
+            for (int i = 0; i < k; i++) {
+                deltas[i] = distance(centers[i], means[i].getResult());
+                centers[i] = means[i].getResult();
+            }
+
+            updateBounds(partitions, u, l, deltas);
+        }
+
+        return buildResults(pointsList, partitions, centers);
+    }
+
+    /**
+     * kmeans++ seeding which provides guarantee of resulting with log(k) approximation
+     * for final clustering results
+     * <p>
+     * Arthur, David, and Sergei Vassilvitskii. "k-means++: The advantages of careful seeding."
+     * Proceedings of the eighteenth annual ACM-SIAM symposium on Discrete algorithms.
+     * Society for Industrial and Applied Mathematics, 2007.
+     *
+     * @param points input data points
+     * @return an array of initial clusters centers
+     *
+     */
+    private double[][] seed(final List<T> points) {
+        final int k = getNumberOfClusters();
+        final UniformRandomProvider random = getRandomGenerator();
+
+        final double[][] result = new double[k][];
+        final int n = points.size();
+        final int pointIndex = random.nextInt(n);
+
+        final double[] minDistances = new double[n];
+
+        int idx = 0;
+        result[idx] = points.get(pointIndex).getPoint();
+
+        double sumSqDist = 0;
+
+        for (int i = 0; i < n; i++) {
+            final double d = distance(result[idx], points.get(i).getPoint());
+            minDistances[i] = d * d;
+            sumSqDist += minDistances[i];
+        }
+
+        while (++idx < k) {
+            final double p = sumSqDist * random.nextDouble();
+            int next = 0;
+            for (double cdf = 0; cdf < p; next++) {
+                cdf += minDistances[next];
+            }
+
+            result[idx] = points.get(next - 1).getPoint();
+            for (int i = 0; i < n; i++) {
+                final double d = distance(result[idx], points.get(i).getPoint());
+                sumSqDist -= minDistances[i];
+                minDistances[i] = Math.min(minDistances[i], d * d);
+                sumSqDist += minDistances[i];
+            }
+        }
+
+        return result;
+    }
+
+
+    /**
+     * Once initial centers are chosen, we can actually go through data points and assign points to the
+     * cluster based on the distance between initial centers and points.
+     *
+     * @param pointsList data points list
+     * @param centers current clusters centers
+     * @param u points upper bounds
+     * @param l lower bounds for points to clusters centers
+     *
+     * @return initial assignment of points into clusters
+     */
+    private int[] partitionPoints(List<T> pointsList,
+                                  double[][] centers,
+                                  double[] u,
+                                  double[][] l) {
+        final int k = getNumberOfClusters();
+        final int n = pointsList.size();
+        // Points assignments vector.
+        final int[] assignments = new int[n];
+        Arrays.fill(assignments, -1);
+        // Need to assign points to the clusters for the first time and intitialize the lower bound l(x, c)
+        for (int i = 0; i < n; i++) {
+            final double[] x = pointsList.get(i).getPoint();
+            for (int j = 0; j < k; j++) {
+                l[i][j] = distance(x, centers[j]); // l(x, c) = d(x, c)
+                if (u[i] > l[i][j]) {
+                    u[i] = l[i][j]; // u(x) = min_c d(x, c)
+                    assignments[i] = j; // c(x) = argmin_c d(x, c)
+                }
+            }
+        }
+        return assignments;
+    }
+
+    /**
+     * Updated distances between clusters centers and for each cluster
+     * pick the closest neighbour and keep distance to it.
+     *
+     * @param centers cluster centers
+     * @param dcc matrix of distance between clusters centers, e.g.
+     * {@code dcc[i][j] = distance(centers[i], centers[j])}
+     * @param s For a given cluster, {@code s[si]} holds distance value
+     * to the closest cluster center.
+     */
+    private void updateIntraCentersDistances(double[][] centers,
+                                             double[][] dcc,
+                                             double[] s) {
+        final int k = getNumberOfClusters();
+        for (int i = 0; i < k; i++) {
+            // Since distance(xi, xj) == distance(xj, xi), we need to update
+            // only upper or lower triangle of the distances matrix and mirror
+            // to the lower of upper triangle accordingly, trace has to be all
+            // zeros, since distance(xi, xi) == 0.
+            for (int j = i + 1; j < k; j++) {
+                dcc[i][j] = 0.5 * distance(centers[i], centers[j]);
+                dcc[j][i] = dcc[i][j];
+                if (dcc[i][j] < s[i]) {
+                    s[i] = dcc[i][j];
+                }
+                if (dcc[j][i] < s[j]) {
+                    s[j] = dcc[j][i];
+                }
+            }
+        }
+    }
+
+    /**
+     * For given points and and cluster, check condition (3) of Elkan algorithm.
+     *
+     * <ul>
+     *  <li>c is not the cluster xi assigned to</li>
+     *  <li>{@code u[xi] > l[xi][x]} upper bound for point xi is greater than
+     *   lower bound between xi and some cluster c</li>
+     *  <li>{@code u[xi] > 1/2 * d(c(xi), c)} upper bound is greater than
+     *   distance between center of xi's cluster and c</li>
+     * </ul>
+     *
+     * @param partitions current partition of points into clusters
+     * @param u upper bounds for points
+     * @param l lower bounds for distance between cluster centers and points
+     * @param dcc matrix of distance between clusters centers
+     * @param xi index of the point
+     * @param c index of the cluster
+     * @return true if conditions above satisfied false otherwise
+     */
+    private boolean isSkipNext(int partitions[],
+                               double[] u,
+                               double[][] l,
+                               double[][] dcc,
+                               int xi,
+                               int c) {
+        return (c == partitions[xi]) ||
+                (u[xi] <= l[xi][c]) ||
+                (u[xi] <= dcc[partitions[xi]][c]);
+    }
+
+    /**
+     * Once kmeans iterations have been converged and no more movements, we can build up the final
+     * resulted list of cluster centroids ({@link CentroidCluster}) and assign input points based
+     * on the converged partitioning.
+     *
+     * @param pointsList list of data points
+     * @param partitions current partition of points into clusters
+     * @param centers cluster centers
+     * @return cluster partitioning
+     */
+    private List<CentroidCluster<T>> buildResults(List<T> pointsList,
+                                                  int[] partitions,
+                                                  double[][] centers) {
+        final int k = getNumberOfClusters();
+        final List<CentroidCluster<T>> result = new ArrayList<>();
+        for (int i = 0; i < k; i++) {
+            final CentroidCluster<T> cluster = new CentroidCluster<>(new DoublePoint(centers[i]));
+            result.add(cluster);
+        }
+        for (int i = 0; i < pointsList.size(); i++) {
+            result.get(partitions[i]).addPoint(pointsList.get(i));
+        }
+        return result;
+    }
+
+    /**
+     * Based on the distance that cluster center has moved we need to update our upper and lower bound.
+     * Worst case assumption, the center of the assigned to given cluster moves away from the point, while
+     * centers of over clusters become closer.
+     *
+     * @param partitions current points assiments to the clusters
+     * @param u points upper bounds
+     * @param l lower bounds for distances between point and corresponding cluster
+     * @param deltas the movement delta for each cluster center
+     */
+    private void updateBounds(int[] partitions,
+                              double[] u,
+                              double[][] l,
+                              double[] deltas) {
+        final int k = getNumberOfClusters();
+        for (int i = 0; i < partitions.length; i++) {
+            u[i] += deltas[partitions[i]];
+            for (int j = 0; j < k; j++) {
+                l[i][j] = Math.max(0, l[i][j] - deltas[j]);
+            }
+        }
+    }
+
+    /**
+     * @param a Coordinates.
+     * @param b Coordinates.
+     * @return the distance between {@code a} and {@code b}.
+     */
+    private double distance(final double[] a,
+                            final double[] b) {
+        return getDistanceMeasure().compute(a, b);
+    }
+}
diff --git a/commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/ml/clustering/ElkanKMeansPlusPlusClustererTest.java b/commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/ml/clustering/ElkanKMeansPlusPlusClustererTest.java
new file mode 100644
index 0000000..a7723c6
--- /dev/null
+++ b/commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/ml/clustering/ElkanKMeansPlusPlusClustererTest.java
@@ -0,0 +1,137 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math4.legacy.ml.clustering;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import org.junit.Assert;
+import org.junit.Test;
+
+import org.apache.commons.rng.simple.RandomSource;
+import org.apache.commons.rng.sampling.shape.BoxSampler;
+import org.apache.commons.math4.legacy.exception.NotStrictlyPositiveException;
+import org.apache.commons.math4.legacy.exception.NumberIsTooSmallException;
+import org.apache.commons.math4.legacy.stat.descriptive.moment.VectorialMean;
+import org.apache.commons.math4.legacy.core.MathArrays;
+
+/**
+ * Tests for {@link ElkanKmeansPlusPlusClusterer}.
+ */
+public class ElkanKMeansPlusPlusClustererTest {
+    @Test
+    public void validateOneDimensionSingleClusterZeroMean() {
+        final List<DoublePoint> testPoints = Arrays.asList(new DoublePoint(new double[]{1}),
+                                                           new DoublePoint(new double[]{2}),
+                                                           new DoublePoint(new double[]{-3}));
+        final ElkanKMeansPlusPlusClusterer<DoublePoint> clusterer = new ElkanKMeansPlusPlusClusterer<>(1);
+        final List<CentroidCluster<DoublePoint>> clusters = clusterer.cluster(testPoints);
+        Assert.assertEquals(1, clusters.size());
+        Assert.assertTrue(MathArrays.equals(new double[]{0}, clusters.get(0).getCenter().getPoint()));
+    }
+
+    @Test(expected = NumberIsTooSmallException.class)
+    public void illegalKParameter() {
+        final int n = 20;
+        final int d = 3;
+        final int k = 100;
+
+        final List<DoublePoint> testPoints = generatePoints(n, d);
+        final ElkanKMeansPlusPlusClusterer<DoublePoint> clusterer = new ElkanKMeansPlusPlusClusterer<>(k);
+        clusterer.cluster(testPoints);
+    }
+
+    @Test
+    public void numberOfClustersSameAsInputSize() {
+        final int n = 3;
+        final int d = 2;
+        final int k = 3;
+
+        final List<DoublePoint> testPoints = generatePoints(n, d);
+        final ElkanKMeansPlusPlusClusterer<DoublePoint> clusterer = new ElkanKMeansPlusPlusClusterer<>(k);
+        final List<CentroidCluster<DoublePoint>> clusters = clusterer.cluster(testPoints);
+        Assert.assertEquals(k, clusters.size());
+        Assert.assertEquals(1, clusters.get(0).getPoints().size());
+        Assert.assertEquals(1, clusters.get(1).getPoints().size());
+        Assert.assertEquals(1, clusters.get(2).getPoints().size());
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void illegalInputParameter() {
+        final ElkanKMeansPlusPlusClusterer<DoublePoint> clusterer = new ElkanKMeansPlusPlusClusterer<>(10);
+        clusterer.cluster(null);
+    }
+
+    @Test(expected = NumberIsTooSmallException.class)
+    public void emptyInputPointsList() {
+        final ElkanKMeansPlusPlusClusterer<DoublePoint> clusterer = new ElkanKMeansPlusPlusClusterer<>(10);
+        clusterer.cluster(Collections.<DoublePoint>emptyList());
+    }
+
+    @Test(expected = NotStrictlyPositiveException.class)
+    public void negativeKParameterValue() {
+        new ElkanKMeansPlusPlusClusterer<>(-1);
+    }
+
+    @Test(expected = NotStrictlyPositiveException.class)
+    public void kParameterEqualsZero() {
+        new ElkanKMeansPlusPlusClusterer<>(0);
+    }
+
+    @Test
+    public void oneClusterCenterShouldBeTheMean() {
+        final int n = 100;
+        final int d = 2;
+
+        final List<DoublePoint> testPoints = generatePoints(n, d);
+        final KMeansPlusPlusClusterer<DoublePoint> clusterer = new KMeansPlusPlusClusterer<>(1);
+
+        final List<CentroidCluster<DoublePoint>> clusters = clusterer.cluster(testPoints);
+
+        final VectorialMean mean = new VectorialMean(d);
+        for (DoublePoint each : testPoints) {
+            mean.increment(each.getPoint());
+        }
+        Assert.assertEquals(1, clusters.size());
+        Assert.assertArrayEquals(mean.getResult(), clusters.get(0).getCenter().getPoint(), 1e-6);
+    }
+
+    /**
+     * Generates a list of random uncorrelated points to cluster.
+     *
+     * @param n number of points
+     * @param d dimensionality
+     * @return list of n generated random vectors of dimension d.
+     */
+    private static List<DoublePoint> generatePoints(int n, int d) {
+        final List<DoublePoint> results = new ArrayList<>();
+        final double[] lower = new double[d];
+        final double[] upper = new double[d];
+        Arrays.fill(upper, 1);
+        final BoxSampler rnd = BoxSampler.of(RandomSource.KISS.create(),
+                                             lower,
+                                             upper);
+
+        for (int i = 0; i < n; i++) {
+            results.add(new DoublePoint(rnd.sample()));
+        }
+
+        return results;
+    }
+}

[commons-math] 10/11: Add "ElkanKMeansPlusPlusClusterer" to example application.

Posted by er...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

erans pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-math.git

commit 9ce9f49d4a8d81a633882a8ea126de6c33207384
Author: Gilles Sadowski <gi...@gmail.com>
AuthorDate: Tue Jan 25 01:27:17 2022 +0100

    Add "ElkanKMeansPlusPlusClusterer" to example application.
---
 .../math4/examples/kmeans/image/StandAlone.java    | 51 +++++++++++++++++++---
 1 file changed, 44 insertions(+), 7 deletions(-)

diff --git a/commons-math-examples/examples-kmeans/image/src/main/java/org/apache/commons/math4/examples/kmeans/image/StandAlone.java b/commons-math-examples/examples-kmeans/image/src/main/java/org/apache/commons/math4/examples/kmeans/image/StandAlone.java
index 9f84a4b..f0081e7 100644
--- a/commons-math-examples/examples-kmeans/image/src/main/java/org/apache/commons/math4/examples/kmeans/image/StandAlone.java
+++ b/commons-math-examples/examples-kmeans/image/src/main/java/org/apache/commons/math4/examples/kmeans/image/StandAlone.java
@@ -18,6 +18,8 @@
 package org.apache.commons.math4.examples.kmeans.image;
 
 import java.util.concurrent.Callable;
+import java.time.Instant;
+import java.time.Duration;
 
 import picocli.CommandLine;
 import picocli.CommandLine.Option;
@@ -29,6 +31,7 @@ import org.apache.commons.math4.legacy.ml.distance.DistanceMeasure;
 import org.apache.commons.math4.legacy.ml.distance.EuclideanDistance;
 import org.apache.commons.math4.legacy.ml.clustering.Clusterer;
 import org.apache.commons.math4.legacy.ml.clustering.KMeansPlusPlusClusterer;
+import org.apache.commons.math4.legacy.ml.clustering.ElkanKMeansPlusPlusClusterer;
 
 /**
  * Application class.
@@ -67,14 +70,48 @@ public final class StandAlone implements Callable<Void> {
         final ImageData image = ImageData.load(inputFile);
         final UniformRandomProvider rng = RandomSource.MWC_256.create();
         final DistanceMeasure distance = new EuclideanDistance();
-        final Clusterer<ImageData.PixelClusterable> algo =
-            new KMeansPlusPlusClusterer<>(numClusters,
-                                          maxIter,
-                                          distance,
-                                          rng);
-        image.write(algo.cluster(image.getPixels()),
-                    outputPrefix + ".k_" + numClusters + ".kmeans.");
+
+        cluster(image,
+                new ElkanKMeansPlusPlusClusterer<ImageData.PixelClusterable>(numClusters,
+                                                                             maxIter,
+                                                                             distance,
+                                                                             rng),
+                "elkan");
+        cluster(image,
+                new KMeansPlusPlusClusterer<ImageData.PixelClusterable>(numClusters,
+                                                                        maxIter,
+                                                                        distance,
+                                                                        rng),
+                "kmeans");
 
         return null;
     }
+
+    /**
+     * Perform clustering and write results.
+     *
+     * @param image Input.
+     * @param algo Algorithm to do the clustering.
+     * @param id Identifier for output file name.
+     */
+    private void cluster(ImageData image,
+                         Clusterer<ImageData.PixelClusterable> algo,
+                         String id) {
+        final String dot = ".";
+        final String out = new StringBuilder()
+            .append(outputPrefix)
+            .append(dot)
+            .append("k_")
+            .append(numClusters)
+            .append(dot)
+            .append(id)
+            .append(dot)
+            .toString();
+
+        final Instant start = Instant.now();
+        image.write(algo.cluster(image.getPixels()), out);
+        //CHECKSTYLE: stop all
+        System.out.println("time=" + Duration.between(start, Instant.now()).toMillis());
+        //CHECKSTYLE: resume all
+    }
 }

[commons-math] 07/11: Unused "import".

Posted by er...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

erans pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-math.git

commit 134d731a6bc048bfe29754fb294ad64b12b67827
Author: Gilles Sadowski <gi...@gmail.com>
AuthorDate: Mon Jan 24 18:12:52 2022 +0100

    Unused "import".
---
 .../apache/commons/math4/legacy/ml/clustering/Clusterer.java  | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/ml/clustering/Clusterer.java b/commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/ml/clustering/Clusterer.java
index e1c4266..055b7b1 100644
--- a/commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/ml/clustering/Clusterer.java
+++ b/commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/ml/clustering/Clusterer.java
@@ -19,8 +19,6 @@ package org.apache.commons.math4.legacy.ml.clustering;
 import java.util.Collection;
 import java.util.List;
 
-import org.apache.commons.math4.legacy.exception.ConvergenceException;
-import org.apache.commons.math4.legacy.exception.MathIllegalArgumentException;
 import org.apache.commons.math4.legacy.ml.distance.DistanceMeasure;
 
 /**
@@ -48,10 +46,11 @@ public abstract class Clusterer<T extends Clusterable> {
      *
      * @param points the set of {@link Clusterable} instances
      * @return a {@link List} of clusters
-     * @throws MathIllegalArgumentException if points are null or the number of
-     *   data points is not compatible with this clusterer
-     * @throws ConvergenceException if the algorithm has not yet converged after
-     *   the maximum number of iterations has been exceeded
+     * @throws IllegalArgumentException if points are null or the number of
+     * data points is not compatible with this clusterer.
+     * @throws org.apache.commons.math4.legacy.exception.ConvergenceException
+     * if the algorithm has not yet converged after the maximum number of
+     * iterations has been exceeded.
      */
     public abstract List<? extends Cluster<T>> cluster(Collection<T> points);
 

[commons-math] 02/11: Upgrade dependencies.

Posted by er...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

erans pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-math.git

commit 4844e3aeab99cda4d6eff0e7f918c858c1abc5e9
Author: Gilles Sadowski <gi...@gmail.com>
AuthorDate: Sun Jan 23 03:55:21 2022 +0100

    Upgrade dependencies.
---
 commons-math-examples/pom.xml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/commons-math-examples/pom.xml b/commons-math-examples/pom.xml
index 595491f..e89d8f6 100644
--- a/commons-math-examples/pom.xml
+++ b/commons-math-examples/pom.xml
@@ -47,8 +47,8 @@
 
     <math.version>4.0-SNAPSHOT</math.version>
     <math.picocli.version>3.9.5</math.picocli.version>
-    <math.rng.version>1.3</math.rng.version>
-    <math.geometry.version>1.0-beta1</math.geometry.version>
+    <math.rng.version>1.4</math.rng.version>
+    <math.geometry.version>1.0</math.geometry.version>
 
     <!-- Disable JApiCmp failures (but keep the report). The examples API is allowed to change. -->
     <commons.japicmp.breakBuildOnBinaryIncompatibleModifications>false</commons.japicmp.breakBuildOnBinaryIncompatibleModifications>

[commons-math] 04/11: MATH-1589: Remove spurious "throws" clause.

Posted by er...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

erans pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-math.git

commit f862efe4c6b84a06548e72f5b5088e22f099fb32
Author: Gilles Sadowski <gi...@gmail.com>
AuthorDate: Mon Jan 24 05:35:06 2022 +0100

    MATH-1589: Remove spurious "throws" clause.
---
 .../java/org/apache/commons/math4/legacy/ml/clustering/Clusterer.java | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/ml/clustering/Clusterer.java b/commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/ml/clustering/Clusterer.java
index d56152e..e1c4266 100644
--- a/commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/ml/clustering/Clusterer.java
+++ b/commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/ml/clustering/Clusterer.java
@@ -53,8 +53,7 @@ public abstract class Clusterer<T extends Clusterable> {
      * @throws ConvergenceException if the algorithm has not yet converged after
      *   the maximum number of iterations has been exceeded
      */
-    public abstract List<? extends Cluster<T>> cluster(Collection<T> points)
-            throws MathIllegalArgumentException, ConvergenceException;
+    public abstract List<? extends Cluster<T>> cluster(Collection<T> points);
 
     /**
      * Returns the {@link DistanceMeasure} instance used by this clusterer.
@@ -76,5 +75,4 @@ public abstract class Clusterer<T extends Clusterable> {
     protected double distance(final Clusterable p1, final Clusterable p2) {
         return measure.compute(p1.getPoint(), p2.getPoint());
     }
-
 }

[commons-math] 06/11: Minor cosmetic change (POM).

Posted by er...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

erans pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-math.git

commit 49a38cdf0323fa3d605ca61696ba8f1a9e277877
Author: Gilles Sadowski <gi...@gmail.com>
AuthorDate: Mon Jan 24 06:37:38 2022 +0100

    Minor cosmetic change (POM).
---
 commons-math-examples/examples-sofm/chinese-rings/pom.xml | 2 +-
 commons-math-examples/examples-sofm/tsp/pom.xml           | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/commons-math-examples/examples-sofm/chinese-rings/pom.xml b/commons-math-examples/examples-sofm/chinese-rings/pom.xml
index 905e009..9df68c3 100644
--- a/commons-math-examples/examples-sofm/chinese-rings/pom.xml
+++ b/commons-math-examples/examples-sofm/chinese-rings/pom.xml
@@ -27,7 +27,7 @@
   <artifactId>examples-sofm-chinese-rings</artifactId>
   <version>4.0-SNAPSHOT</version>
   <!-- This name is used in the shaded jar to provide the application title for the version information. -->
-  <name>Chinese Rings</name>
+  <name>SOFM: Chinese Rings</name>
 
   <description>SOFM used for classification.</description>
 
diff --git a/commons-math-examples/examples-sofm/tsp/pom.xml b/commons-math-examples/examples-sofm/tsp/pom.xml
index 0360b7c..a62f9bb 100644
--- a/commons-math-examples/examples-sofm/tsp/pom.xml
+++ b/commons-math-examples/examples-sofm/tsp/pom.xml
@@ -27,7 +27,7 @@
   <artifactId>examples-sofm-tsp</artifactId>
   <version>4.0-SNAPSHOT</version>
   <!-- This name is used in the shaded jar to provide the application title for the version information. -->
-  <name>Traveling Salesman Problem</name>
+  <name>SOFM: Traveling Salesman Problem</name>
 
   <description>SOFM used for optimization.</description>
 

[commons-math] 03/11: Replace deprecated calls.

Posted by er...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

erans pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-math.git

commit e18ac1b03b095c3485fd2c86bb1b2843927ff80b
Author: Gilles Sadowski <gi...@gmail.com>
AuthorDate: Sun Jan 23 03:56:07 2022 +0100

    Replace deprecated calls.
---
 .../apache/commons/math4/examples/sofm/chineserings/ChineseRings.java | 2 +-
 .../math4/examples/sofm/chineserings/ChineseRingsClassifier.java      | 4 ++--
 .../java/org/apache/commons/math4/examples/sofm/tsp/StandAlone.java   | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/commons-math-examples/examples-sofm/chinese-rings/src/main/java/org/apache/commons/math4/examples/sofm/chineserings/ChineseRings.java b/commons-math-examples/examples-sofm/chinese-rings/src/main/java/org/apache/commons/math4/examples/sofm/chineserings/ChineseRings.java
index 3127c1c..dab8e16 100644
--- a/commons-math-examples/examples-sofm/chinese-rings/src/main/java/org/apache/commons/math4/examples/sofm/chineserings/ChineseRings.java
+++ b/commons-math-examples/examples-sofm/chinese-rings/src/main/java/org/apache/commons/math4/examples/sofm/chineserings/ChineseRings.java
@@ -58,7 +58,7 @@ class ChineseRings {
         // Second ring (centered around the first ring).
         final Vector3D[] secondRing = new Vector3D[numPointsRing2];
 
-        final UniformRandomProvider rng = RandomSource.create(RandomSource.WELL_19937_C);
+        final UniformRandomProvider rng = RandomSource.WELL_19937_C.create();
 
         // Create two rings lying in xy-plane.
         final UnitSphereSampler unit = UnitSphereSampler.of(rng, 2);
diff --git a/commons-math-examples/examples-sofm/chinese-rings/src/main/java/org/apache/commons/math4/examples/sofm/chineserings/ChineseRingsClassifier.java b/commons-math-examples/examples-sofm/chinese-rings/src/main/java/org/apache/commons/math4/examples/sofm/chineserings/ChineseRingsClassifier.java
index d8c5560..122ff6f 100644
--- a/commons-math-examples/examples-sofm/chinese-rings/src/main/java/org/apache/commons/math4/examples/sofm/chineserings/ChineseRingsClassifier.java
+++ b/commons-math-examples/examples-sofm/chinese-rings/src/main/java/org/apache/commons/math4/examples/sofm/chineserings/ChineseRingsClassifier.java
@@ -146,7 +146,7 @@ class ChineseRingsClassifier {
             0.1 * centre[2].getStandardDeviation()
         };
 
-        final UniformRandomProvider rng = RandomSource.create(RandomSource.SPLIT_MIX_64);
+        final UniformRandomProvider rng = RandomSource.SPLIT_MIX_64.create();
         return new FeatureInitializer[] {
             FeatureInitializerFactory.uniform(rng, mean[0] - dev[0], mean[0] + dev[0]),
             FeatureInitializerFactory.uniform(rng, mean[1] - dev[1], mean[1] + dev[1]),
@@ -166,7 +166,7 @@ class ChineseRingsClassifier {
             /** Data. */
             private final Vector3D[] points = rings.getPoints();
             /** RNG. */
-            private final UniformRandomProvider rng = RandomSource.create(RandomSource.KISS);
+            private final UniformRandomProvider rng = RandomSource.KISS.create();
             /** Number of samples. */
             private long n;
 
diff --git a/commons-math-examples/examples-sofm/tsp/src/main/java/org/apache/commons/math4/examples/sofm/tsp/StandAlone.java b/commons-math-examples/examples-sofm/tsp/src/main/java/org/apache/commons/math4/examples/sofm/tsp/StandAlone.java
index 6dd92fe..bb4e535 100644
--- a/commons-math-examples/examples-sofm/tsp/src/main/java/org/apache/commons/math4/examples/sofm/tsp/StandAlone.java
+++ b/commons-math-examples/examples-sofm/tsp/src/main/java/org/apache/commons/math4/examples/sofm/tsp/StandAlone.java
@@ -86,7 +86,7 @@ public final class StandAlone implements Callable<Void> {
             new City("i0", 1, 1),
         };
 
-        final UniformRandomProvider rng = RandomSource.create(RandomSource.KISS);
+        final UniformRandomProvider rng = RandomSource.KISS.create();
         City[] best = null;
         int maxCities = 0;
         double minDist = Double.POSITIVE_INFINITY;