View Javadoc
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.algebra.AlgebraException;
19  import com.irurueta.algebra.Matrix;
20  import com.irurueta.algebra.Utils;
21  import com.irurueta.numerical.LockedException;
22  import com.irurueta.numerical.NotReadyException;
23  import com.irurueta.numerical.polynomials.Polynomial;
24  
25  import java.util.List;
26  
27  /**
28   * This class defines an LMSE (Least Mean Square Error) estimator of a
29   * polynomial of a given degree using points where polynomials (or its
30   * derivatives or integrals) are evaluated.
31   */
32  public class LMSEPolynomialEstimator extends PolynomialEstimator {
33  
34      /**
35       * Indicates if by default an LMSE (Least Mean Square Error) solution is
36       * allowed if more evaluations than the required minimum are provided.
37       */
38      public static final boolean DEFAULT_ALLOW_LMSE_SOLUTION = false;
39  
40      /**
41       * Indicates if an LMSE (Least Mean Square Error) solution is allowed if
42       * more evaluations than the required minimum are provided. If false, the
43       * exceeding evaluations are ignored, and only the first minimum required
44       * are used.
45       */
46      private boolean allowLMSESolution;
47  
48      /**
49       * Constructor.
50       */
51      public LMSEPolynomialEstimator() {
52          super();
53          allowLMSESolution = DEFAULT_ALLOW_LMSE_SOLUTION;
54      }
55  
56      /**
57       * Constructor.
58       *
59       * @param degree degree of polynomial to be estimated.
60       * @throws IllegalArgumentException if provided degree is less than 1.
61       */
62      public LMSEPolynomialEstimator(final int degree) {
63          super(degree);
64          allowLMSESolution = DEFAULT_ALLOW_LMSE_SOLUTION;
65      }
66  
67      /**
68       * Constructor.
69       *
70       * @param evaluations collection of polynomial evaluations.
71       */
72      public LMSEPolynomialEstimator(final List<PolynomialEvaluation> evaluations) {
73          super(evaluations);
74          allowLMSESolution = DEFAULT_ALLOW_LMSE_SOLUTION;
75      }
76  
77      /**
78       * Constructor.
79       *
80       * @param listener listener to be notified of events.
81       */
82      public LMSEPolynomialEstimator(final PolynomialEstimatorListener listener) {
83          super(listener);
84          allowLMSESolution = DEFAULT_ALLOW_LMSE_SOLUTION;
85      }
86  
87      /**
88       * Constructor.
89       *
90       * @param degree      degree of polynomial to be estimated.
91       * @param evaluations collection of polynomial evaluations.
92       * @throws IllegalArgumentException if provided degree is less than 1.
93       */
94      public LMSEPolynomialEstimator(final int degree, final List<PolynomialEvaluation> evaluations) {
95          super(degree, evaluations);
96          allowLMSESolution = DEFAULT_ALLOW_LMSE_SOLUTION;
97      }
98  
99      /**
100      * Constructor.
101      *
102      * @param degree   degree of polynomial to be estimated.
103      * @param listener listener to be notified of events.
104      * @throws IllegalArgumentException if provided degree is less than 1.
105      */
106     public LMSEPolynomialEstimator(final int degree, final PolynomialEstimatorListener listener) {
107         super(degree, listener);
108         allowLMSESolution = DEFAULT_ALLOW_LMSE_SOLUTION;
109     }
110 
111     /**
112      * Constructor.
113      *
114      * @param evaluations collection of polynomial evaluations.
115      * @param listener    listener to be notified of events.
116      */
117     public LMSEPolynomialEstimator(
118             final List<PolynomialEvaluation> evaluations, final PolynomialEstimatorListener listener) {
119         super(evaluations, listener);
120         allowLMSESolution = DEFAULT_ALLOW_LMSE_SOLUTION;
121     }
122 
123     /**
124      * Constructor.
125      *
126      * @param degree      degree of polynomial to be estimated.
127      * @param evaluations collection of polynomial evaluations.
128      * @param listener    listener to be notified of events.
129      * @throws IllegalArgumentException if provided degree is less than 1.
130      */
131     public LMSEPolynomialEstimator(
132             final int degree, final List<PolynomialEvaluation> evaluations,
133             final PolynomialEstimatorListener listener) {
134         super(degree, evaluations, listener);
135         allowLMSESolution = DEFAULT_ALLOW_LMSE_SOLUTION;
136     }
137 
138     /**
139      * Indicates if an LMSE (Least Mean Square Error) solution is allowed if
140      * more evaluations than the required minimum are provided. If false, the
141      * exceeding evaluations are ignored, and only the first minimum required
142      * are used.
143      *
144      * @return true if LMSE solution is allowed, false otherwise.
145      */
146     public boolean isLMSESolutionAllowed() {
147         return allowLMSESolution;
148     }
149 
150     /**
151      * Specified if an LMSE (Least Mean Square Error) solution is allowed if
152      * more evaluations than the required minimum are provided. If false, the
153      * exceeding evaluations are ignored, and only the first minimum required
154      * are used.
155      *
156      * @param allowed true if LMSE solution is allowed, false otherwise.
157      * @throws LockedException if estimator is locked.
158      */
159     public void setLMSESolutionAllowed(final boolean allowed) throws LockedException {
160         if (isLocked()) {
161             throw new LockedException();
162         }
163         allowLMSESolution = allowed;
164     }
165 
166     /**
167      * Estimates a polynomial based on provided evaluations.
168      *
169      * @return estimated polynomial.
170      * @throws LockedException               if estimator is locked.
171      * @throws NotReadyException             if estimator is not ready.
172      * @throws PolynomialEstimationException if polynomial estimation fails.
173      */
174     @SuppressWarnings("DuplicatedCode")
175     @Override
176     public Polynomial estimate() throws LockedException, NotReadyException, PolynomialEstimationException {
177         if (isLocked()) {
178             throw new LockedException();
179         }
180         if (!isReady()) {
181             throw new NotReadyException();
182         }
183 
184         try {
185             locked = true;
186             if (listener != null) {
187                 listener.onEstimateStart(this);
188             }
189 
190             final var minNumberOfEvaluations = getMinNumberOfEvaluations();
191 
192             final var a = new Matrix(evaluations.size(), degree + 1);
193             final var b = new Matrix(evaluations.size(), 1);
194 
195             var counter = 0;
196             for (var evaluation : evaluations) {
197                 switch (evaluation.getType()) {
198                     case DIRECT_EVALUATION:
199                         fillDirectEvaluation((DirectPolynomialEvaluation) evaluation, a, b, counter);
200                         break;
201                     case DERIVATIVE_EVALUATION:
202                         fillDerivativeEvaluation((DerivativePolynomialEvaluation) evaluation, a, b, counter);
203                         break;
204                     case INTEGRAL_EVALUATION:
205                         fillIntegralEvaluation((IntegralPolynomialEvaluation) evaluation, a, b, counter);
206                         break;
207                     case INTEGRAL_INTERVAL:
208                         fillIntegralIntervalEvaluation((IntegralIntervalPolynomialEvaluation) evaluation, a, b,
209                                 counter);
210                         break;
211                     default:
212                         continue;
213                 }
214 
215                 normalize(a, b, counter);
216                 counter++;
217 
218                 if (!isLMSESolutionAllowed() && counter >= minNumberOfEvaluations) {
219                     break;
220                 }
221             }
222 
223             final var params = Utils.solve(a, b);
224 
225             final var result = new Polynomial(params.toArray());
226 
227             if (listener != null) {
228                 listener.onEstimateEnd(this);
229             }
230 
231             return result;
232         } catch (final AlgebraException e) {
233             throw new PolynomialEstimationException(e);
234         } finally {
235             locked = false;
236         }
237     }
238 
239     /**
240      * Returns type of polynomial estimator.
241      *
242      * @return type of polynomial estimator.
243      */
244     @Override
245     public PolynomialEstimatorType getType() {
246         return PolynomialEstimatorType.LMSE_POLYNOMIAL_ESTIMATOR;
247     }
248 }