teichmann@5863: /* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde teichmann@5863: * Software engineering by Intevation GmbH teichmann@5863: * teichmann@5994: * This file is Free Software under the GNU AGPL (>=v3) teichmann@5863: * and comes with ABSOLUTELY NO WARRANTY! Check out the teichmann@5994: * documentation coming with Dive4Elements River for details. teichmann@5863: */ teichmann@5863: teichmann@5831: package org.dive4elements.river.artifacts.model.sq; sascha@3188: teichmann@5831: import org.dive4elements.river.artifacts.math.fitting.Function; teichmann@6761: import org.dive4elements.river.artifacts.math.fitting.Linear; sascha@3188: sascha@3188: import java.util.ArrayList; sascha@3188: import java.util.List; sascha@3188: sascha@3188: import org.apache.commons.math.MathException; sascha@3188: sascha@3188: import org.apache.commons.math.optimization.fitting.CurveFitter; sascha@3188: teichmann@6761: import org.apache.commons.math.optimization.general.AbstractLeastSquaresOptimizer; teichmann@6761: import org.apache.commons.math.optimization.general.GaussNewtonOptimizer; sascha@3188: import org.apache.commons.math.optimization.general.LevenbergMarquardtOptimizer; sascha@3188: sascha@3188: import org.apache.log4j.Logger; sascha@3188: sascha@3188: public class Fitting sascha@3552: implements Outlier.Callback sascha@3188: { teichmann@6761: // XXX: Hack to force linear fitting! teichmann@6761: private static final boolean USE_NON_LINEAR_FITTING = teichmann@6761: Boolean.getBoolean("minfo.sq.fitting.nonlinear"); teichmann@6761: sascha@3188: private static Logger log = Logger.getLogger(Fitting.class); sascha@3188: sascha@3552: public interface Callback { sascha@3552: sascha@3552: void afterIteration( sascha@3552: double [] parameters, sascha@3552: SQ [] measurements, sascha@3552: SQ [] outliers, sascha@3552: double standardDeviation, sascha@3552: double chiSqr); sascha@3552: } // interfacte sascha@3552: sascha@3188: protected Function function; sascha@3188: sascha@3552: protected double [] coeffs; sascha@3188: teichmann@5831: protected org.dive4elements.river.artifacts.math.Function instance; sascha@3552: sascha@3552: protected double stdDevFactor; sascha@3188: protected double chiSqr; sascha@3188: sascha@3552: protected Callback callback; sascha@3188: sascha@3188: public Fitting() { sascha@3188: } sascha@3188: sascha@3188: public Fitting(Function function, double stdDevFactor) { sascha@3552: this(); sascha@3188: this.function = function; sascha@3188: this.stdDevFactor = stdDevFactor; sascha@3188: } sascha@3188: sascha@3188: public Function getFunction() { sascha@3188: return function; sascha@3188: } sascha@3188: sascha@3188: public void setFunction(Function function) { sascha@3188: this.function = function; sascha@3188: } sascha@3188: sascha@3188: public double getStdDevFactor() { sascha@3188: return stdDevFactor; sascha@3188: } sascha@3188: sascha@3188: public void setStdDevFactor(double stdDevFactor) { sascha@3188: this.stdDevFactor = stdDevFactor; sascha@3188: } sascha@3188: sascha@3552: @Override sascha@3566: public void initialize(List sqs) throws MathException { sascha@3552: teichmann@6761: AbstractLeastSquaresOptimizer optimizer = getOptimizer(); sascha@3552: teichmann@6761: CurveFitter cf = new CurveFitter(optimizer); teichmann@6761: double [] values = new double[2]; sascha@3566: for (SQ sq: sqs) { teichmann@6761: values[0] = sq.getQ(); teichmann@6761: values[1] = sq.getS(); teichmann@6761: transformInputValues(values); teichmann@6761: cf.addObservedPoint(values[0], values[1]); sascha@3552: } sascha@3552: sascha@3552: coeffs = cf.fit( sascha@3552: function, function.getInitialGuess()); sascha@3552: teichmann@6761: transformCoeffsBack(coeffs); teichmann@6761: sascha@3552: instance = function.instantiate(coeffs); sascha@3552: teichmann@6761: chiSqr = optimizer.getChiSquare(); teichmann@6761: } teichmann@6761: teichmann@6761: protected Function getFunction(Function function) { teichmann@6761: return USE_NON_LINEAR_FITTING teichmann@6761: ? function teichmann@6761: : Linear.INSTANCE; teichmann@6761: } teichmann@6761: teichmann@6761: protected void transformInputValues(double [] values) { teichmann@6761: if (!USE_NON_LINEAR_FITTING) { teichmann@6761: for (int i = 0; i < values.length; ++i) { teichmann@6761: values[i] = Math.log(values[i]); teichmann@6761: } teichmann@6761: } teichmann@6761: } teichmann@6761: teichmann@6761: protected AbstractLeastSquaresOptimizer getOptimizer() { teichmann@6761: return USE_NON_LINEAR_FITTING teichmann@6761: ? new LevenbergMarquardtOptimizer() teichmann@6761: : new GaussNewtonOptimizer(false); teichmann@6761: } teichmann@6761: teichmann@6761: protected void transformCoeffsBack(double [] coeffs) { teichmann@6761: if (!USE_NON_LINEAR_FITTING && coeffs.length > 0) { teichmann@6761: coeffs[0] = Math.exp(coeffs[0]); teichmann@6761: } sascha@3188: } sascha@3188: sascha@3552: @Override sascha@3552: public double eval(SQ sq) { sascha@3552: double s = instance.value(sq.q); sascha@3552: return sq.s - s; sascha@3552: } sascha@3552: sascha@3552: @Override sascha@3566: public void iterationFinished( sascha@3566: double standardDeviation, sascha@3566: SQ outlier, sascha@3566: List remainings sascha@3566: ) { sascha@3552: if (log.isDebugEnabled()) { sascha@3552: log.debug("iterationFinished ----"); sascha@3552: log.debug(" num remainings: " + remainings.size()); sascha@3566: log.debug(" has outlier: " + outlier != null); sascha@3552: log.debug(" standardDeviation: " + standardDeviation); sascha@3552: log.debug(" Chi^2: " + chiSqr); sascha@3552: log.debug("---- iterationFinished"); sascha@3552: } sascha@3552: callback.afterIteration( sascha@3552: coeffs, sascha@3552: remainings.toArray(new SQ[remainings.size()]), sascha@3566: outlier != null ? new SQ [] { outlier} : new SQ [] {}, sascha@3552: standardDeviation, sascha@3552: chiSqr); sascha@3188: } sascha@3188: sascha@3188: protected static final List onlyValid(List sqs) { sascha@3188: sascha@3188: List good = new ArrayList(sqs.size()); sascha@3188: sascha@3188: for (SQ sq: sqs) { sascha@3188: if (sq.isValid()) { sascha@3188: good.add(sq); sascha@3188: } sascha@3188: } sascha@3188: sascha@3188: return good; sascha@3188: } sascha@3188: rrenkert@5396: public boolean fit(List sqs, String method, Callback callback) { sascha@3188: sascha@3188: sqs = onlyValid(sqs); sascha@3188: sascha@3188: if (sqs.size() < 2) { sascha@3188: log.warn("Too less points for fitting."); sascha@3188: return false; sascha@3188: } sascha@3188: sascha@3552: this.callback = callback; sascha@3188: sascha@3188: try { rrenkert@5396: Outlier.detectOutliers(this, sqs, stdDevFactor, method); sascha@3188: } sascha@3188: catch (MathException me) { sascha@3188: log.warn(me); sascha@3188: return false; sascha@3188: } sascha@3188: sascha@3188: return true; sascha@3188: } sascha@3188: } sascha@3188: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :