mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-10-17 01:31:01 +00:00
Added symbolic inversion for cubic least squares
This commit is contained in:
1 parent
4a030a33d3
commit
46cd534676
2 files changed
+162
-5
No files matched your search
+60
-1
@@ -87,7 +87,7 @@ public class IncrementalLeastSquaresTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testIncrementalRegressionWithRealDataNoIntercept() throws NotEnoughDataHasBeenAddedException {
|
public void testIncrementalRegressionWithRealDataNoInterceptWithSymbollicInversion() throws NotEnoughDataHasBeenAddedException {
|
||||||
|
|
||||||
IncrementalLeastSquares leastSquares = new IncrementalAnyOrderLeastSquaresImpl(3, false);
|
IncrementalLeastSquares leastSquares = new IncrementalAnyOrderLeastSquaresImpl(3, false);
|
||||||
fillRegression(leastSquares);
|
fillRegression(leastSquares);
|
||||||
@@ -97,6 +97,65 @@ public class IncrementalLeastSquaresTest {
|
|||||||
assertThat(coeffs[1], closeTo(0.9317759507, ERROR));
|
assertThat(coeffs[1], closeTo(0.9317759507, ERROR));
|
||||||
assertThat(coeffs[2], closeTo(-0.0364029315, ERROR));
|
assertThat(coeffs[2], closeTo(-0.0364029315, ERROR));
|
||||||
assertThat(coeffs[3], closeTo(0.0005326266, ERROR));
|
assertThat(coeffs[3], closeTo(0.0005326266, ERROR));
|
||||||
|
|
||||||
|
leastSquares.addData(0, 0);
|
||||||
|
long startTime = System.nanoTime();
|
||||||
|
leastSquares.getOrCreatePolynomialFunction().getCoefficients();
|
||||||
|
System.out.println("With symbollic inversion no intercept" + (System.nanoTime() - startTime) + "ns");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIncrementalRegressionWithRealDataNoInterceptWithoutSymbollicInversion() throws NotEnoughDataHasBeenAddedException {
|
||||||
|
|
||||||
|
IncrementalLeastSquares leastSquares = new IncrementalAnyOrderLeastSquaresImpl(3, false, false);
|
||||||
|
fillRegression(leastSquares);
|
||||||
|
double[] coeffs = leastSquares.getOrCreatePolynomialFunction().getCoefficients();
|
||||||
|
|
||||||
|
assertThat(coeffs[0], closeTo(0, ERROR));
|
||||||
|
assertThat(coeffs[1], closeTo(0.9317759507, ERROR));
|
||||||
|
assertThat(coeffs[2], closeTo(-0.0364029315, ERROR));
|
||||||
|
assertThat(coeffs[3], closeTo(0.0005326266, ERROR));
|
||||||
|
|
||||||
|
leastSquares.addData(0, 0);
|
||||||
|
long startTime = System.nanoTime();
|
||||||
|
leastSquares.getOrCreatePolynomialFunction().getCoefficients();
|
||||||
|
System.out.println("With numeric inversion no intercept " + (System.nanoTime() - startTime) + "ns");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIncrementalRegressionWithRealDataWithInterceptWithSymbollicInversion() throws NotEnoughDataHasBeenAddedException {
|
||||||
|
|
||||||
|
IncrementalLeastSquares leastSquares = new IncrementalAnyOrderLeastSquaresImpl(3, true, true);
|
||||||
|
fillRegression(leastSquares);
|
||||||
|
double[] coeffs = leastSquares.getOrCreatePolynomialFunction().getCoefficients();
|
||||||
|
|
||||||
|
assertThat(coeffs[0], closeTo(6.319899225, ERROR));
|
||||||
|
assertThat(coeffs[1], closeTo(-0.427890096, ERROR));
|
||||||
|
assertThat(coeffs[2], closeTo(0.057763706, ERROR));
|
||||||
|
assertThat(coeffs[3], closeTo(-0.001574209, ERROR));
|
||||||
|
|
||||||
|
leastSquares.addData(0, 0);
|
||||||
|
long startTime = System.nanoTime();
|
||||||
|
leastSquares.getOrCreatePolynomialFunction().getCoefficients();
|
||||||
|
System.out.println("With symbollic inversion and intercept " + (System.nanoTime() - startTime) + "ns");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIncrementalRegressionWithRealDataWithInterceptWithoutSymbollicInversion() throws NotEnoughDataHasBeenAddedException {
|
||||||
|
|
||||||
|
IncrementalLeastSquares leastSquares = new IncrementalAnyOrderLeastSquaresImpl(3, true, false);
|
||||||
|
fillRegression(leastSquares);
|
||||||
|
double[] coeffs = leastSquares.getOrCreatePolynomialFunction().getCoefficients();
|
||||||
|
|
||||||
|
assertThat(coeffs[0], closeTo(6.319899225, ERROR));
|
||||||
|
assertThat(coeffs[1], closeTo(-0.427890096, ERROR));
|
||||||
|
assertThat(coeffs[2], closeTo(0.057763706, ERROR));
|
||||||
|
assertThat(coeffs[3], closeTo(-0.001574209, ERROR));
|
||||||
|
|
||||||
|
leastSquares.addData(0, 0);
|
||||||
|
long startTime = System.nanoTime();
|
||||||
|
leastSquares.getOrCreatePolynomialFunction().getCoefficients();
|
||||||
|
System.out.println("With numeric inversion and intercept " + (System.nanoTime() - startTime) + "ns");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fillRegression(IncrementalLeastSquares leastSquares) {
|
private void fillRegression(IncrementalLeastSquares leastSquares) {
|
||||||
|
|||||||
+102
-4
@@ -25,7 +25,7 @@ import com.sap.sse.concurrent.NamedReentrantReadWriteLock;
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class IncrementalAnyOrderLeastSquaresImpl implements IncrementalLeastSquares {
|
public class IncrementalAnyOrderLeastSquaresImpl implements IncrementalLeastSquares {
|
||||||
|
|
||||||
private double[][] matrixOfXSums;
|
private double[][] matrixOfXSums;
|
||||||
|
|
||||||
private double[] vectorOfXYMultSums;
|
private double[] vectorOfXYMultSums;
|
||||||
@@ -45,10 +45,12 @@ public class IncrementalAnyOrderLeastSquaresImpl implements IncrementalLeastSqua
|
|||||||
private PolynomialFunction cachedFunction;
|
private PolynomialFunction cachedFunction;
|
||||||
|
|
||||||
private final boolean hasIntercept;
|
private final boolean hasIntercept;
|
||||||
|
private final boolean useSymbollicInversionIfPossible;
|
||||||
|
|
||||||
public IncrementalAnyOrderLeastSquaresImpl(int polynomialOrder, boolean hasIntercept) {
|
public IncrementalAnyOrderLeastSquaresImpl(int polynomialOrder, boolean hasIntercept, boolean useSymbollicInversionIfPossible) {
|
||||||
this.hasIntercept = hasIntercept;
|
this.hasIntercept = hasIntercept;
|
||||||
this.polynomialOrder = polynomialOrder;
|
this.polynomialOrder = polynomialOrder;
|
||||||
|
this.useSymbollicInversionIfPossible = useSymbollicInversionIfPossible;
|
||||||
if (hasIntercept) {
|
if (hasIntercept) {
|
||||||
matrixOfXSums = new double[polynomialOrder + 1][polynomialOrder + 1];
|
matrixOfXSums = new double[polynomialOrder + 1][polynomialOrder + 1];
|
||||||
vectorOfXYMultSums = new double[polynomialOrder + 1];
|
vectorOfXYMultSums = new double[polynomialOrder + 1];
|
||||||
@@ -59,7 +61,11 @@ public class IncrementalAnyOrderLeastSquaresImpl implements IncrementalLeastSqua
|
|||||||
}
|
}
|
||||||
|
|
||||||
public IncrementalAnyOrderLeastSquaresImpl(int polynomialOrder) {
|
public IncrementalAnyOrderLeastSquaresImpl(int polynomialOrder) {
|
||||||
this(polynomialOrder, true);
|
this(polynomialOrder, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IncrementalAnyOrderLeastSquaresImpl(int polynomialOrder, boolean hasIntercept) {
|
||||||
|
this(polynomialOrder, hasIntercept, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -127,7 +133,14 @@ public class IncrementalAnyOrderLeastSquaresImpl implements IncrementalLeastSqua
|
|||||||
} finally {
|
} finally {
|
||||||
LockUtil.unlockAfterRead(lock);
|
LockUtil.unlockAfterRead(lock);
|
||||||
}
|
}
|
||||||
RealMatrix inversedMatrix = new LUDecompositionImpl(matrixOfXSumsCopy).getSolver().getInverse();
|
LeastSquaresInversionHelperType type = LeastSquaresInversionHelperType.getType(polynomialOrder, hasIntercept);
|
||||||
|
RealMatrix inversedMatrix = null;
|
||||||
|
if (useSymbollicInversionIfPossible && type != null) {
|
||||||
|
inversedMatrix = type.inverseMatrix(matrixOfXSumsCopy);
|
||||||
|
}
|
||||||
|
if (inversedMatrix == null) {
|
||||||
|
inversedMatrix = new LUDecompositionImpl(matrixOfXSumsCopy).getSolver().getInverse();
|
||||||
|
}
|
||||||
RealVector coeffs = inversedMatrix.operate(vectorOfXYMultSumsCopy);
|
RealVector coeffs = inversedMatrix.operate(vectorOfXYMultSumsCopy);
|
||||||
if (!hasIntercept) {
|
if (!hasIntercept) {
|
||||||
resultFunction = createFunctionWithZeroIntercept(coeffs);
|
resultFunction = createFunctionWithZeroIntercept(coeffs);
|
||||||
@@ -159,5 +172,90 @@ public class IncrementalAnyOrderLeastSquaresImpl implements IncrementalLeastSqua
|
|||||||
}
|
}
|
||||||
return new PolynomialFunction(coeff);
|
return new PolynomialFunction(coeff);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private enum LeastSquaresInversionHelperType {
|
||||||
|
|
||||||
|
CUBIC,
|
||||||
|
CUBIC_NO_INTERCEPT;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param realMatrix matrix to inverse
|
||||||
|
* @return Inversed matrix if symbolic inversion was found. Else null
|
||||||
|
*/
|
||||||
|
private RealMatrix inverseMatrix(RealMatrix realMatrix) {
|
||||||
|
RealMatrix result = null;
|
||||||
|
switch (this) {
|
||||||
|
case CUBIC_NO_INTERCEPT:
|
||||||
|
double[][] originDataCNI = realMatrix.getData();
|
||||||
|
double a = originDataCNI[0][0];
|
||||||
|
double b = originDataCNI[0][1];
|
||||||
|
double c = originDataCNI[0][2];
|
||||||
|
double d = originDataCNI[1][2];
|
||||||
|
double e = originDataCNI[2][2];
|
||||||
|
double[][] matrixDataCNI = { { d * d - c * e, b * e - c * d, c * c - b * d },
|
||||||
|
{ b * e - c * d, c * c - a * e, a * d - b * c },
|
||||||
|
{ c * c - b * d, a * d - b * c, b * b - a * c } };
|
||||||
|
RealMatrix intermediateMatrixCNI = MatrixUtils.createRealMatrix(matrixDataCNI);
|
||||||
|
double factorCNI = (1.0 / (-a*c*e + a*d*d + b*b*e-2*b*c*d+c*c*c));
|
||||||
|
result = intermediateMatrixCNI.scalarMultiply(factorCNI);
|
||||||
|
break;
|
||||||
|
case CUBIC:
|
||||||
|
double[][] originDataC = realMatrix.getData();
|
||||||
|
double n = originDataC[0][0];
|
||||||
|
a = originDataC[0][1];
|
||||||
|
b = originDataC[0][2];
|
||||||
|
c = originDataC[0][3];
|
||||||
|
d = originDataC[1][3];
|
||||||
|
e = originDataC[2][3];
|
||||||
|
double f = originDataC[3][3];
|
||||||
|
double[][] matrixDataC = {
|
||||||
|
{ -d * d * d + 2 * c * e * d + b * f * d - b * e * e - c * c * f,
|
||||||
|
-e * c * c + d * d * c + b * f * c + a * e * e - b * d * e - a * d * f,
|
||||||
|
-f * b * b + d * d * b + c * e * b - c * c * d - a * d * e + a * c * f,
|
||||||
|
c * c * c - 2 * b * d * c - a * e * c + a * d * d + b * b * e },
|
||||||
|
{ -e * c * c + d * d * c + b * f * c + a * e * e - b * d * e - a * d * f,
|
||||||
|
-f * b * b + 2 * c * e * b - c * c * d - e * e * n + d * f * n,
|
||||||
|
c * c * c - b * d * c - a * e * c - f * n * c + a * b * f + d * e * n,
|
||||||
|
d * b * b - c * c * b - a * e * b + a * c * d - d * d * n + c * e * n },
|
||||||
|
{ -f * b * b + d * d * b + c * e * b - c * c * d - a * d * e + a * c * f,
|
||||||
|
c * c * c - b * d * c - a * e * c - f * n * c + a * b * f + d * e * n,
|
||||||
|
-f * a * a + 2 * c * d * a - b * c * c - d * d * n + b * f * n,
|
||||||
|
e * a * a - c * c * a - b * d * a + b * b * c + c * d * n - b * e * n },
|
||||||
|
{ c * c * c - 2 * b * d * c - a * e * c + a * d * d + b * b * e,
|
||||||
|
d * b * b - c * c * b - a * e * b + a * c * d - d * d * n + c * e * n,
|
||||||
|
e * a * a - c * c * a - b * d * a + b * b * c + c * d * n - b * e * n,
|
||||||
|
-b * b * b + 2 * a * c * b + d * n * b - a * a * d - c * c * n } };
|
||||||
|
RealMatrix intermediateMatrixC = MatrixUtils.createRealMatrix(matrixDataC);
|
||||||
|
double factorC = 1.0 / (-a * a * d * f + a * a * e * e + 2 * c
|
||||||
|
* (a * (b * f + d * d) + e * (b * b + d * n)) - c * c * (2 * a * e + 3 * b * d + f * n) + b
|
||||||
|
* (-2 * a * d * e + d * f * n + e * e * -n) - b * b * b * f + b * b * d * d + c * c * c * c - d
|
||||||
|
* d * d * n);
|
||||||
|
result = intermediateMatrixC.scalarMultiply(factorC);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param order Order of the polynomial
|
||||||
|
* @param hasIntercept If the least squares is set to no intercept
|
||||||
|
* @return type of the polynomial or null if it doesn't exist in this implementation
|
||||||
|
*/
|
||||||
|
private static LeastSquaresInversionHelperType getType(int order, boolean hasIntercept) {
|
||||||
|
LeastSquaresInversionHelperType result = null;
|
||||||
|
switch(order) {
|
||||||
|
case 3:
|
||||||
|
result = hasIntercept ? CUBIC : CUBIC_NO_INTERCEPT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user