Skip to content

Commit

Permalink
[NOID] Remove commons math3
Browse files Browse the repository at this point in the history
We only used a few methods from the library. Also, the FastMath that the library is built on claims to be faster and more accurate, but this is a lie in modern versions of Java. Performance will actually be worse since no intrinsics will be used.
  • Loading branch information
klaren authored Feb 26, 2024
1 parent dc9369f commit 8bf2c93
Show file tree
Hide file tree
Showing 9 changed files with 325 additions and 26 deletions.
2 changes: 1 addition & 1 deletion LICENSES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ Apache-2.0
commons-io-2.15.1.jar
commons-lang3-3.14.0.jar
commons-logging-1.2.jar
commons-math3-3.6.1.jar
commons-math3-3.1.1.jar
commons-net-3.9.0.jar
commons-text-1.10.0.jar
commons-text-1.11.0.jar
Expand Down
2 changes: 1 addition & 1 deletion NOTICE.txt
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ Apache-2.0
commons-io-2.15.1.jar
commons-lang3-3.14.0.jar
commons-logging-1.2.jar
commons-math3-3.6.1.jar
commons-math3-3.1.1.jar
commons-net-3.9.0.jar
commons-text-1.10.0.jar
commons-text-1.11.0.jar
Expand Down
1 change: 0 additions & 1 deletion common/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ dependencies {
strictly '3.14.0'
}
}
api group: 'org.apache.commons', name: 'commons-math3', version: '3.6.1'
api group: 'com.github.seancfoley', name: 'ipaddress', version: '5.3.3'

// These will be dependencies not packaged with the .jar
Expand Down
6 changes: 2 additions & 4 deletions core/src/main/java/apoc/coll/Coll.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,6 @@
import java.util.stream.Stream;
import org.apache.commons.lang3.mutable.MutableInt;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.math3.stat.descriptive.moment.StandardDeviation;
import org.apache.commons.math3.util.Combinations;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Path;
import org.neo4j.graphdb.Relationship;
Expand All @@ -78,8 +76,8 @@ public Number stdev(
@Name("list") List<Number> list,
@Name(value = "isBiasCorrected", defaultValue = "true") boolean isBiasCorrected) {
if (list == null || list.isEmpty()) return null;
final double stdev = new StandardDeviation(isBiasCorrected)
.evaluate(list.stream().mapToDouble(Number::doubleValue).toArray());
final double stdev = StandardDeviation.stdDev(
list.stream().mapToDouble(Number::doubleValue).toArray(), isBiasCorrected);
if ((long) stdev == stdev) return (long) stdev;
return stdev;
}
Expand Down
165 changes: 165 additions & 0 deletions core/src/main/java/apoc/coll/Combinations.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
/*
* Copyright (c) "Neo4j"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* 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.
*/
package apoc.coll;

import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.stream.IntStream;

/**
* This code was copied from project commons-math3
*/
class Combinations implements Iterable<int[]> {
private final int n;
private final int k;

public Combinations(int n, int k) {
checkBinomial(n, k);
this.n = n;
this.k = k;
}

@Override
public Iterator<int[]> iterator() {
if (k == 0 || k == n) {
return new SingletonIterator(IntStream.range(0, k).toArray());
}
return new LexicographicIterator(n, k);
}

private static class LexicographicIterator implements Iterator<int[]> {
private final int k;
private final int[] c;
private boolean more = true;
private int j;

LexicographicIterator(int n, int k) {
this.k = k;
c = new int[k + 3];
if (k == 0 || k >= n) {
more = false;
return;
}
// Initialize c to start with lexicographically first k-set
for (int i = 1; i <= k; i++) {
c[i] = i - 1;
}
// Initialize sentinels
c[k + 1] = n;
c[k + 2] = 0;
j = k; // Set up invariant: j is smallest index such that c[j + 1] > j
}

@Override
public boolean hasNext() {
return more;
}

@Override
public int[] next() {
if (!more) {
throw new NoSuchElementException();
}
// Copy return value (prepared by last activation)
final int[] ret = new int[k];
System.arraycopy(c, 1, ret, 0, k);

// Prepare next iteration
// T2 and T6 loop
int x = 0;
if (j > 0) {
x = j;
c[j] = x;
j--;
return ret;
}
// T3
if (c[1] + 1 < c[2]) {
c[1]++;
return ret;
} else {
j = 2;
}
// T4
boolean stepDone = false;
while (!stepDone) {
c[j - 1] = j - 2;
x = c[j] + 1;
if (x == c[j + 1]) {
j++;
} else {
stepDone = true;
}
}
// T5
if (j > k) {
more = false;
return ret;
}
// T6
c[j] = x;
j--;
return ret;
}

@Override
public void remove() {
throw new UnsupportedOperationException();
}
}

private static class SingletonIterator implements Iterator<int[]> {
private final int[] singleton;
private boolean more = true;

SingletonIterator(final int[] singleton) {
this.singleton = singleton;
}

@Override
public boolean hasNext() {
return more;
}

@Override
public int[] next() {
if (more) {
more = false;
return singleton;
} else {
throw new NoSuchElementException();
}
}

@Override
public void remove() {
throw new UnsupportedOperationException();
}
}

private static void checkBinomial(final int n, final int k) {
if (n < k) {
throw new IllegalArgumentException(
"must have n >= k for binomial coefficient (n, k), got k = %d, n = %d".formatted(k, n));
}
if (n < 0) {
throw new IllegalArgumentException("must have n >= 0 for binomial coefficient (n, k), got n = " + n);
}
}
}
74 changes: 74 additions & 0 deletions core/src/main/java/apoc/coll/StandardDeviation.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright (c) "Neo4j"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* 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.
*/
package apoc.coll;

/**
* This code was copied from project commons-math3
*/
final class StandardDeviation {

private StandardDeviation() {}

public static double stdDev(double[] values, boolean isBiasCorrected) {
return Math.sqrt(variance(values, isBiasCorrected));
}

private static double variance(double[] values, boolean isBiasCorrected) {
double variance = Double.NaN;

int length = values.length;
if (length == 1) {
variance = 0.0;
} else if (length > 1) {
double m = meanValue(values, length);
double accum = 0.0;
double dev;
double accum2 = 0.0;
for (double value : values) {
dev = value - m;
accum += dev * dev;
accum2 += dev;
}
double len = length;
if (isBiasCorrected) {
variance = (accum - (accum2 * accum2 / len)) / (len - 1.0);
} else {
variance = (accum - (accum2 * accum2 / len)) / len;
}
}

return variance;
}

private static double meanValue(double[] values, int length) {
// Compute initial estimate using definitional formula
double sum = 0.0;
for (double i : values) {
sum += i;
}
double xbar = sum / length;

// Compute correction factor in second pass
double correction = 0;
for (double value : values) {
correction += value - xbar;
}
return xbar + (correction / length);
}
}
7 changes: 3 additions & 4 deletions core/src/main/java/apoc/math/Regression.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
package apoc.math;

import java.util.stream.Stream;
import org.apache.commons.math3.stat.regression.SimpleRegression;
import org.neo4j.graphdb.Label;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.ResourceIterator;
Expand Down Expand Up @@ -53,14 +52,14 @@ public Output(double r2, double avgX, double avgY, double slope) {
"Returns the coefficient of determination (R-squared) for the values of propertyY and propertyX in the given label.")
public Stream<Output> regr(@Name("label") String label, @Name("propertyY") String y, @Name("propertyX") String x) {

SimpleRegression regr = new SimpleRegression(false);
SimpleRegression regr = new SimpleRegression();
double regrAvgX = 0;
double regrAvgY = 0;
int count = 0;

try (ResourceIterator it = tx.findNodes(Label.label(label))) {
try (ResourceIterator<Node> it = tx.findNodes(Label.label(label))) {
while (it.hasNext()) {
Node node = (Node) it.next();
Node node = it.next();
Number propX = (Number) node.getProperty(x, null);
Number propY = (Number) node.getProperty(y, null);
if (propX != null && propY != null) {
Expand Down
71 changes: 71 additions & 0 deletions core/src/main/java/apoc/math/SimpleRegression.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright (c) "Neo4j"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* 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.
*/
package apoc.math;

/**
* This code was copied from project commons-math3
*/
class SimpleRegression {
private double sumXX = 0d;
private double sumYY = 0d;
private double sumXY = 0d;
private long n = 0;

public void addData(final double x, final double y) {
sumXX += x * x;
sumYY += y * y;
sumXY += x * y;
n++;
}

public double getSlope() {
if (n < 2) {
return Double.NaN; // not enough data
}
if (Math.abs(sumXX) < 10 * Double.MIN_VALUE) {
return Double.NaN; // not enough variation in x
}
return sumXY / sumXX;
}

public double getSumSquaredErrors() {
return Math.max(0d, sumYY - sumXY * sumXY / sumXX);
}

public double getTotalSumSquares() {
if (n < 2) {
return Double.NaN;
}
return sumYY;
}

public double getR() {
double b1 = getSlope();
double result = Math.sqrt(getRSquare());
if (b1 < 0) {
result = -result;
}
return result;
}

public double getRSquare() {
double ssto = getTotalSumSquares();
return (ssto - getSumSquaredErrors()) / ssto;
}
}
Loading

0 comments on commit 8bf2c93

Please sign in to comment.