LMSEPolynomialEstimator.java

/*
 * Copyright (C) 2016 Alberto Irurueta Carro (alberto@irurueta.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.irurueta.numerical.polynomials.estimators;

import com.irurueta.algebra.AlgebraException;
import com.irurueta.algebra.Matrix;
import com.irurueta.algebra.Utils;
import com.irurueta.numerical.LockedException;
import com.irurueta.numerical.NotReadyException;
import com.irurueta.numerical.polynomials.Polynomial;

import java.util.List;

/**
 * This class defines an LMSE (Least Mean Square Error) estimator of a
 * polynomial of a given degree using points where polynomials (or its
 * derivatives or integrals) are evaluated.
 */
public class LMSEPolynomialEstimator extends PolynomialEstimator {

    /**
     * Indicates if by default an LMSE (Least Mean Square Error) solution is
     * allowed if more evaluations than the required minimum are provided.
     */
    public static final boolean DEFAULT_ALLOW_LMSE_SOLUTION = false;

    /**
     * Indicates if an LMSE (Least Mean Square Error) solution is allowed if
     * more evaluations than the required minimum are provided. If false, the
     * exceeding evaluations are ignored, and only the first minimum required
     * are used.
     */
    private boolean allowLMSESolution;

    /**
     * Constructor.
     */
    public LMSEPolynomialEstimator() {
        super();
        allowLMSESolution = DEFAULT_ALLOW_LMSE_SOLUTION;
    }

    /**
     * Constructor.
     *
     * @param degree degree of polynomial to be estimated.
     * @throws IllegalArgumentException if provided degree is less than 1.
     */
    public LMSEPolynomialEstimator(final int degree) {
        super(degree);
        allowLMSESolution = DEFAULT_ALLOW_LMSE_SOLUTION;
    }

    /**
     * Constructor.
     *
     * @param evaluations collection of polynomial evaluations.
     */
    public LMSEPolynomialEstimator(final List<PolynomialEvaluation> evaluations) {
        super(evaluations);
        allowLMSESolution = DEFAULT_ALLOW_LMSE_SOLUTION;
    }

    /**
     * Constructor.
     *
     * @param listener listener to be notified of events.
     */
    public LMSEPolynomialEstimator(final PolynomialEstimatorListener listener) {
        super(listener);
        allowLMSESolution = DEFAULT_ALLOW_LMSE_SOLUTION;
    }

    /**
     * Constructor.
     *
     * @param degree      degree of polynomial to be estimated.
     * @param evaluations collection of polynomial evaluations.
     * @throws IllegalArgumentException if provided degree is less than 1.
     */
    public LMSEPolynomialEstimator(final int degree, final List<PolynomialEvaluation> evaluations) {
        super(degree, evaluations);
        allowLMSESolution = DEFAULT_ALLOW_LMSE_SOLUTION;
    }

    /**
     * Constructor.
     *
     * @param degree   degree of polynomial to be estimated.
     * @param listener listener to be notified of events.
     * @throws IllegalArgumentException if provided degree is less than 1.
     */
    public LMSEPolynomialEstimator(final int degree, final PolynomialEstimatorListener listener) {
        super(degree, listener);
        allowLMSESolution = DEFAULT_ALLOW_LMSE_SOLUTION;
    }

    /**
     * Constructor.
     *
     * @param evaluations collection of polynomial evaluations.
     * @param listener    listener to be notified of events.
     */
    public LMSEPolynomialEstimator(
            final List<PolynomialEvaluation> evaluations, final PolynomialEstimatorListener listener) {
        super(evaluations, listener);
        allowLMSESolution = DEFAULT_ALLOW_LMSE_SOLUTION;
    }

    /**
     * Constructor.
     *
     * @param degree      degree of polynomial to be estimated.
     * @param evaluations collection of polynomial evaluations.
     * @param listener    listener to be notified of events.
     * @throws IllegalArgumentException if provided degree is less than 1.
     */
    public LMSEPolynomialEstimator(
            final int degree, final List<PolynomialEvaluation> evaluations,
            final PolynomialEstimatorListener listener) {
        super(degree, evaluations, listener);
        allowLMSESolution = DEFAULT_ALLOW_LMSE_SOLUTION;
    }

    /**
     * Indicates if an LMSE (Least Mean Square Error) solution is allowed if
     * more evaluations than the required minimum are provided. If false, the
     * exceeding evaluations are ignored, and only the first minimum required
     * are used.
     *
     * @return true if LMSE solution is allowed, false otherwise.
     */
    public boolean isLMSESolutionAllowed() {
        return allowLMSESolution;
    }

    /**
     * Specified if an LMSE (Least Mean Square Error) solution is allowed if
     * more evaluations than the required minimum are provided. If false, the
     * exceeding evaluations are ignored, and only the first minimum required
     * are used.
     *
     * @param allowed true if LMSE solution is allowed, false otherwise.
     * @throws LockedException if estimator is locked.
     */
    public void setLMSESolutionAllowed(final boolean allowed) throws LockedException {
        if (isLocked()) {
            throw new LockedException();
        }
        allowLMSESolution = allowed;
    }

    /**
     * Estimates a polynomial based on provided evaluations.
     *
     * @return estimated polynomial.
     * @throws LockedException               if estimator is locked.
     * @throws NotReadyException             if estimator is not ready.
     * @throws PolynomialEstimationException if polynomial estimation fails.
     */
    @SuppressWarnings("DuplicatedCode")
    @Override
    public Polynomial estimate() throws LockedException, NotReadyException, PolynomialEstimationException {
        if (isLocked()) {
            throw new LockedException();
        }
        if (!isReady()) {
            throw new NotReadyException();
        }

        try {
            locked = true;
            if (listener != null) {
                listener.onEstimateStart(this);
            }

            final var minNumberOfEvaluations = getMinNumberOfEvaluations();

            final var a = new Matrix(evaluations.size(), degree + 1);
            final var b = new Matrix(evaluations.size(), 1);

            var counter = 0;
            for (var evaluation : evaluations) {
                switch (evaluation.getType()) {
                    case DIRECT_EVALUATION:
                        fillDirectEvaluation((DirectPolynomialEvaluation) evaluation, a, b, counter);
                        break;
                    case DERIVATIVE_EVALUATION:
                        fillDerivativeEvaluation((DerivativePolynomialEvaluation) evaluation, a, b, counter);
                        break;
                    case INTEGRAL_EVALUATION:
                        fillIntegralEvaluation((IntegralPolynomialEvaluation) evaluation, a, b, counter);
                        break;
                    case INTEGRAL_INTERVAL:
                        fillIntegralIntervalEvaluation((IntegralIntervalPolynomialEvaluation) evaluation, a, b,
                                counter);
                        break;
                    default:
                        continue;
                }

                normalize(a, b, counter);
                counter++;

                if (!isLMSESolutionAllowed() && counter >= minNumberOfEvaluations) {
                    break;
                }
            }

            final var params = Utils.solve(a, b);

            final var result = new Polynomial(params.toArray());

            if (listener != null) {
                listener.onEstimateEnd(this);
            }

            return result;
        } catch (final AlgebraException e) {
            throw new PolynomialEstimationException(e);
        } finally {
            locked = false;
        }
    }

    /**
     * Returns type of polynomial estimator.
     *
     * @return type of polynomial estimator.
     */
    @Override
    public PolynomialEstimatorType getType() {
        return PolynomialEstimatorType.LMSE_POLYNOMIAL_ESTIMATOR;
    }
}