View Javadoc
1   /*
2    * Copyright (C) 2018 Alberto Irurueta Carro (alberto@irurueta.com)
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *         http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package com.irurueta.geometry;
17  
18  import com.irurueta.algebra.AlgebraException;
19  import com.irurueta.algebra.Matrix;
20  import com.irurueta.algebra.NonSymmetricPositiveDefiniteMatrixException;
21  import com.irurueta.algebra.SingularValueDecomposer;
22  import com.irurueta.statistics.NormalDist;
23  
24  import java.io.Serializable;
25  
26  /**
27   * Base class representing the confidence of provided accuracy from a covariance matrix
28   * expressed in the distance unit of such matrix.
29   * This class contains utility methods to convert covariance matrices into geometric figures
30   * with the requested confidence.
31   */
32  public abstract class Accuracy implements Serializable {
33  
34      /**
35       * Default standard deviation factor to account for a given accuracy confidence.
36       * Typically, a factor of 2.0 will be used, which means that accuracy of position will
37       * be drawn as an ellipse of size equal to one time the standard deviation. Assuming a
38       * Gaussian distribution this is equivalent to providing a 95.44% of confidence on provided
39       * accuracy.
40       */
41      private static final double DEFAULT_STANDARD_DEVIATION_FACTOR = 2.0;
42  
43      /**
44       * Covariance matrix representing the accuracy of an estimated position.
45       */
46      private Matrix covarianceMatrix;
47  
48      /**
49       * Standard deviation factor to account for a given accuracy confidence.
50       * Typically, a factor of 2.0 will be used, which means that accuracy of a point or
51       * measure will be drawn as a geometric figure (either an ellipse in 2D or an
52       * ellipsoid in 3D) equal to two times the standard deviation. Assuming a
53       * Gaussian distribution this is equivalent to providing a 95.44% of confidence on
54       * provided accuracy.
55       */
56      protected double standardDeviationFactor = DEFAULT_STANDARD_DEVIATION_FACTOR;
57  
58      /**
59       * Confidence of provided accuracy of a point for a value located up to the standard
60       * deviation factor distance from the mean.
61       * This is expressed as a value between 0 and 1, where 1 indicates a 100% confidence that
62       * the real position is within provided accuracy.
63       */
64      protected double confidence = 2.0 * NormalDist.cdf(DEFAULT_STANDARD_DEVIATION_FACTOR, 0.0, 1.0) - 1.0;
65  
66      /**
67       * Square root of singular values of decomposed covariance matrix.
68       */
69      protected double[] sqrtSingularValues;
70  
71      /**
72       * Orthonormal matrix representing a rotation after decomposing covariance matrix.
73       */
74      protected Matrix u;
75  
76      /**
77       * Minimum square root of singular value of decomposed covariance matrix. Can be used to
78       * determine the smallest accuracy on a geometric figure (i.e. the shortest semi-axis on
79       * an ellipse or an ellipsoid).
80       */
81      private double minSqrtSingularValue = Double.POSITIVE_INFINITY;
82  
83      /**
84       * Maximum square root of singular value of decomposed covariance matrix. Can be used to
85       * determine the largest accuracy on a geometric figure (i.e. the largest semi-axis on
86       * an ellipse or an ellipsoid).
87       */
88      private double maxSqrtSingularValue = Double.POSITIVE_INFINITY;
89  
90      /**
91       * Average square root of singular value of decomposed covariance matrix. Can be used to
92       * determine the average accuracy on a geometric figure (i.e. the average semi-axis on
93       * an ellipse or an ellipsoid).
94       */
95      private double avgSqrtSingularValue = Double.POSITIVE_INFINITY;
96  
97      /**
98       * Constructor.
99       */
100     protected Accuracy() {
101     }
102 
103     /**
104      * Constructor.
105      *
106      * @param covarianceMatrix covariance matrix to be set. Must be NxN where N
107      *                         is the number of dimensions and positive definite.
108      * @throws IllegalArgumentException                    if provided matrix is not square (it must also be
109      *                                                     positive definite to be properly converted to a geometric figure - e.g. an ellipse or
110      *                                                     an ellipsoid).
111      * @throws NonSymmetricPositiveDefiniteMatrixException if provided matrix is not symmetric and
112      *                                                     positive definite.
113      */
114     protected Accuracy(final Matrix covarianceMatrix) throws NonSymmetricPositiveDefiniteMatrixException {
115         setCovarianceMatrix(covarianceMatrix);
116     }
117 
118     /**
119      * Constructor.
120      *
121      * @param confidence confidence of provided accuracy of an estimated position.
122      * @throws IllegalArgumentException if provided value is not within 0 and 1.
123      */
124     protected Accuracy(final double confidence) {
125         setConfidence(confidence);
126     }
127 
128     /**
129      * Constructor.
130      *
131      * @param covarianceMatrix covariance matrix to be set. Must be NxN where N
132      *                         is the number of dimensions and positive definite.
133      * @param confidence       confidence of provided accuracy of an estimated position.
134      * @throws IllegalArgumentException                    if provided matrix is not square (it must also be
135      *                                                     positive definite to be properly converted to a geometric figure - e.g. an ellipse or
136      *                                                     an ellipsoid), or if provided confidence value is not within 0 and 1.
137      * @throws NonSymmetricPositiveDefiniteMatrixException if provided matrix is not symmetric and
138      *                                                     positive definite.
139      */
140     protected Accuracy(final Matrix covarianceMatrix, final double confidence)
141             throws NonSymmetricPositiveDefiniteMatrixException {
142         setCovarianceMatrix(covarianceMatrix);
143         setConfidence(confidence);
144     }
145 
146     /**
147      * Gets covariance matrix representing the accuracy of an estimated point or measure.
148      *
149      * @return covariance matrix representing the accuracy of an estimated point or measure.
150      */
151     public Matrix getCovarianceMatrix() {
152         return covarianceMatrix;
153     }
154 
155     /**
156      * Sets covariance matrix representing the accuracy of an estimated point or measure.
157      *
158      * @param covarianceMatrix covariance matrix representing the accuracy of an estimated
159      *                         point or measure.
160      * @throws IllegalArgumentException                    if provided matrix is not square (it must also be
161      *                                                     positive definite to be properly converted to a geometric figure - e.g. an ellipse
162      *                                                     or an ellipsoid).
163      * @throws NonSymmetricPositiveDefiniteMatrixException if provided matrix is not symmetric and
164      *                                                     positive definite.
165      */
166     public void setCovarianceMatrix(final Matrix covarianceMatrix) throws NonSymmetricPositiveDefiniteMatrixException {
167         final var dims = getNumberOfDimensions();
168         if (covarianceMatrix.getRows() != dims || covarianceMatrix.getColumns() != dims) {
169             throw new IllegalArgumentException();
170         }
171 
172         try {
173             final var svdDecomposer = new SingularValueDecomposer();
174             svdDecomposer.setInputMatrix(covarianceMatrix);
175             svdDecomposer.decompose();
176 
177             final var singularValues = svdDecomposer.getSingularValues();
178             final var sqrtSingularValues = new double[dims];
179 
180             var minSqrtSingularValue = Double.MAX_VALUE;
181             var maxSqrtSingularValue = -Double.MAX_VALUE;
182             var avgSqrtSingularValue = 0.0;
183             var i = 0;
184             for (final var singularValue : singularValues) {
185                 if (singularValue < 0.0) {
186                     // matrix is not positive definite
187                     throw new NonSymmetricPositiveDefiniteMatrixException();
188                 }
189 
190                 final var sqrtSingularValue = Math.sqrt(singularValue);
191                 if (sqrtSingularValue < minSqrtSingularValue) {
192                     minSqrtSingularValue = sqrtSingularValue;
193                 }
194                 if (sqrtSingularValue > maxSqrtSingularValue) {
195                     maxSqrtSingularValue = sqrtSingularValue;
196                 }
197                 avgSqrtSingularValue += sqrtSingularValue / dims;
198 
199                 sqrtSingularValues[i] = sqrtSingularValue;
200                 i++;
201             }
202 
203             this.sqrtSingularValues = sqrtSingularValues;
204             u = svdDecomposer.getU();
205 
206             this.minSqrtSingularValue = minSqrtSingularValue;
207             this.maxSqrtSingularValue = maxSqrtSingularValue;
208             this.avgSqrtSingularValue = avgSqrtSingularValue;
209 
210             this.covarianceMatrix = covarianceMatrix;
211         } catch (final AlgebraException e) {
212             throw new NonSymmetricPositiveDefiniteMatrixException(e);
213         }
214     }
215 
216     /**
217      * Gets standard deviation factor to account for a given accuracy confidence.
218      * Typically, a factor of 2.0 will be used, which means that accuracy can be drawn as
219      * a geometric figure of size equal to 2 times the standard deviation. Assuming a
220      * Gaussian distribution this is equivalent to providing a 95.44% confidence on provided
221      * accuracy.
222      *
223      * @return standard deviation factor.
224      */
225     public double getStandardDeviationFactor() {
226         return standardDeviationFactor;
227     }
228 
229     /**
230      * Sets standard deviation factor to account for a given accuracy confidence.
231      * Typically, a factor of 2.0 will be used, which means that accuracy can be drawn as
232      * a geometric figure of size equal to 2 times the standard deviation. Assuming a
233      * Gaussian distribution this is equivalent to providing a 95.44% confidence on provided
234      * accuracy.
235      *
236      * @param standardDeviationFactor standard deviation factor to be set.
237      * @throws IllegalArgumentException if provided value is zero or negative.
238      */
239     public void setStandardDeviationFactor(final double standardDeviationFactor) {
240         if (standardDeviationFactor <= 0.0) {
241             throw new IllegalArgumentException();
242         }
243         this.standardDeviationFactor = standardDeviationFactor;
244         confidence = 2.0 * NormalDist.cdf(this.standardDeviationFactor, 0.0, 1.0) - 1.0;
245     }
246 
247     /**
248      * Gets confidence of provided accuracy of estimated point or measure.
249      * This is expressed as a value between 0 and 1, where 1 indicates a 100% confidence
250      * that the real point or measure is within provided accuracy.
251      *
252      * @return confidence of provided accuracy of estimated point or measure.
253      */
254     public double getConfidence() {
255         return confidence;
256     }
257 
258     /**
259      * Sets confidence of provided accuracy of estimated point or measure.
260      * This is expressed as a value between 0 and 1, where 1 indicates a 100% confidence
261      * that the real point or measure is within provided accuracy.
262      *
263      * @param confidence confidence of provided accuracy of estimated point or measure.
264      * @throws IllegalArgumentException if provided value is not within 0 and 1.
265      */
266     public void setConfidence(final double confidence) {
267         if (confidence < 0.0 || confidence > 1.0) {
268             throw new IllegalArgumentException();
269         }
270         this.confidence = confidence;
271         standardDeviationFactor = NormalDist.invcdf((confidence + 1.0) / 2.0, 0.0, 1.0);
272     }
273 
274     /**
275      * Gets smallest (best) accuracy in any direction (i.e. either 2D or 3D).
276      * This value is represented by the smallest semi axis representing the ellipse or ellipsoid of accuracy.
277      *
278      * @return smallest accuracy in any direction.
279      */
280     public double getSmallestAccuracy() {
281         return minSqrtSingularValue * standardDeviationFactor;
282     }
283 
284     /**
285      * Gets largest (worse) accuracy in any direction (i.e. either 2D or 3D).
286      * This value is represented by the largest semi axis representing the ellipse or ellipsoid of accuracy.
287      *
288      * @return largest accuracy in any direction.
289      */
290     public double getLargestAccuracy() {
291         return maxSqrtSingularValue * standardDeviationFactor;
292     }
293 
294     /**
295      * Gets average accuracy among all directions.
296      * This value is equal to the average value of all semi axes representing the ellipse or ellipsoid of
297      * accuracy.
298      *
299      * @return average accuracy among all directions.
300      */
301     public double getAverageAccuracy() {
302         return avgSqrtSingularValue * standardDeviationFactor;
303     }
304 
305     /**
306      * Gets number of dimensions.
307      * This is equal to 2 for 2D, and to 3 for 3D.
308      *
309      * @return number of dimensions.
310      */
311     public abstract int getNumberOfDimensions();
312 }