├── .gitignore ├── LICENSE ├── README.md ├── pom.xml └── src ├── main └── java │ └── org │ └── orangepalantir │ ├── filters │ └── SavitzyGolayFilter.java │ └── leastsquares │ ├── Fitter.java │ ├── Function.java │ ├── examples │ └── ParabolaExample.java │ ├── fitters │ ├── LinearFitter.java │ ├── MarquardtFitter.java │ └── NonLinearSolver.java │ └── functions │ ├── ExponentialFunction.java │ └── LinearFunction.java └── test └── java └── org └── orangepalantir └── leastsquares └── fitters ├── LinearFitterTest.java ├── NonLinearSolverTest.java └── NonLinearTrigonometricSolver.java /.gitignore: -------------------------------------------------------------------------------- 1 | # IntelliJ 2 | .idea/ 3 | *.iws 4 | *.iml 5 | *.ipr 6 | *.ips 7 | .idea/ 8 | 9 | 10 | # Eclipse 11 | .settings/ 12 | .metadata/ 13 | .classpath 14 | .project 15 | 16 | # Generated 17 | target/ 18 | javadoc/ 19 | bin/ 20 | log/ 21 | 22 | # Misc. 23 | .DS_Store 24 | *.bak 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 odinsbane 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | least-squares-in-java 2 | ===================== 3 | 4 | Java Least Squares fitting library. 5 | ----------------------------------- 6 | 7 | This is a small least squares fitting library made in java. 8 | It was originally used in the development of an image analysis tool 9 | SpeckleTrackerJ. http://athena.physics.lehigh.edu/speckletrackerj/ It uses 10 | methods described in "Numerical Recipes in C", Second Edition (1992). 11 | 12 | The outline of using this library is you have a set of data that you 13 | would like fit a function to. Define the Function, how it evaluates 14 | the input and paramters. Create a Fitter (three types currently) initialized 15 | to the function. Set the fitter with the data and make an initial guess of the 16 | parameters. Tell the fitter to find the best set of parameters which minimizes the 17 | sum of squares of error. 18 | 19 | Example 20 | ------- 21 | 22 | Lets say we have some data. 23 | 24 | double[][] xs = { 25 | {0}, {1}, {2}, {3}, {4}, {5} 26 | }; 27 | double[] z = {1.1, 0.9, 1.0, 1.35, 1.82, 2.5}; 28 | 29 | And we want to fit a quadratic function f(x) = A*x*x + B*x + C. The parameters are A, B, C and the 30 | input is x. So we have 3 parameters and 1 input. 31 | 32 | Function fun = new Function(){ 33 | @Override 34 | public double evaluate(double[] values, double[] parameters) { 35 | double A = parameters[0]; 36 | double B = parameters[1]; 37 | double C = parameters[2]; 38 | double x = values[0]; 39 | return A*x*x + B*x + C; 40 | } 41 | @Override 42 | public int getNParameters() { 43 | return 3; 44 | } 45 | 46 | @Override 47 | public int getNInputs() { 48 | return 1; 49 | } 50 | }; 51 | 52 | The next step is to create a fitter. 53 | 54 | Fitter fit = new LinearFitter(fun); 55 | 56 | Set the data, and make a guess at the initial parameters. 57 | 58 | fit.setData(xs, zs); 59 | fit.setParameters(new double[]{0,0,0}); 60 | 61 | fit.fitData(); 62 | 63 | The results can be obtained by using getParameters. 64 | 65 | System.out.println(Arrays.toString(fit.getParameters())); 66 | #[0.10000000000000023, -0.20000000000000123, 1.000000000000001] 67 | 68 | This example is included in the examples package. 69 | 70 | 71 | Fitter implementations 72 | ---------------------- 73 | 74 | LinearFitter: for use with equations that are linearly dependent on the input parameters. Standard 75 | linear regression where derivatives are taken by setting all of the parameters to zero, except for the 76 | one of interest. 77 | 78 | NonLinearSolver: Similar to the LinearSolver, except it will run multiple iterations, there is a damping 79 | factor, and the derivatives are calculated by taken by varying the parameters a small amount from the 80 | previous guess. 81 | 82 | MarquardtFitter: Similar to the NonLinearSolver except the damping is adaptive. 83 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.orangepalantir 7 | leastsquares 8 | 1.0.1-SNAPSHOT 9 | jar 10 | 11 | Least squares in java. 12 | https://github.com/odinsbane/least-squares-in-java 13 | A small library for using least squres fitting in java. Build using the JAMA 14 | matrix library 15 | 16 | 17 | The MIT License (MIT) 18 | https://opensource.org/licenses/MIT 19 | repo 20 | 21 | 22 | 23 | 24 | odinsbane 25 | Matthew Smith 26 | melkor@orangepalantir.org 27 | https://orangepalantir.org 28 | UCL LMCB 29 | http://ucl.ac.uk/lmcb 30 | 31 | lead 32 | developer 33 | debugger 34 | reviewer 35 | support 36 | maintainer 37 | 38 | +1 39 | 40 | 41 | 42 | scm:git:git://github.com/odinsbane/least-squares-in-java 43 | scm:git:git@github.com/odinsbane/least-squares-in-java 44 | HEAD 45 | https://github.com/odinsbane/least-squares-in-java 46 | 47 | 48 | 49 | junit 50 | junit 51 | 4.8.2 52 | test 53 | 54 | 55 | gov.nist.math 56 | jama 57 | 1.0.3 58 | compile 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | org.apache.maven.plugins 67 | maven-compiler-plugin 68 | 3.5.1 69 | 70 | 1.6 71 | 1.6 72 | true 73 | true 74 | 75 | 76 | 77 | 78 | org.apache.maven.plugins 79 | maven-source-plugin 80 | 3.0.0 81 | 82 | 83 | attach-sources 84 | 85 | jar 86 | 87 | 88 | 89 | 90 | 91 | 92 | org.apache.maven.plugins 93 | maven-javadoc-plugin 94 | 2.10.3 95 | 96 | 97 | attach-javadocs 98 | 99 | jar 100 | 101 | 102 | -Xdoclint:none 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /src/main/java/org/orangepalantir/filters/SavitzyGolayFilter.java: -------------------------------------------------------------------------------- 1 | package org.orangepalantir.filters; 2 | import Jama.LUDecomposition; 3 | import Jama.Matrix; 4 | 5 | /** Class implements the Savitzy-Golay filter. The algorithm is based on 6 | * the algorithm used in 7 | * 8 | * Numerical Methods in C: The art of scientific computing second edition 9 | * 10 | * This filter is used for smoothing data. The current implementation is 11 | * a 1-d approach and a symmetric 2-d approach. 12 | * 13 | * 14 | * @Author: Matthew B. Smith 15 | * @Url: http://orangepalantir.org 16 | * @Version: 0.8 17 | * @Date: 2/10/2010 18 | * 19 | **/ 20 | public class SavitzyGolayFilter{ 21 | double[] coefficients; 22 | int LEFT, RIGHT, ORDER; 23 | /** 24 | * Prepares the filter coefficients 25 | * 26 | * @param left number of places to the left of the origion 27 | * @param right number of places to the right of the origion 28 | * @param order of polynomial used to fit data 29 | * */ 30 | public SavitzyGolayFilter(int left, int right, int order){ 31 | 32 | coefficients = SavitzkyGolayCoefficients(left,right,order); 33 | LEFT = left; 34 | RIGHT = right; 35 | ORDER = order; 36 | } 37 | 38 | /** 39 | * Prepares the filter coefficients 40 | * 41 | * @param left number of places to the left of the origion 42 | * @param right number of places to the right of the origion 43 | * @param order of polynomial used to fit data 44 | * @param derivative use a SavitzkyGolayFilter to calculate the derivative or order. 45 | * */ 46 | public SavitzyGolayFilter(int left, int right, int order, int derivative){ 47 | 48 | coefficients = SavitzkyGolayCoefficients(left,right,order, derivative); 49 | LEFT = left; 50 | RIGHT = right; 51 | ORDER = order; 52 | } 53 | 54 | 55 | /** 56 | * filters the data by assuming the ends are reflected. 57 | * 58 | * @param data the array that is going to be filtered. 59 | * */ 60 | public double[] filterData(double[] data){ 61 | int pts = data.length; 62 | double[] ret_value = new double[pts]; 63 | 64 | int j; 65 | //reflected 66 | for(j = 0; jorder - xp){ 416 | 417 | yp = 0; 418 | xp++; 419 | 420 | } 421 | } 422 | 423 | 424 | 425 | Matrix rs = new Matrix(equals, M); 426 | Matrix ls = lud.solve(rs); 427 | 428 | 429 | //evaluate 2-d function; 430 | double sum = 0; 431 | 432 | xp = 0; 433 | yp = 0; 434 | 435 | for(int k = 0; korder - xp){ 442 | 443 | yp = 0; 444 | xp++; 445 | 446 | } 447 | } 448 | 449 | kernel[j][i] = sum; 450 | } 451 | 452 | } 453 | 454 | return kernel; 455 | } 456 | 457 | /** 458 | * Evaluates a polynomial where: 459 | * p(x) = poly[0] * x**m + poly[1] * x**(m-1) + ... + poly[m] 460 | * 461 | * @param poly - double array representation of a polynomial 462 | * @param x - the variable that will be evaluated. 463 | **/ 464 | public static double evaluatePolynomial(double[] poly, double x){ 465 | 466 | double val = 0; 467 | int m = poly.length; 468 | for(int j = 0; j0){ 79 | aafter[j-1] = A[j-1]; 80 | abefore[j-1] = A[j-1]; 81 | } 82 | 83 | for(int i = 0; iMath.abs(values[i][0])?max_change:Math.abs(values[i][0]); 128 | A[i] += values[i][0]*STEP; 129 | } 130 | 131 | return max_change; 132 | 133 | 134 | } 135 | public void iterateValuesB(){ 136 | Matrix system = new Matrix(DERIVATIVES); 137 | Matrix params = new Matrix(ERROR,ERROR.length); 138 | //params = params.times(-1.); 139 | Matrix out = system.solve(params); 140 | 141 | double[][] values = out.getArray(); 142 | 143 | for(int i = 0; ilast_error){ 170 | System.err.println("Error increased: consider smaller step size."); 171 | break; 172 | } 173 | last_error = e; 174 | calculateDerivatives(); 175 | try{ 176 | changes = iterateValues(); 177 | if(changes< MIN_CHANGE) 178 | break; 179 | } catch(Exception exc){ 180 | throw new RuntimeException(exc); 181 | } 182 | } 183 | if(i==MAX_ITERATIONS){ 184 | System.err.println("Warning: Maximum iteration reached."); 185 | } 186 | 187 | 188 | } 189 | /** 190 | * Gets the current set of parameters values. 191 | * */ 192 | public double[] getParameters(){ 193 | return A; 194 | } 195 | 196 | @Override 197 | public double[] getUncertainty() { 198 | return new double[0]; 199 | } 200 | 201 | public void setStepSize(double step){ 202 | STEP = step; 203 | } 204 | 205 | public void setMinError(double error){ 206 | MIN_ERROR = error; 207 | } 208 | 209 | /** 210 | * When the parameters change less than the change parameter the program will stop iterating 211 | * 212 | * @param change 213 | */ 214 | public void setMinChange(double change){ 215 | MIN_CHANGE = change; 216 | } 217 | 218 | } 219 | -------------------------------------------------------------------------------- /src/main/java/org/orangepalantir/leastsquares/functions/ExponentialFunction.java: -------------------------------------------------------------------------------- 1 | package org.orangepalantir.leastsquares.functions; 2 | 3 | import org.orangepalantir.leastsquares.Function; 4 | 5 | /** 6 | * 7 | * Created by msmith on 2/26/14. 8 | */ 9 | public class ExponentialFunction implements Function { 10 | @Override 11 | public double evaluate(double[] values, double[] parameters) { 12 | return parameters[0] + parameters[1]*Math.exp(parameters[2]*values[0]); 13 | } 14 | 15 | @Override 16 | public int getNParameters() { 17 | return 3; 18 | } 19 | 20 | @Override 21 | public int getNInputs() { 22 | return 1; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/org/orangepalantir/leastsquares/functions/LinearFunction.java: -------------------------------------------------------------------------------- 1 | package org.orangepalantir.leastsquares.functions; 2 | 3 | import org.orangepalantir.leastsquares.Function; 4 | 5 | /** 6 | * Generic linear function, with an offset. 7 | * 8 | * A + B x1 + C x2 + D x3 ... 9 | * 10 | * Where the number of inputs, x's is indicated in the constructor and 11 | * the number of parameters is the number of terms plus 1. 12 | * 13 | * Created by msmith on 2/26/14. 14 | */ 15 | public class LinearFunction implements Function { 16 | int terms; 17 | public LinearFunction(int terms){ 18 | this.terms = terms; 19 | } 20 | @Override 21 | public double evaluate(double[] values, double[] parameters) { 22 | double sum = parameters[0]; 23 | for(int i = 0; i