You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ch...@apache.org on 2017/12/07 17:34:23 UTC
ignite git commit: IGNITE-6373: Create example for local and
distributed k-means algorithm
Repository: ignite
Updated Branches:
refs/heads/master 1f63b8115 -> a7893b630
IGNITE-6373: Create example for local and distributed k-means algorithm
this closes #3173
Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/a7893b63
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/a7893b63
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/a7893b63
Branch: refs/heads/master
Commit: a7893b6307bc752d7b7f1432967a964f30716858
Parents: 1f63b81
Author: Oleg Ignatenko <oi...@gridgain.com>
Authored: Thu Dec 7 20:34:14 2017 +0300
Committer: Yury Babak <yb...@gridgain.com>
Committed: Thu Dec 7 20:34:14 2017 +0300
----------------------------------------------------------------------
.../clustering/DatasetWithObviousStructure.java | 105 ++++++++++++++++++
.../KMeansDistributedClustererExample.java | 95 +++++++++++++++++
.../clustering/KMeansLocalClustererExample.java | 106 +++++++++++++++++++
.../examples/ml/clustering/package-info.java | 2 +-
...KMeansDistributedClustererTestMultiNode.java | 15 +--
...MeansDistributedClustererTestSingleNode.java | 15 +--
.../apache/ignite/ml/clustering/KMeansUtil.java | 6 +-
7 files changed, 326 insertions(+), 18 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ignite/blob/a7893b63/examples/src/main/ml/org/apache/ignite/examples/ml/clustering/DatasetWithObviousStructure.java
----------------------------------------------------------------------
diff --git a/examples/src/main/ml/org/apache/ignite/examples/ml/clustering/DatasetWithObviousStructure.java b/examples/src/main/ml/org/apache/ignite/examples/ml/clustering/DatasetWithObviousStructure.java
new file mode 100644
index 0000000..5cd0e09
--- /dev/null
+++ b/examples/src/main/ml/org/apache/ignite/examples/ml/clustering/DatasetWithObviousStructure.java
@@ -0,0 +1,105 @@
+/*
+ * 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.ignite.examples.ml.clustering;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import org.apache.ignite.ml.math.Matrix;
+import org.apache.ignite.ml.math.Vector;
+import org.apache.ignite.ml.math.VectorUtils;
+import org.apache.ignite.ml.math.impls.vector.DenseLocalOnHeapVector;
+
+/**
+ * See KMeansDistributedClustererTestSingleNode#testClusterizationOnDatasetWithObviousStructure.
+ */
+class DatasetWithObviousStructure {
+ /** */
+ private final Random rnd = new Random(123456L);
+
+ /** Let centers be in the vertices of square. */
+ private final Map<Integer, Vector> centers = new HashMap<>();
+
+ /** Square side length. */
+ private final int squareSideLen;
+
+ /** */
+ DatasetWithObviousStructure(int squareSideLen) {
+ this.squareSideLen = squareSideLen;
+ centers.put(100, new DenseLocalOnHeapVector(new double[] {0.0, 0.0}));
+ centers.put(900, new DenseLocalOnHeapVector(new double[] {squareSideLen, 0.0}));
+ centers.put(3000, new DenseLocalOnHeapVector(new double[] {0.0, squareSideLen}));
+ centers.put(6000, new DenseLocalOnHeapVector(new double[] {squareSideLen, squareSideLen}));
+ }
+
+ /** */
+ List<Vector> generate(Matrix points) {
+ int ptsCnt = points.rowSize();
+
+ // Mass centers of dataset.
+ List<Vector> massCenters = new ArrayList<>();
+
+ int centersCnt = centers.size();
+
+ List<Integer> permutation = IntStream.range(0, ptsCnt).boxed().collect(Collectors.toList());
+ Collections.shuffle(permutation, rnd);
+
+ Vector[] mc = new Vector[centersCnt];
+ Arrays.fill(mc, VectorUtils.zeroes(2));
+
+ int totalCnt = 0;
+
+ int centIdx = 0;
+ massCenters.clear();
+
+ for (Integer count : centers.keySet()) {
+ for (int i = 0; i < count; i++) {
+ Vector pnt = getPoint(count);
+
+ mc[centIdx] = mc[centIdx].plus(pnt);
+
+ points.assignRow(permutation.get(totalCnt), pnt);
+
+ totalCnt++;
+ }
+ massCenters.add(mc[centIdx].times(1 / (double)count));
+ centIdx++;
+ }
+
+ return massCenters;
+ }
+
+ /** */
+ Map<Integer, Vector> centers() {
+ return centers;
+ }
+
+ /** */
+ private Vector getPoint(Integer cnt) {
+ Vector pnt = new DenseLocalOnHeapVector(2).assign(centers.get(cnt));
+ // Perturbate point on random value.
+ pnt.map(val -> val + rnd.nextDouble() * squareSideLen / 100);
+ return pnt;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/a7893b63/examples/src/main/ml/org/apache/ignite/examples/ml/clustering/KMeansDistributedClustererExample.java
----------------------------------------------------------------------
diff --git a/examples/src/main/ml/org/apache/ignite/examples/ml/clustering/KMeansDistributedClustererExample.java b/examples/src/main/ml/org/apache/ignite/examples/ml/clustering/KMeansDistributedClustererExample.java
new file mode 100644
index 0000000..456e915
--- /dev/null
+++ b/examples/src/main/ml/org/apache/ignite/examples/ml/clustering/KMeansDistributedClustererExample.java
@@ -0,0 +1,95 @@
+/*
+ * 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.ignite.examples.ml.clustering;
+
+import java.util.Arrays;
+import java.util.List;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.Ignition;
+import org.apache.ignite.examples.ExampleNodeStartup;
+import org.apache.ignite.examples.ml.math.matrix.SparseDistributedMatrixExample;
+import org.apache.ignite.ml.clustering.KMeansDistributedClusterer;
+import org.apache.ignite.ml.math.EuclideanDistance;
+import org.apache.ignite.ml.math.StorageConstants;
+import org.apache.ignite.ml.math.Tracer;
+import org.apache.ignite.ml.math.Vector;
+import org.apache.ignite.ml.math.impls.matrix.SparseDistributedMatrix;
+import org.apache.ignite.thread.IgniteThread;
+
+/**
+ * <p>
+ * Example of using {@link KMeansDistributedClusterer}.</p>
+ * <p>
+ * Note that in this example we cannot guarantee order in which nodes return results of intermediate
+ * computations and therefore algorithm can return different results.</p>
+ * <p>
+ * Remote nodes should always be started with special configuration file which
+ * enables P2P class loading: {@code 'ignite.{sh|bat} examples/config/example-ignite.xml'}.</p>
+ * <p>
+ * Alternatively you can run {@link ExampleNodeStartup} in another JVM which will start node
+ * with {@code examples/config/example-ignite.xml} configuration.</p>
+ */
+public class KMeansDistributedClustererExample {
+ /**
+ * Executes example.
+ *
+ * @param args Command line arguments, none required.
+ */
+ public static void main(String[] args) throws InterruptedException {
+ // IMPL NOTE based on KMeansDistributedClustererTestSingleNode#testClusterizationOnDatasetWithObviousStructure
+ System.out.println(">>> K-means distributed clusterer example started.");
+ // Start ignite grid.
+ try (Ignite ignite = Ignition.start("examples/config/example-ignite.xml")) {
+ System.out.println(">>> Ignite grid started.");
+ // Create IgniteThread, we must work with SparseDistributedMatrix inside IgniteThread
+ // because we create ignite cache internally.
+ IgniteThread igniteThread = new IgniteThread(ignite.configuration().getIgniteInstanceName(),
+ SparseDistributedMatrixExample.class.getSimpleName(), () -> {
+
+ int ptsCnt = 10000;
+
+ SparseDistributedMatrix points = new SparseDistributedMatrix(ptsCnt, 2,
+ StorageConstants.ROW_STORAGE_MODE, StorageConstants.RANDOM_ACCESS_MODE);
+
+ DatasetWithObviousStructure dataset = new DatasetWithObviousStructure(10000);
+
+ List<Vector> massCenters = dataset.generate(points);
+
+ EuclideanDistance dist = new EuclideanDistance();
+
+ KMeansDistributedClusterer clusterer = new KMeansDistributedClusterer(dist, 3, 100, 1L);
+
+ Vector[] resCenters = clusterer.cluster(points, 4).centers();
+
+ System.out.println("Mass centers:");
+ massCenters.forEach(Tracer::showAscii);
+
+ System.out.println("Cluster centers:");
+ Arrays.asList(resCenters).forEach(Tracer::showAscii);
+
+ points.destroy();
+
+ System.out.println("\n>>> K-means distributed clusterer example completed.");
+ });
+
+ igniteThread.start();
+
+ igniteThread.join();
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/a7893b63/examples/src/main/ml/org/apache/ignite/examples/ml/clustering/KMeansLocalClustererExample.java
----------------------------------------------------------------------
diff --git a/examples/src/main/ml/org/apache/ignite/examples/ml/clustering/KMeansLocalClustererExample.java b/examples/src/main/ml/org/apache/ignite/examples/ml/clustering/KMeansLocalClustererExample.java
new file mode 100644
index 0000000..970931e
--- /dev/null
+++ b/examples/src/main/ml/org/apache/ignite/examples/ml/clustering/KMeansLocalClustererExample.java
@@ -0,0 +1,106 @@
+/*
+ * 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.ignite.examples.ml.clustering;
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+import org.apache.ignite.ml.clustering.KMeansLocalClusterer;
+import org.apache.ignite.ml.clustering.KMeansModel;
+import org.apache.ignite.ml.math.DistanceMeasure;
+import org.apache.ignite.ml.math.EuclideanDistance;
+import org.apache.ignite.ml.math.Tracer;
+import org.apache.ignite.ml.math.Vector;
+import org.apache.ignite.ml.math.functions.Functions;
+import org.apache.ignite.ml.math.impls.matrix.DenseLocalOnHeapMatrix;
+
+/**
+ * Example of using {@link KMeansLocalClusterer}.
+ */
+public class KMeansLocalClustererExample {
+ /**
+ * Executes example.
+ *
+ * @param args Command line arguments, none required.
+ */
+ public static void main(String[] args) {
+ // IMPL NOTE based on KMeansDistributedClustererTestSingleNode#testClusterizationOnDatasetWithObviousStructure
+ System.out.println(">>> K-means local clusterer example started.");
+
+ int ptsCnt = 10000;
+ DenseLocalOnHeapMatrix points = new DenseLocalOnHeapMatrix(ptsCnt, 2);
+
+ DatasetWithObviousStructure dataset = new DatasetWithObviousStructure(10000);
+
+ List<Vector> massCenters = dataset.generate(points);
+
+ EuclideanDistance dist = new EuclideanDistance();
+ OrderedNodesComparator comp = new OrderedNodesComparator(
+ dataset.centers().values().toArray(new Vector[] {}), dist);
+
+ massCenters.sort(comp);
+
+ KMeansLocalClusterer clusterer = new KMeansLocalClusterer(dist, 100, 1L);
+
+ KMeansModel mdl = clusterer.cluster(points, 4);
+ Vector[] resCenters = mdl.centers();
+ Arrays.sort(resCenters, comp);
+
+ System.out.println("Mass centers:");
+ massCenters.forEach(Tracer::showAscii);
+
+ System.out.println("Cluster centers:");
+ Arrays.asList(resCenters).forEach(Tracer::showAscii);
+
+ System.out.println("\n>>> K-means local clusterer example completed.");
+ }
+
+ /** */
+ private static class OrderedNodesComparator implements Comparator<Vector> {
+ /** */
+ private final DistanceMeasure measure;
+
+ /** */
+ List<Vector> orderedNodes;
+
+ /** */
+ OrderedNodesComparator(Vector[] orderedNodes, DistanceMeasure measure) {
+ this.orderedNodes = Arrays.asList(orderedNodes);
+ this.measure = measure;
+ }
+
+ /** */
+ private int findClosestNodeIndex(Vector v) {
+ return Functions.argmin(orderedNodes, v1 -> measure.compute(v1, v)).get1();
+ }
+
+ /** */
+ @Override public int compare(Vector v1, Vector v2) {
+ int ind1 = findClosestNodeIndex(v1);
+ int ind2 = findClosestNodeIndex(v2);
+
+ int signum = (int)Math.signum(ind1 - ind2);
+
+ if (signum != 0)
+ return signum;
+
+ return (int)Math.signum(orderedNodes.get(ind1).minus(v1).kNorm(2) -
+ orderedNodes.get(ind2).minus(v2).kNorm(2));
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/a7893b63/examples/src/main/ml/org/apache/ignite/examples/ml/clustering/package-info.java
----------------------------------------------------------------------
diff --git a/examples/src/main/ml/org/apache/ignite/examples/ml/clustering/package-info.java b/examples/src/main/ml/org/apache/ignite/examples/ml/clustering/package-info.java
index 7051912..872aa16 100644
--- a/examples/src/main/ml/org/apache/ignite/examples/ml/clustering/package-info.java
+++ b/examples/src/main/ml/org/apache/ignite/examples/ml/clustering/package-info.java
@@ -17,6 +17,6 @@
/**
* <!-- Package description. -->
- * Clustering examples.
+ * ML clustering examples.
*/
package org.apache.ignite.examples.ml.clustering;
http://git-wip-us.apache.org/repos/asf/ignite/blob/a7893b63/modules/ml/src/test/java/org/apache/ignite/ml/clustering/KMeansDistributedClustererTestMultiNode.java
----------------------------------------------------------------------
diff --git a/modules/ml/src/test/java/org/apache/ignite/ml/clustering/KMeansDistributedClustererTestMultiNode.java b/modules/ml/src/test/java/org/apache/ignite/ml/clustering/KMeansDistributedClustererTestMultiNode.java
index 2722d74..1f71dee 100644
--- a/modules/ml/src/test/java/org/apache/ignite/ml/clustering/KMeansDistributedClustererTestMultiNode.java
+++ b/modules/ml/src/test/java/org/apache/ignite/ml/clustering/KMeansDistributedClustererTestMultiNode.java
@@ -33,12 +33,11 @@ import org.apache.ignite.ml.math.Vector;
import org.apache.ignite.ml.math.impls.matrix.SparseDistributedMatrix;
import org.apache.ignite.ml.math.impls.vector.DenseLocalOnHeapVector;
import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
-import org.junit.Test;
/**
- * This test is made to make sure that K-Means distributed clustering does not crush on distributed environment.
- * In {@link KMeansDistributedClustererTestMultiNode} we check logic of clustering (checks for clusters structures).
- * In this class we just check that clusterer does not crush. There are two separate tests because we cannot
+ * This test is made to make sure that K-Means distributed clustering does not crash on distributed environment.
+ * In {@link KMeansDistributedClustererTestSingleNode} we check logic of clustering (checks for clusters structures).
+ * In this class we just check that clusterer does not crash. There are two separate tests because we cannot
* guarantee order in which nodes return results of intermediate computations and therefore algorithm can return
* different results.
*/
@@ -75,7 +74,6 @@ public class KMeansDistributedClustererTestMultiNode extends GridCommonAbstractT
}
/** */
- @Test
public void testPerformClusterAnalysisDegenerate() {
IgniteUtils.setCurrentIgniteName(ignite.configuration().getIgniteInstanceName());
@@ -91,10 +89,11 @@ public class KMeansDistributedClustererTestMultiNode extends GridCommonAbstractT
points.setRow(1, v2);
clusterer.cluster(points, 1);
+
+ points.destroy();
}
/** */
- @Test
public void testClusterizationOnDatasetWithObviousStructure() throws IOException {
IgniteUtils.setCurrentIgniteName(ignite.configuration().getIgniteInstanceName());
@@ -120,7 +119,7 @@ public class KMeansDistributedClustererTestMultiNode extends GridCommonAbstractT
for (Integer count : centers.keySet()) {
for (int i = 0; i < count; i++) {
- DenseLocalOnHeapVector pnt = (DenseLocalOnHeapVector)new DenseLocalOnHeapVector(2).assign(centers.get(count));
+ Vector pnt = new DenseLocalOnHeapVector(2).assign(centers.get(count));
// Perturbate point on random value.
pnt.map(val -> val + rnd.nextDouble() * squareSideLen / 100);
points.assignRow(permutation.get(totalCnt), pnt);
@@ -133,5 +132,7 @@ public class KMeansDistributedClustererTestMultiNode extends GridCommonAbstractT
KMeansDistributedClusterer clusterer = new KMeansDistributedClusterer(dist, 3, 100, 1L);
clusterer.cluster(points, 4);
+
+ points.destroy();
}
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/a7893b63/modules/ml/src/test/java/org/apache/ignite/ml/clustering/KMeansDistributedClustererTestSingleNode.java
----------------------------------------------------------------------
diff --git a/modules/ml/src/test/java/org/apache/ignite/ml/clustering/KMeansDistributedClustererTestSingleNode.java b/modules/ml/src/test/java/org/apache/ignite/ml/clustering/KMeansDistributedClustererTestSingleNode.java
index 27aaa0c..19c328a 100644
--- a/modules/ml/src/test/java/org/apache/ignite/ml/clustering/KMeansDistributedClustererTestSingleNode.java
+++ b/modules/ml/src/test/java/org/apache/ignite/ml/clustering/KMeansDistributedClustererTestSingleNode.java
@@ -40,11 +40,12 @@ import org.apache.ignite.ml.math.impls.matrix.SparseDistributedMatrix;
import org.apache.ignite.ml.math.impls.vector.DenseLocalOnHeapVector;
import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
import org.junit.Assert;
-import org.junit.Test;
import static org.apache.ignite.ml.clustering.KMeansUtil.checkIsInEpsilonNeighbourhood;
-/** */
+/**
+ * This test checks logic of clustering (checks for clusters structures).
+ */
public class KMeansDistributedClustererTestSingleNode extends GridCommonAbstractTest {
/**
* Number of nodes in grid. We should use 1 in this test because otherwise algorithm will be unstable
@@ -81,7 +82,6 @@ public class KMeansDistributedClustererTestSingleNode extends GridCommonAbstract
}
/** */
- @Test
public void testPerformClusterAnalysisDegenerate() {
IgniteUtils.setCurrentIgniteName(ignite.configuration().getIgniteInstanceName());
@@ -103,7 +103,6 @@ public class KMeansDistributedClustererTestSingleNode extends GridCommonAbstract
}
/** */
- @Test
public void testClusterizationOnDatasetWithObviousStructure() throws IOException {
IgniteUtils.setCurrentIgniteName(ignite.configuration().getIgniteInstanceName());
@@ -137,7 +136,7 @@ public class KMeansDistributedClustererTestSingleNode extends GridCommonAbstract
for (Integer count : centers.keySet()) {
for (int i = 0; i < count; i++) {
- DenseLocalOnHeapVector pnt = (DenseLocalOnHeapVector)new DenseLocalOnHeapVector(2).assign(centers.get(count));
+ Vector pnt = new DenseLocalOnHeapVector(2).assign(centers.get(count));
// Perturbate point on random value.
pnt.map(val -> val + rnd.nextDouble() * squareSideLen / 100);
mc[centIdx] = mc[centIdx].plus(pnt);
@@ -159,6 +158,8 @@ public class KMeansDistributedClustererTestSingleNode extends GridCommonAbstract
Arrays.sort(resCenters, comp);
checkIsInEpsilonNeighbourhood(resCenters, massCenters.toArray(new Vector[] {}), 30.0);
+
+ points.destroy();
}
/** */
@@ -170,7 +171,7 @@ public class KMeansDistributedClustererTestSingleNode extends GridCommonAbstract
List<Vector> orderedNodes;
/** */
- public OrderedNodesComparator(Vector[] orderedNodes, DistanceMeasure measure) {
+ OrderedNodesComparator(Vector[] orderedNodes, DistanceMeasure measure) {
this.orderedNodes = Arrays.asList(orderedNodes);
this.measure = measure;
}
@@ -194,4 +195,4 @@ public class KMeansDistributedClustererTestSingleNode extends GridCommonAbstract
orderedNodes.get(ind2).minus(v2).kNorm(2));
}
}
-}
\ No newline at end of file
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/a7893b63/modules/ml/src/test/java/org/apache/ignite/ml/clustering/KMeansUtil.java
----------------------------------------------------------------------
diff --git a/modules/ml/src/test/java/org/apache/ignite/ml/clustering/KMeansUtil.java b/modules/ml/src/test/java/org/apache/ignite/ml/clustering/KMeansUtil.java
index 0a39748..420678f 100644
--- a/modules/ml/src/test/java/org/apache/ignite/ml/clustering/KMeansUtil.java
+++ b/modules/ml/src/test/java/org/apache/ignite/ml/clustering/KMeansUtil.java
@@ -21,10 +21,10 @@ import org.apache.ignite.ml.math.Vector;
import static org.junit.Assert.assertTrue;
-/** Base test for k-means algorithms. */
-public class KMeansUtil {
+/** Utilities for k-means tests. */
+class KMeansUtil {
/** */
- public static void checkIsInEpsilonNeighbourhood(Vector[] v1s, Vector[] v2s, double epsilon) {
+ static void checkIsInEpsilonNeighbourhood(Vector[] v1s, Vector[] v2s, double epsilon) {
for (int i = 0; i < v1s.length; i++) {
assertTrue("Not in epsilon neighbourhood (index " + i + ") ",
v1s[i].minus(v2s[i]).kNorm(2) < epsilon);