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 }