1 // ****************************************************************************** 2 // 3 // Title: Force Field X. 4 // Description: Force Field X - Software for Molecular Biophysics. 5 // Copyright: Copyright (c) Michael J. Schnieders 2001-2025. 6 // 7 // This file is part of Force Field X. 8 // 9 // Force Field X is free software; you can redistribute it and/or modify it 10 // under the terms of the GNU General Public License version 3 as published by 11 // the Free Software Foundation. 12 // 13 // Force Field X is distributed in the hope that it will be useful, but WITHOUT 14 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 // FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 16 // details. 17 // 18 // You should have received a copy of the GNU General Public License along with 19 // Force Field X; if not, write to the Free Software Foundation, Inc., 59 Temple 20 // Place, Suite 330, Boston, MA 02111-1307 USA 21 // 22 // Linking this library statically or dynamically with other modules is making a 23 // combined work based on this library. Thus, the terms and conditions of the 24 // GNU General Public License cover the whole combination. 25 // 26 // As a special exception, the copyright holders of this library give you 27 // permission to link this library with independent modules to produce an 28 // executable, regardless of the license terms of these independent modules, and 29 // to copy and distribute the resulting executable under terms of your choice, 30 // provided that you also meet, for each linked independent module, the terms 31 // and conditions of the license of that module. An independent module is a 32 // module which is not derived from or based on this library. If you modify this 33 // library, you may extend this exception to your version of the library, but 34 // you are not obligated to do so. If you do not wish to do so, delete this 35 // exception statement from your version. 36 // 37 // ****************************************************************************** 38 package ffx.numerics.math; 39 40 import static java.lang.Double.isFinite; 41 import static java.lang.Double.isNaN; 42 import static java.lang.String.format; 43 import static org.apache.commons.math3.util.FastMath.max; 44 import static org.apache.commons.math3.util.FastMath.min; 45 import static org.apache.commons.math3.util.FastMath.sqrt; 46 47 /** 48 * The RunningStatistics class uses online, stable algorithms to calculate summary statistics from a 49 * source of doubles, including mean, variance, standard deviation, max, min, sum, and count. 50 * 51 * <p>This is intended for accuracy and numerical stability, not necessarily for performance (e.g. 52 * using Kahan summation). 53 * 54 * <p>This is effectively a dynamic version of SummaryStatistics. 55 * 56 * @author Michael J. Schnieders 57 * @author Jacob M. Litman 58 * @since 1.0 59 */ 60 public class RunningStatistics { 61 62 // Weight-sensitive values. 63 private double mean = 0; 64 private double var = 0; 65 private double weight = 0; 66 // Weight-insensitive values. 67 private double min = Double.MAX_VALUE; 68 private double max = Double.MIN_VALUE; 69 private long count = 0; 70 private double sum = 0; 71 private long dof = -1; 72 private double comp = 0; 73 74 /** 75 * Constructs new running statistics accumulator. 76 */ 77 public RunningStatistics() { 78 // Empty constructor; all variables are initialized at definition. 79 } 80 81 /** 82 * Add a value and update key variables. 83 * 84 * @param val Value to add. 85 */ 86 public void addValue(double val) { 87 addValue(val, 1.0); 88 } 89 90 /** 91 * Add a value and update key variables. 92 * 93 * @param val Value to add. 94 * @param weight Weight to give the value. 95 */ 96 public void addValue(double val, double weight) { 97 assert isFinite(val); 98 assert isFinite(weight); 99 assert weight > 0.0; 100 ++count; 101 ++dof; 102 double priorMean = mean; 103 this.weight += weight; 104 double y = val - comp; 105 double t = sum + y; 106 comp = (t - sum) - y; 107 sum = t; 108 109 min = min(min, val); 110 max = max(max, val); 111 double invCount = 1.0 / this.weight; 112 mean += ((val - mean) * invCount); 113 var += ((val - priorMean) * (val - mean)) * weight; 114 if (isNaN(var)) { 115 throw new IllegalArgumentException( 116 format(" Val %.5f w/ wt %.3f resulted in NaN varAcc; current state %s", 117 val, weight, new SummaryStatistics(this))); 118 } 119 } 120 121 /** 122 * Add a RunningStatistics object to this one. 123 */ 124 public void reset() { 125 // Weight-sensitive values. 126 mean = 0; 127 var = 0; 128 weight = 0; 129 // Weight-insensitive values. 130 min = Double.MAX_VALUE; 131 max = Double.MIN_VALUE; 132 count = 0; 133 sum = 0; 134 dof = -1; 135 comp = 0; 136 } 137 138 /** 139 * Get the count. 140 * 141 * @return Returns the count. 142 */ 143 public long getCount() { 144 return count; 145 } 146 147 /** 148 * Get the DOF. 149 * 150 * @return Returns DOF. 151 */ 152 public long getDOF() { 153 return dof; 154 } 155 156 /** 157 * Get the max. 158 * 159 * @return Returns the max. 160 */ 161 public double getMax() { 162 return max; 163 } 164 165 /** 166 * Gets the mean as of the last value added. 167 * 168 * @return Current running mean. 169 */ 170 public double getMean() { 171 return mean; 172 } 173 174 /** 175 * Get the min. 176 * 177 * @return Returns the min. 178 */ 179 public double getMin() { 180 return min; 181 } 182 183 /** 184 * Get the population standard deviations. 185 * 186 * @return The population standard deviation. 187 */ 188 public double getPopulationStandardDeviation() { 189 return sqrt(getPopulationVariance()); 190 } 191 192 /** 193 * Get the population variance. 194 * 195 * @return Returns the population variance. 196 */ 197 public double getPopulationVariance() { 198 return var / ((double) count); 199 } 200 201 /** 202 * Get the standard deviation. 203 * 204 * @return Returns the standard deviation. 205 */ 206 public double getStandardDeviation() { 207 return sqrt(getVariance()); 208 } 209 210 /** 211 * Get the sum. 212 * 213 * @return Returns the sum. 214 */ 215 public double getSum() { 216 return sum; 217 } 218 219 /** 220 * Get the variance. 221 * 222 * @return Returns the variance. 223 */ 224 public double getVariance() { 225 return var / ((double) dof); 226 } 227 228 /** 229 * Get the weight. 230 * 231 * @return Returns the weight. 232 */ 233 public double getWeight() { 234 return weight; 235 } 236 237 /** 238 * Describe the Summary Statistics. 239 * 240 * @return Return the description. 241 */ 242 public String describe() { 243 return format(" Mean: %12.6f +/-%12.6f, Min/Max: %12.6f/%12.6f", mean, 244 getStandardDeviation(), min, max); 245 } 246 247 }