All Downloads are FREE. Search and download functionalities are using the official Maven repository.

repicea.simulation.stemtaper.AbstractStemTaperEstimate Maven / Gradle / Ivy

There is a newer version: 1.1.1
Show newest version
/*
 * This file is part of the repicea-foresttools library.
 *
 * Copyright (C) 2009-2014 Mathieu Fortin for Rouge-Epicea
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed with the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE. See the GNU Lesser General Public
 * License for more details.
 *
 * Please see the license at http://www.gnu.org/copyleft/lesser.html.
 */
package repicea.simulation.stemtaper;

import java.security.InvalidParameterException;
import java.util.List;

import repicea.math.Matrix;
import repicea.math.SymmetricMatrix;
import repicea.math.integral.TrapezoidalRule;
import repicea.stats.CentralMomentsSettable;
import repicea.stats.Distribution;
import repicea.stats.distributions.GaussianDistribution;
import repicea.stats.estimates.Estimate;
import repicea.stats.estimates.GaussianEstimate;

/**
 * The StemTaperEstimate class extends the Estimate class. It handles MonteCarlo or maximum likelihood based estimates.
 * likelihood based. The method getMean() and getVariance() are overriden in order to select which super methods apply.
 * The StemTaperEstimate handles the volume integration.
 * @author Mathieu Fortin - March 2012
 * @author Mathieu Fortin - December 2016 (Refactoring)
 */
@SuppressWarnings({ "rawtypes", "serial" })
public abstract class AbstractStemTaperEstimate extends Estimate implements CentralMomentsSettable  {
		
	private List heights;
	private Estimate volumeEstimate;


	/**
	 * Constructor.
	 * @param computedHeights a List instance containing the heights (m) of the cross sections
	 */
	public AbstractStemTaperEstimate(List computedHeights) {
		super(new GaussianDistribution(null, null));
		estimatorType = EstimatorType.LikelihoodBased;
		heights = computedHeights;
	}

	
	/**
	 * This method post processes the predictions. If a particular height appears twice in the originalHeightsToEvaluate
	 * member, the matrix row (and column) corresponding to this heights is doubled. 
	 * @param originalPredictions the original predictions (Matrix instance)
	 * @return the post processing predictions (also Matrix instance)
	 */
	private Matrix reshapeMatrixAccordingToSegments(StemTaperSegmentList segments, Matrix originalPredictions) {
		List originalHeightsToEvaluate = segments.getHeights();
		
		if (originalHeightsToEvaluate.size() == heights.size() && heights.containsAll(originalHeightsToEvaluate)) {
			return originalPredictions;
		} else {
			Matrix outputMatrix;
			boolean isColumnVector = false;
			if (originalPredictions.isColumnVector()) {
				isColumnVector = true;
				outputMatrix = new Matrix(originalHeightsToEvaluate.size(), 1);
			} else if (originalPredictions.isSquare()) {
				outputMatrix = new Matrix(originalHeightsToEvaluate.size(), originalHeightsToEvaluate.size());
			} else {
				throw new InvalidParameterException("Matrix originalPredictions is not square nor a row vector!");
			}
			int i_index, j_index;
			for (int i = 0; i < outputMatrix.m_iRows; i++) {
				i_index = heights.indexOf(originalHeightsToEvaluate.get(i));
				if (isColumnVector) {
					j_index = 0;
					outputMatrix.setValueAt(i, 0, originalPredictions.getValueAt(i_index, j_index));
				} else {		// is square then
					for (int j = i; j < outputMatrix.m_iCols; j++) {
						j_index = heights.indexOf(originalHeightsToEvaluate.get(j));
						outputMatrix.setValueAt(i, j, originalPredictions.getValueAt(i_index, j_index));
						outputMatrix.setValueAt(j, i, originalPredictions.getValueAt(i_index, j_index));
					}
				}
			}
			return outputMatrix;
		}
	}
	

	/**
	 * This method returns a volume estimate from the integrated taper all along the heights contained
	 * in the heights member.
	 * @return an Estimate instance 
	 */
	public Estimate getVolumeEstimate() {
		return getVolumeEstimate(null);
	}
	
	
	/**
	 * This method returns a volume estimate from the integrated taper all along the selected segments.
	 * @param segments a StemTaperSegmentList that represents the selected taper (can be null, in such case the integration is carried out
	 * all along the heights, by default)
	 * @return an Estimate instance 
	 */
	public Estimate getVolumeEstimate(StemTaperSegmentList segments) {
		if (segments == null) {	// means it has to be integrated all along the sections
			if (volumeEstimate == null) {
				segments = getDefaultSegments();
				volumeEstimate = getVolumeEstimateForTheseSegments(segments);
			} 
			return volumeEstimate;
		} else {
			return getVolumeEstimateForTheseSegments(segments);
		}
	}

	
	private StemTaperSegmentList getDefaultSegments() {
		StemTaperSegmentList segments = new StemTaperSegmentList();
		segments.add(new StemTaperSegment(new TrapezoidalRule(heights)));
		return segments;
	}


	/**
	 * This method returns a volume estimate from the integrated taper all along the selected segments.
	 * @param segments a StemTaperSegmentList instance
	 * @return an Estimate instance 
	 */
	private Estimate getVolumeEstimateForTheseSegments(StemTaperSegmentList segments) {
		if (!heights.containsAll(segments.getHeights())) {
			throw new InvalidParameterException("There is a mismatch between the requested sections and the heights that have been computed!");
		}

		Matrix weights = new Matrix(segments.getWeightsAcrossSegments());
		Matrix rescalingFactors = new Matrix(segments.getRescalingFactorsAcrossSegments());
		Matrix volumeFactor = rescalingFactors.elementWiseMultiply(weights).scalarMultiply(getScalingFactor());
		Matrix varianceFactor = volumeFactor.multiply(volumeFactor.transpose());

		Estimate result = new GaussianEstimate();
		((GaussianEstimate) result).setMean(null);
		((GaussianEstimate) result).setVariance(null);

		SymmetricMatrix variance;

		Matrix taper = getSquaredDiameters(reshapeMatrixAccordingToSegments(segments, getMean()));

		Matrix volumeEstim = taper.elementWiseMultiply(volumeFactor);
		((GaussianEstimate) result).setMean(volumeEstim);
		if (getVariance() != null) {
			variance = getVarianceOfSquaredDiameter(
					SymmetricMatrix.convertToSymmetricIfPossible(
							reshapeMatrixAccordingToSegments(segments, getVariance())));
			Matrix scaledVariance = variance.elementWiseMultiply(varianceFactor);
			if (scaledVariance instanceof SymmetricMatrix) {
				((GaussianEstimate) result).setVariance((SymmetricMatrix) scaledVariance);
			} else {
				throw new UnsupportedOperationException("The scaledVariance object is not a SymmetricMatrix instance!");
			}
		}
		return result;
	}


	/**
	 * This method returns the heights of the cross sections for which the square diameters were predicted.
	 * @return a List of Double instances
	 */
	public List getCrossSectionHeights() {return heights;}
	
	protected abstract Matrix getSquaredDiameters(Matrix predictedDiameters);
	
	protected abstract SymmetricMatrix getVarianceOfSquaredDiameter(SymmetricMatrix variancePredictedDiameters);
	
	protected abstract double getScalingFactor();
	
	@Override
	public void setMean(Matrix mean) {
		((GaussianDistribution) getDistribution()).setMean(mean);
	}

	@Override
	public void setVariance(SymmetricMatrix variance) {
		((GaussianDistribution) getDistribution()).setVariance(variance);
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy