1 /*
2 * Copyright (C) 2016 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.numerical.polynomials.estimators;
17
18 import com.irurueta.numerical.LockedException;
19 import com.irurueta.numerical.NotReadyException;
20 import com.irurueta.numerical.polynomials.Polynomial;
21 import com.irurueta.numerical.robust.RobustEstimatorException;
22 import com.irurueta.numerical.robust.RobustEstimatorMethod;
23
24 import java.util.List;
25
26 /**
27 * This is an abstract class for algorithms to robustly find the best
28 * Polynomial for provided collection of evaluations.
29 * Implementations of this class should be able to detect and discard outliers
30 * in order to find the best solution.
31 */
32 @SuppressWarnings("Duplicates")
33 public abstract class PolynomialRobustEstimator {
34
35 /**
36 * Default robust estimator method when none is provided.
37 * In general for Polynomial estimation is best to use PROSAC or RANSAC
38 * than any other method, as it provides more robust methods.
39 */
40 public static final RobustEstimatorMethod DEFAULT_ROBUST_METHOD = RobustEstimatorMethod.PROSAC;
41
42 /**
43 * Default amount of progress variation before notifying a change in
44 * estimation progress. By default, this is set to 5%.
45 */
46 public static final float DEFAULT_PROGRESS_DELTA = 0.05f;
47
48 /**
49 * Minimum allowed value for progress delta.
50 */
51 public static final float MIN_PROGRESS_DELTA = 0.0f;
52
53 /**
54 * Maximum allowed value for progress delta.
55 */
56 public static final float MAX_PROGRESS_DELTA = 1.0f;
57
58 /**
59 * Constant defining default confidence of the estimated result, which is
60 * 99%. This means that with a probability of 99% estimation will be
61 * accurate because chosen sub-samples will be inliers.
62 */
63 public static final double DEFAULT_CONFIDENCE = 0.99;
64
65 /**
66 * Default maximum allowed number of iterations.
67 */
68 public static final int DEFAULT_MAX_ITERATIONS = 5000;
69
70 /**
71 * Minimum allowed confidence value.
72 */
73 public static final double MIN_CONFIDENCE = 0.0;
74
75 /**
76 * Maximum allowed confidence value.
77 */
78 public static final double MAX_CONFIDENCE = 1.0;
79
80 /**
81 * Minimum allowed number of iterations.
82 */
83 public static final int MIN_ITERATIONS = 1;
84
85 /**
86 * Flag indicating whether geometric distance is used by default or not
87 * to find outliers.
88 */
89 public static final boolean DEFAULT_USE_GEOMETRIC_DISTANCE = false;
90
91 /**
92 * Collection of polynomial evaluations and their corresponding point of
93 * evaluation used to determine a polynomial of required degree.
94 */
95 protected List<PolynomialEvaluation> evaluations;
96
97 /**
98 * Internal non robust estimator of polynomial estimator.
99 */
100 protected final LMSEPolynomialEstimator polynomialEstimator;
101
102 /**
103 * Listener to be notified of events such as when estimation starts, ends or
104 * its progress significantly changes.
105 */
106 protected PolynomialRobustEstimatorListener listener;
107
108 /**
109 * Indicates if this estimator is locked because an estimation is being
110 * computed.
111 */
112 protected boolean locked;
113
114 /**
115 * Amount of progress variation before notifying a progress change during
116 * estimation.
117 */
118 protected float progressDelta;
119
120 /**
121 * Amount of confidence expressed as a value between 0.0 and 1.0 (which is
122 * equivalent to 100%). The amount of confidence indicates the probability
123 * that the estimated result is correct. Usually this value will be close
124 * to 1.0, but not exactly 1.0.
125 */
126 protected double confidence;
127
128 /**
129 * Maximum allowed number of iterations. When the maximum number of
130 * iterations is exceeded, result will not be available, however an
131 * approximate result will be available for retrieval.
132 */
133 protected int maxIterations;
134
135 /**
136 * Indicates whether geometric distance will be used to find outliers or
137 * algebraic distance will be used instead.
138 */
139 protected boolean useGeometricDistance;
140
141 /**
142 * Constructor.
143 */
144 protected PolynomialRobustEstimator() {
145 progressDelta = DEFAULT_PROGRESS_DELTA;
146 confidence = DEFAULT_CONFIDENCE;
147 maxIterations = DEFAULT_MAX_ITERATIONS;
148 useGeometricDistance = DEFAULT_USE_GEOMETRIC_DISTANCE;
149 polynomialEstimator = new LMSEPolynomialEstimator();
150 }
151
152 /**
153 * Constructor.
154 *
155 * @param degree degree of polynomial to be estimated.
156 * @throws IllegalArgumentException if provided degree is less than 1.
157 */
158 protected PolynomialRobustEstimator(final int degree) {
159 progressDelta = DEFAULT_PROGRESS_DELTA;
160 confidence = DEFAULT_CONFIDENCE;
161 maxIterations = DEFAULT_MAX_ITERATIONS;
162 useGeometricDistance = DEFAULT_USE_GEOMETRIC_DISTANCE;
163 polynomialEstimator = new LMSEPolynomialEstimator(degree);
164 }
165
166 /**
167 * Constructor.
168 *
169 * @param evaluations collection of polynomial evaluations.
170 * @throws IllegalArgumentException if provided number of evaluations is
171 * less than the required minimum.
172 */
173 protected PolynomialRobustEstimator(final List<PolynomialEvaluation> evaluations) {
174 this();
175 internalSetEvaluations(evaluations);
176 }
177
178 /**
179 * Constructor.
180 *
181 * @param listener listener to be notified of events such as when estimation
182 * starts, ends or its progress significantly changes.
183 */
184 protected PolynomialRobustEstimator(final PolynomialRobustEstimatorListener listener) {
185 this();
186 this.listener = listener;
187 }
188
189 /**
190 * Constructor.
191 *
192 * @param degree degree of polynomial to be estimated.
193 * @param evaluations collection of polynomial evaluations.
194 * @throws IllegalArgumentException if provided degree is less than 1 or if
195 * provided number of evaluations is less than the required minimum for
196 * provided degree.
197 */
198 protected PolynomialRobustEstimator(
199 final int degree, final List<PolynomialEvaluation> evaluations) {
200 this(degree);
201 internalSetEvaluations(evaluations);
202 }
203
204 /**
205 * Constructor.
206 *
207 * @param degree degree of polynomial to be estimated.
208 * @param listener listener to be notified of events such as when estimation
209 * starts, ends or its progress significantly changes.
210 * @throws IllegalArgumentException if provided degree is less than 1.
211 */
212 protected PolynomialRobustEstimator(
213 final int degree, final PolynomialRobustEstimatorListener listener) {
214 this(degree);
215 this.listener = listener;
216 }
217
218 /**
219 * Constructor.
220 *
221 * @param evaluations collection of polynomial evaluations.
222 * @param listener listener to be notified of events such as when estimation
223 * starts, ends or its progress significantly changes.
224 * @throws IllegalArgumentException if provided number of evaluations is
225 * less than the required minimum.
226 */
227 protected PolynomialRobustEstimator(
228 final List<PolynomialEvaluation> evaluations, final PolynomialRobustEstimatorListener listener) {
229 this(evaluations);
230 this.listener = listener;
231 }
232
233 /**
234 * Constructor.
235 *
236 * @param degree degree of polynomial to be estimated.
237 * @param evaluations collection of polynomial evaluations.
238 * @param listener listener to be notified of events.
239 * @throws IllegalArgumentException if provided degree is less than 1 or if
240 * provided number of evaluations is less than the required minimum for
241 * provided degree.
242 */
243 protected PolynomialRobustEstimator(
244 final int degree, final List<PolynomialEvaluation> evaluations,
245 final PolynomialRobustEstimatorListener listener) {
246 this(degree, evaluations);
247 this.listener = listener;
248 }
249
250 /**
251 * Gets collection of polynomial evaluations and their corresponding point
252 * of evaluation used to determine a polynomial of required degree.
253 *
254 * @return collection of polynomial evaluations.
255 */
256 public List<PolynomialEvaluation> getEvaluations() {
257 return evaluations;
258 }
259
260 /**
261 * Sets collection of polynomial evaluations and their corresponding point
262 * of evaluation used to determine a polynomial of required degree.
263 *
264 * @param evaluations collection of polynomial evaluations.
265 * @throws LockedException if estimator is locked.
266 * @throws IllegalArgumentException if provided list of evaluations does
267 * not contain enough evaluations to estimate the polynomial using current
268 * settings.
269 */
270 public void setEvaluations(final List<PolynomialEvaluation> evaluations) throws LockedException {
271 if (isLocked()) {
272 throw new LockedException();
273 }
274 internalSetEvaluations(evaluations);
275 }
276
277 /**
278 * Gets minimum number of evaluations required to estimate a polynomial of
279 * the specified degree.
280 *
281 * @return number of required evaluations.
282 */
283 public int getMinNumberOfEvaluations() {
284 return polynomialEstimator.getMinNumberOfEvaluations();
285 }
286
287 /**
288 * Gets listener to be notified of events such as when estimation starts,
289 * ends or its progress significantly changes.
290 *
291 * @return listener to be notified of events.
292 */
293 public PolynomialRobustEstimatorListener getListener() {
294 return listener;
295 }
296
297 /**
298 * Sets listener to be notified of events such as when estimation starts,
299 * ends or its progress significantly changes.
300 *
301 * @param listener listener to be notified of events.
302 */
303 public void setListener(final PolynomialRobustEstimatorListener listener) {
304 this.listener = listener;
305 }
306
307 /**
308 * Indicates if this estimator is locked because an estimation is being
309 * computed.
310 *
311 * @return true if this estimator is locked, false otherwise.
312 */
313 public boolean isLocked() {
314 return locked;
315 }
316
317 /**
318 * Returns amount of progress variation before notifying a progress change
319 * during estimation.
320 *
321 * @return amount of progress variation before notifying a progress change
322 * during estimation.
323 */
324 public float getProgressDelta() {
325 return progressDelta;
326 }
327
328 /**
329 * Sets amount of progress variation before notifying a progress change
330 * during estimation.
331 *
332 * @param progressDelta amount of progress variation before notifying a
333 * progress change during estimation.
334 * @throws IllegalArgumentException if progress delta is less than zero or
335 * greater than 1.
336 * @throws LockedException if this estimator is locked because an estimation
337 * is being computed.
338 */
339 public void setProgressDelta(final float progressDelta) throws LockedException {
340 if (isLocked()) {
341 throw new LockedException();
342 }
343 if (progressDelta < MIN_PROGRESS_DELTA || progressDelta > MAX_PROGRESS_DELTA) {
344 throw new IllegalArgumentException();
345 }
346 this.progressDelta = progressDelta;
347 }
348
349 /**
350 * Returns amount of confidence expressed as a value between 0.0 and 1.0
351 * (which is equivalent to 100%). The amount of confidence indicates the
352 * probability that the estimated result is correct. Usually this value will
353 * be close to 1.0, but not exactly 1.0.
354 *
355 * @return amount of confidence as a value between 0.0 and 1.0.
356 */
357 public double getConfidence() {
358 return confidence;
359 }
360
361 /**
362 * Sets amount of confidence expressed as a value between 0.0 and 1.0 (which
363 * is equivalent to 100%). The amount of confidence indicates the
364 * probability that the estimated result is correct. Usually this value will
365 * be close to 1.0 but not exactly 1.0.
366 *
367 * @param confidence confidence to be set as a value between 0.0 and 1.0.
368 * @throws IllegalArgumentException if provided value is not between 0.0 and
369 * 1.0.
370 * @throws LockedException if this estimator is locked because an estimator
371 * is being computed.
372 */
373 public void setConfidence(final double confidence) throws LockedException {
374 if (isLocked()) {
375 throw new LockedException();
376 }
377 if (confidence < MIN_CONFIDENCE || confidence > MAX_CONFIDENCE) {
378 throw new IllegalArgumentException();
379 }
380 this.confidence = confidence;
381 }
382
383 /**
384 * Returns maximum allowed number of iterations. If maximum allowed number
385 * of iterations is achieved without converging to a result when calling
386 * estimate(), a RobustEstimatorException will be raised.
387 *
388 * @return maximum allowed number of iterations.
389 */
390 public int getMaxIterations() {
391 return maxIterations;
392 }
393
394 /**
395 * Sets maximum allowed number of iterations. When the maximum number of
396 * iterations is exceeded, result will not be available, however an
397 * approximate result will be available for retrieval.
398 *
399 * @param maxIterations maximum allowed number of iterations to be set.
400 * @throws IllegalArgumentException if provided value is less than 1.
401 * @throws LockedException if this estimator is locked because an estimation
402 * is being computed.
403 */
404 public void setMaxIterations(final int maxIterations) throws LockedException {
405 if (isLocked()) {
406 throw new LockedException();
407 }
408 if (maxIterations < MIN_ITERATIONS) {
409 throw new IllegalArgumentException();
410 }
411 this.maxIterations = maxIterations;
412 }
413
414 /**
415 * Indicates whether geometric distance will be used to find outliers or
416 * algebraic distance will be used instead.
417 *
418 * @return true if geometric distance is used, false otherwise.
419 */
420 public boolean isGeometricDistanceUsed() {
421 return useGeometricDistance;
422 }
423
424 /**
425 * Specifies whether geometric distance will be used to find outliers or
426 * algebraic distance will be used instead.
427 *
428 * @param geometricDistanceUsed true if geometric distance is used, false
429 * otherwise.
430 * @throws LockedException if this estimator is locked.
431 */
432 public void setGeometricDistanceUsed(final boolean geometricDistanceUsed) throws LockedException {
433 if (isLocked()) {
434 throw new LockedException();
435 }
436 useGeometricDistance = geometricDistanceUsed;
437 }
438
439 /**
440 * Gets degree of polynomial to be estimated.
441 *
442 * @return degree of polynomial to be estimated.
443 */
444 public int getDegree() {
445 return polynomialEstimator.getDegree();
446 }
447
448 /**
449 * Sets degree of polynomial to be estimated.
450 *
451 * @param degree degree of polynomial to be estimated.
452 * @throws IllegalArgumentException if provided degree is less than 1.
453 * @throws LockedException if this instance is locked.
454 */
455 public void setDegree(final int degree) throws LockedException {
456 if (isLocked()) {
457 throw new LockedException();
458 }
459 polynomialEstimator.setDegree(degree);
460 }
461
462 /**
463 * Determines whether estimation is ready to start with the given data and
464 * required degree of polynomial to be estimated.
465 *
466 * @return true if estimator is ready, false otherwise.
467 */
468 public boolean isReady() {
469 final var nParams = polynomialEstimator.getDegree() + 1;
470 if (evaluations == null || evaluations.size() < nParams) {
471 return false;
472 }
473
474 // also ensure that at least a direct or integral evaluation exists
475 var count = 0;
476 for (final var eval : evaluations) {
477 if (eval.getType() == PolynomialEvaluationType.DIRECT_EVALUATION
478 || eval.getType() == PolynomialEvaluationType.INTEGRAL_EVALUATION
479 || eval.getType() == PolynomialEvaluationType.INTEGRAL_INTERVAL) {
480 count++;
481 }
482 }
483
484 return count >= 1 && evaluations.size() >= nParams;
485 }
486
487 /**
488 * Returns quality scores corresponding to each polynomial evaluation.
489 * The larger the score value the better the quality of the evaluation.
490 * This implementation always returns null.
491 * Subclasses using quality scores must implement proper behaviour.
492 *
493 * @return quality scores corresponding to each evaluation.
494 */
495 public double[] getQualityScores() {
496 // quality scores ignored
497 return null;
498 }
499
500 /**
501 * Sets quality scores corresponding to each polynomial evaluation.
502 * The larger the score value the better the quality of the evaluation.
503 * This implementation makes no action.
504 * Subclasses using quality scores must implement proper behaviour.
505 *
506 * @param qualityScores quality scores corresponding to each evaluation.
507 * @throws LockedException if robust estimator is locked because an
508 * estimation is already in progress.
509 * @throws IllegalArgumentException if provided quality scores length is
510 * smaller than minimum required number of evaluations.
511 */
512 public void setQualityScores(final double[] qualityScores) throws LockedException {
513 // quality scores ignored
514 }
515
516 /**
517 * Estimates polynomial.
518 *
519 * @return estimated polynomial.
520 * @throws LockedException if robust estimator is locked because an
521 * estimation is already in progress.
522 * @throws NotReadyException if provided input data is not enough to start
523 * the estimation.
524 * @throws RobustEstimatorException if estimation fails for any other reason
525 * (i.e. numerical instability, no solution available, etc).
526 */
527 public abstract Polynomial estimate() throws LockedException, NotReadyException, RobustEstimatorException;
528
529 /**
530 * Returns method being used for robust estimation.
531 *
532 * @return method being used for robust estimation.
533 */
534 public abstract RobustEstimatorMethod getMethod();
535
536 /**
537 * Creates a robust polynomial estimator using default method.
538 *
539 * @return an instance of a robust polynomial estimator.
540 */
541 public static PolynomialRobustEstimator create() {
542 return create(DEFAULT_ROBUST_METHOD);
543 }
544
545 /**
546 * Creates a robust polynomial estimator using provided degree and default
547 * method.
548 *
549 * @param degree degree of polynomial to be estimated.
550 * @return an instance of a robust polynomial estimator.
551 * @throws IllegalArgumentException if provided degree is less than 1.
552 */
553 public static PolynomialRobustEstimator create(final int degree) {
554 return create(degree, DEFAULT_ROBUST_METHOD);
555 }
556
557 /**
558 * Creates a robust polynomial estimator using provided evaluations and
559 * default method.
560 *
561 * @param evaluations collection of polynomial evaluations.
562 * @return an instance of a robust polynomial estimator.
563 */
564 public static PolynomialRobustEstimator create(final List<PolynomialEvaluation> evaluations) {
565 return create(evaluations, DEFAULT_ROBUST_METHOD);
566 }
567
568 /**
569 * Creates a robust polynomial estimator using provided listener and default
570 * method.
571 *
572 * @param listener listener to be notified of events such as when estimation
573 * starts, ends or its progress significantly changes.
574 * @return an instance of a robust polynomial estimator.
575 */
576 public static PolynomialRobustEstimator create(final PolynomialRobustEstimatorListener listener) {
577 return create(listener, DEFAULT_ROBUST_METHOD);
578 }
579
580 /**
581 * Creates a robust polynomial estimator using provided degree, evaluations
582 * and default method.
583 *
584 * @param degree degree of polynomial to be estimated.
585 * @param evaluations collection of polynomial evaluations.
586 * @return an instance of a robust polynomial estimator.
587 * @throws IllegalArgumentException if provided degree is less than 1.
588 */
589 public static PolynomialRobustEstimator create(final int degree, final List<PolynomialEvaluation> evaluations) {
590 return create(degree, evaluations, DEFAULT_ROBUST_METHOD);
591 }
592
593 /**
594 * Creates a robust polynomial estimator using provided degree, listener
595 * and default method.
596 *
597 * @param degree degree of polynomial to be estimated.
598 * @param listener listener to be notified of events such as when estimation
599 * starts, ends or its progress significantly changes.
600 * @return an instance of a robust polynomial estimator.
601 * @throws IllegalArgumentException if provided degree is less than 1.
602 */
603 public static PolynomialRobustEstimator create(final int degree, final PolynomialRobustEstimatorListener listener) {
604 return create(degree, listener, DEFAULT_ROBUST_METHOD);
605 }
606
607 /**
608 * Creates a robust polynomial estimator using provided evaluations,
609 * listener and default method.
610 *
611 * @param evaluations collection of polynomial evaluations.
612 * @param listener listener to be notified of events such as when estimation
613 * starts, ends or its progress significantly changes.
614 * @return an instance of a robust polynomial estimator.
615 */
616 public static PolynomialRobustEstimator create(
617 final List<PolynomialEvaluation> evaluations, final PolynomialRobustEstimatorListener listener) {
618 return create(evaluations, listener, DEFAULT_ROBUST_METHOD);
619 }
620
621 /**
622 * Creates a robust polynomial estimator using provided degree, evaluations,
623 * listener and default method.
624 *
625 * @param degree degree of polynomial to be estimated.
626 * @param evaluations collection of polynomial evaluations.
627 * @param listener listener to be notified of events such as when estimation
628 * starts, ends or its progress significantly changes.
629 * @return an instance of a robust polynomial estimator.
630 * @throws IllegalArgumentException if provided degree is less than 1.
631 */
632 public static PolynomialRobustEstimator create(
633 final int degree, final List<PolynomialEvaluation> evaluations,
634 final PolynomialRobustEstimatorListener listener) {
635 return create(degree, evaluations, listener, DEFAULT_ROBUST_METHOD);
636 }
637
638 /**
639 * Creates a robust polynomial estimator using provided method.
640 *
641 * @param method method of a robust polynomial estimator.
642 * @return an instance of a robust polynomial estimator.
643 */
644 public static PolynomialRobustEstimator create(final RobustEstimatorMethod method) {
645 return switch (method) {
646 case RANSAC -> new RANSACPolynomialRobustEstimator();
647 case LMEDS -> new LMedSPolynomialRobustEstimator();
648 case MSAC -> new MSACPolynomialRobustEstimator();
649 case PROMEDS -> new PROMedSPolynomialRobustEstimator();
650 default -> new PROSACPolynomialRobustEstimator();
651 };
652 }
653
654 /**
655 * Creates a robust polynomial estimator using provided degree and method.
656 *
657 * @param degree degree of polynomial to be estimated.
658 * @param method method of a robust polynomial estimator.
659 * @return an instance of a robust polynomial estimator.
660 * @throws IllegalArgumentException if provided degree is less than 1.
661 */
662 public static PolynomialRobustEstimator create(final int degree, final RobustEstimatorMethod method) {
663 return switch (method) {
664 case RANSAC -> new RANSACPolynomialRobustEstimator(degree);
665 case LMEDS -> new LMedSPolynomialRobustEstimator(degree);
666 case MSAC -> new MSACPolynomialRobustEstimator(degree);
667 case PROMEDS -> new PROMedSPolynomialRobustEstimator(degree);
668 default -> new PROSACPolynomialRobustEstimator(degree);
669 };
670 }
671
672 /**
673 * Creates a robust polynomial estimator using provided evaluations and
674 * method.
675 *
676 * @param evaluations collection of polynomial evaluations.
677 * @param method method of a robust polynomial estimator.
678 * @return an instance of a robust polynomial estimator.
679 */
680 public static PolynomialRobustEstimator create(
681 final List<PolynomialEvaluation> evaluations, final RobustEstimatorMethod method) {
682 return switch (method) {
683 case RANSAC -> new RANSACPolynomialRobustEstimator(evaluations);
684 case LMEDS -> new LMedSPolynomialRobustEstimator(evaluations);
685 case MSAC -> new MSACPolynomialRobustEstimator(evaluations);
686 case PROMEDS -> new PROMedSPolynomialRobustEstimator(evaluations);
687 default -> new PROSACPolynomialRobustEstimator(evaluations);
688 };
689 }
690
691 /**
692 * Creates a robust polynomial estimator using provided listener and method.
693 *
694 * @param listener listener to be notified of events such as when estimation
695 * starts, ends or its progress significantly changes.
696 * @param method method of a robust polynomial estimator.
697 * @return an instance of a robust polynomial estimator.
698 */
699 public static PolynomialRobustEstimator create(
700 final PolynomialRobustEstimatorListener listener, final RobustEstimatorMethod method) {
701 return switch (method) {
702 case RANSAC -> new RANSACPolynomialRobustEstimator(listener);
703 case LMEDS -> new LMedSPolynomialRobustEstimator(listener);
704 case MSAC -> new MSACPolynomialRobustEstimator(listener);
705 case PROMEDS -> new PROMedSPolynomialRobustEstimator(listener);
706 default -> new PROSACPolynomialRobustEstimator(listener);
707 };
708 }
709
710 /**
711 * Creates a robust polynomial estimator using provided degree, evaluations
712 * and method.
713 *
714 * @param degree degree of polynomial to be estimated.
715 * @param evaluations collection of polynomial evaluations.
716 * @param method method of a robust polynomial estimator.
717 * @return an instance of a robust polynomial estimator.
718 * @throws IllegalArgumentException if provided degree is less than 1.
719 */
720 public static PolynomialRobustEstimator create(
721 final int degree, final List<PolynomialEvaluation> evaluations, final RobustEstimatorMethod method) {
722 return switch (method) {
723 case RANSAC -> new RANSACPolynomialRobustEstimator(degree, evaluations);
724 case LMEDS -> new LMedSPolynomialRobustEstimator(degree, evaluations);
725 case MSAC -> new MSACPolynomialRobustEstimator(degree, evaluations);
726 case PROMEDS -> new PROMedSPolynomialRobustEstimator(degree, evaluations);
727 default -> new PROSACPolynomialRobustEstimator(degree, evaluations);
728 };
729 }
730
731 /**
732 * Creates a robust polynomial estimator using provided degree, listener
733 * and method.
734 *
735 * @param degree degree of polynomial to be estimated.
736 * @param listener listener to be notified of events such as when estimation
737 * starts, ends or its progress significantly changes.
738 * @param method method of a robust polynomial estimator.
739 * @return an instance of a robust polynomial estimator.
740 * @throws IllegalArgumentException if provided degree is less than 1.
741 */
742 public static PolynomialRobustEstimator create(
743 final int degree, final PolynomialRobustEstimatorListener listener, final RobustEstimatorMethod method) {
744 return switch (method) {
745 case RANSAC -> new RANSACPolynomialRobustEstimator(degree, listener);
746 case LMEDS -> new LMedSPolynomialRobustEstimator(degree, listener);
747 case MSAC -> new MSACPolynomialRobustEstimator(degree, listener);
748 case PROMEDS -> new PROMedSPolynomialRobustEstimator(degree, listener);
749 default -> new PROSACPolynomialRobustEstimator(degree, listener);
750 };
751 }
752
753 /**
754 * Creates a robust polynomial estimator using provided evaluations,
755 * listener and method.
756 *
757 * @param evaluations collection of polynomial evaluations.
758 * @param listener listener to be notified of events such as when estimation
759 * starts, ends or its progress significantly changes.
760 * @param method method of a robust polynomial estimator.
761 * @return an instance of a robust polynomial estimator.
762 */
763 public static PolynomialRobustEstimator create(
764 final List<PolynomialEvaluation> evaluations, final PolynomialRobustEstimatorListener listener,
765 final RobustEstimatorMethod method) {
766 return switch (method) {
767 case RANSAC -> new RANSACPolynomialRobustEstimator(evaluations, listener);
768 case LMEDS -> new LMedSPolynomialRobustEstimator(evaluations, listener);
769 case MSAC -> new MSACPolynomialRobustEstimator(evaluations, listener);
770 case PROMEDS -> new PROMedSPolynomialRobustEstimator(evaluations, listener);
771 default -> new PROSACPolynomialRobustEstimator(evaluations, listener);
772 };
773 }
774
775 /**
776 * Creates a robust polynomial estimator using provided degree, evaluations,
777 * listener and method.
778 *
779 * @param degree degree of polynomial to be estimated.
780 * @param evaluations collection of polynomial evaluations.
781 * @param listener listener to be notified of events such as when estimation
782 * starts, ends or its progress significantly changes.
783 * @param method method of a robust polynomial estimator.
784 * @return an instance of a robust polynomial estimator.
785 * @throws IllegalArgumentException if provided degree is less than 1.
786 */
787 public static PolynomialRobustEstimator create(
788 final int degree, final List<PolynomialEvaluation> evaluations,
789 final PolynomialRobustEstimatorListener listener, final RobustEstimatorMethod method) {
790 return switch (method) {
791 case RANSAC -> new RANSACPolynomialRobustEstimator(degree, evaluations, listener);
792 case LMEDS -> new LMedSPolynomialRobustEstimator(degree, evaluations, listener);
793 case MSAC -> new MSACPolynomialRobustEstimator(degree, evaluations, listener);
794 case PROMEDS -> new PROMedSPolynomialRobustEstimator(degree, evaluations, listener);
795 default -> new PROSACPolynomialRobustEstimator(degree, evaluations, listener);
796 };
797 }
798
799 /**
800 * Computes geometric or algebraic distance between provided polynomial
801 * and evaluation.
802 *
803 * @param eval polynomial evaluation.
804 * @param polynomial polynomial.
805 * @return distance.
806 */
807 protected double getDistance(final PolynomialEvaluation eval, final Polynomial polynomial) {
808 if (useGeometricDistance) {
809 return getGeometricOrAlgebraicDistance(eval, polynomial);
810 } else {
811 return getAlgebraicDistance(eval, polynomial);
812 }
813 }
814
815 /**
816 * Computes algebraic distance between provided polynomial and evaluation.
817 *
818 * @param eval polynomial evaluation.
819 * @param polynomial polynomial.
820 * @return algebraic distance.
821 */
822 protected double getAlgebraicDistance(final PolynomialEvaluation eval, final Polynomial polynomial) {
823 return switch (eval.getType()) {
824 case DIRECT_EVALUATION -> getAlgebraicDistance((DirectPolynomialEvaluation) eval, polynomial);
825 case DERIVATIVE_EVALUATION -> getAlgebraicDistance((DerivativePolynomialEvaluation) eval, polynomial);
826 case INTEGRAL_EVALUATION -> getAlgebraicDistance((IntegralPolynomialEvaluation) eval, polynomial);
827 case INTEGRAL_INTERVAL -> getAlgebraicDistance((IntegralIntervalPolynomialEvaluation) eval, polynomial);
828 };
829 }
830
831 /**
832 * Computes algebraic distance of between provided polynomial and direct
833 * evaluation.
834 *
835 * @param eval direct polynomial evaluation.
836 * @param polynomial polynomial.
837 * @return algebraic distance.
838 */
839 protected double getAlgebraicDistance(final DirectPolynomialEvaluation eval, final Polynomial polynomial) {
840 final var x = eval.getX();
841 final var y1 = eval.getEvaluation();
842 final var y2 = polynomial.evaluate(x);
843 return Math.abs(y2 - y1);
844 }
845
846 /**
847 * Computes algebraic distance of a derivative between provided polynomial
848 * and evaluation.
849 *
850 * @param eval derivative polynomial evaluation.
851 * @param polynomial polynomial.
852 * @return algebraic distance.
853 */
854 protected double getAlgebraicDistance(final DerivativePolynomialEvaluation eval, final Polynomial polynomial) {
855 final var x = eval.getX();
856 final var order = eval.getDerivativeOrder();
857 final var d1 = eval.getEvaluation();
858 final var d2 = polynomial.evaluateNthDerivative(x, order);
859 return Math.abs(d2 - d1);
860 }
861
862 /**
863 * Computes algebraic distance of an integral between provided polynomial
864 * and evaluation.
865 *
866 * @param eval integration polynomial evaluation.
867 * @param polynomial polynomial.
868 * @return algebraic distance.
869 */
870 protected double getAlgebraicDistance(final IntegralPolynomialEvaluation eval, final Polynomial polynomial) {
871 final var x = eval.getX();
872 final var order = eval.getIntegralOrder();
873 final var constants = eval.getConstants();
874 final var i1 = eval.getEvaluation();
875 final var i2 = polynomial.nthIntegrationAndReturnNew(order, constants).evaluate(x);
876 return Math.abs(i2 - i1);
877 }
878
879 /**
880 * Computes algebraic distance of an integration interval between provided
881 * polynomial and evaluation.
882 *
883 * @param eval integration interval polynomial evaluation.
884 * @param polynomial polynomial.
885 * @return algebraic distance.
886 */
887 protected double getAlgebraicDistance(final IntegralIntervalPolynomialEvaluation eval,
888 final Polynomial polynomial) {
889 final var startX = eval.getStartX();
890 final var endX = eval.getEndX();
891 final var order = eval.getIntegralOrder();
892 final var constants = eval.getConstants();
893 final var i1 = eval.getEvaluation();
894 final var i2 = polynomial.nthOrderIntegrateInterval(startX, endX, order, constants);
895 return Math.abs(i2 - i1);
896 }
897
898 /**
899 * Commutes distance of evaluation respect to provided polynomial in
900 * a geometric sense if evaluation is direct, otherwise returns algebraic
901 * distance.
902 *
903 * @param eval polynomial evaluation.
904 * @param polynomial polynomial.
905 * @return geometric distance for direct evaluation or algebraic distance
906 * otherwise.
907 */
908 protected double getGeometricOrAlgebraicDistance(final PolynomialEvaluation eval, final Polynomial polynomial) {
909 if (eval.getType() == PolynomialEvaluationType.DIRECT_EVALUATION) {
910 return getGeometricDistance((DirectPolynomialEvaluation) eval, polynomial);
911 } else {
912 return getAlgebraicDistance(eval, polynomial);
913 }
914 }
915
916 /**
917 * Computes distance of evaluation respect to provided polynomial in
918 * a geometric sense by computing a tangent line to polynomial at point x
919 * and comparing the distance of such line to provided evaluation point.
920 *
921 * @param eval polynomial evaluation.
922 * @param polynomial polynomial.
923 * @return geometric distance.
924 */
925 protected double getGeometricDistance(final DirectPolynomialEvaluation eval, final Polynomial polynomial) {
926 final var x = eval.getX();
927 final var y1 = eval.getEvaluation();
928 final var y2 = polynomial.evaluate(x);
929
930 final var slope = polynomial.evaluateDerivative(x);
931 final double a;
932 final double b;
933 final double c;
934 if (Math.abs(slope) > 1.0) {
935 a = 1.0;
936 b = -1.0 / slope;
937 c = -x + y2 / slope;
938 } else {
939 a = -slope;
940 b = 1.0;
941 c = slope * x - y2;
942 }
943
944 final var num = x * a + y1 * b + c;
945 final var den = Math.sqrt(a * a + b * b);
946
947 return Math.abs(num / den);
948 }
949
950 /**
951 * Sets list of polynomial evaluations.
952 * This method does not check whether estimator is locked.
953 *
954 * @param evaluations list of polynomial evaluations to estimate polynomial.
955 * @throws IllegalArgumentException if provided list of polynomials is null
956 * or too small.
957 */
958 private void internalSetEvaluations(final List<PolynomialEvaluation> evaluations) {
959 if (evaluations == null || evaluations.size() < getMinNumberOfEvaluations()) {
960 throw new IllegalArgumentException();
961 }
962 this.evaluations = evaluations;
963 }
964 }