View Javadoc
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 }