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-2024.
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    /** Constructs new running statistics accumulator. */
75    public RunningStatistics() {
76      // Empty constructor; all variables are initialized at definition.
77    }
78  
79    /**
80     * Add a value and update key variables.
81     *
82     * @param val Value to add.
83     */
84    public void addValue(double val) {
85      addValue(val, 1.0);
86    }
87  
88    /**
89     * Add a value and update key variables.
90     *
91     * @param val Value to add.
92     * @param weight Weight to give the value.
93     */
94    public void addValue(double val, double weight) {
95      assert isFinite(val);
96      assert isFinite(weight);
97      assert weight > 0.0;
98      ++count;
99      ++dof;
100     double priorMean = mean;
101     this.weight += weight;
102     double y = val - comp;
103     double t = sum + y;
104     comp = (t - sum) - y;
105     sum = t;
106 
107     min = min(min, val);
108     max = max(max, val);
109     double invCount = 1.0 / this.weight;
110     mean += ((val - mean) * invCount);
111     var += ((val - priorMean) * (val - mean)) * weight;
112     if (isNaN(var)) {
113       throw new IllegalArgumentException(
114           format(" Val %.5f w/ wt %.3f resulted in NaN varAcc; current state %s",
115               val, weight, new SummaryStatistics(this)));
116     }
117   }
118 
119   public void reset() {
120     // Weight-sensitive values.
121     mean = 0;
122     var = 0;
123     weight = 0;
124     // Weight-insensitive values.
125     min = Double.MAX_VALUE;
126     max = Double.MIN_VALUE;
127     count = 0;
128     sum = 0;
129     dof = -1;
130     comp = 0;
131   }
132 
133   /**
134    * Get the count.
135    *
136    * @return Returns the count.
137    */
138   public long getCount() {
139     return count;
140   }
141 
142   /**
143    * Get the DOF.
144    *
145    * @return Returns DOF.
146    */
147   public long getDOF() {
148     return dof;
149   }
150 
151   /**
152    * Get the max.
153    *
154    * @return Returns the max.
155    */
156   public double getMax() {
157     return max;
158   }
159 
160   /**
161    * Gets the mean as of the last value added.
162    *
163    * @return Current running mean.
164    */
165   public double getMean() {
166     return mean;
167   }
168 
169   /**
170    * Get the min.
171    *
172    * @return Returns the min.
173    */
174   public double getMin() {
175     return min;
176   }
177 
178   /**
179    * Get the population standard deviations.
180    *
181    * @return The population standard deviation.
182    */
183   public double getPopulationStandardDeviation() {
184     return sqrt(getPopulationVariance());
185   }
186 
187   /**
188    * Get the population variance.
189    *
190    * @return Returns the population variance.
191    */
192   public double getPopulationVariance() {
193     return var / ((double) count);
194   }
195 
196   /**
197    * Get the standard deviation.
198    *
199    * @return Returns the standard deviation.
200    */
201   public double getStandardDeviation() {
202     return sqrt(getVariance());
203   }
204 
205   /**
206    * Get the sum.
207    *
208    * @return Returns the sum.
209    */
210   public double getSum() {
211     return sum;
212   }
213 
214   /**
215    * Get the variance.
216    *
217    * @return Returns the variance.
218    */
219   public double getVariance() {
220     return var / ((double) dof);
221   }
222 
223   /**
224    * Get the weight.
225    *
226    * @return Returns the weight.
227    */
228   public double getWeight() {
229     return weight;
230   }
231 
232   /**
233    * Describe the Summary Statistics.
234    *
235    * @return Return the description.
236    */
237   public String describe() {
238     return format(" Mean: %12.6f +/-%12.6f, Min/Max: %12.6f/%12.6f", mean,
239         getStandardDeviation(), min, max);
240   }
241 
242 }