package gdsc.smlm.fitting.nonlinear;
import java.util.Arrays;
import gdsc.core.logging.Logger;
/*-----------------------------------------------------------------------------
* GDSC SMLM Software
*
* Copyright (C) 2013 Alex Herbert
* Genome Damage and Stability Centre
* University of Sussex, UK
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*---------------------------------------------------------------------------*/
/**
* Defines the stopping criteria for the {@link gdsc.smlm.fitting.nonlinear.NonLinearFit } class.
*
* <pre>
* {@code
* StoppingCritera sc; // Passed in
*
* double oldError = // ... Initialised;
* sc.initialise(a);
* while (sc.areNotSatisfied())
* {
* // do fitting
* newError = fit(...);
*
* sc.evaluate(oldError, newError, a);
*
* // Set best error and update parameters a
* if (newError < oldError)
* {
* oldError = newError;
* ...
* }
* }
*
* if (sc.areAchieved())
* // ... Do something
* }
* </pre>
*/
public abstract class StoppingCriteria
{
protected Logger log = null;
private int iteration;
protected boolean notSatisfied;
protected boolean areAchieved;
protected double[] bestA;
private int minimumIterations = 0;
private int maximumIterations = 20;
/**
* Called at the start of the fit. It should be used to reset all
* iteration counters.
*
* @param a
* Set of m coefficients for the fit
*/
public void initialise(double[] a)
{
iteration = 0;
notSatisfied = true;
areAchieved = false;
copyCoefficients(a);
}
/**
* Perform a deep copy of the fit coefficients array and store in a variable for later comparison
*
* @param a
*/
protected void copyCoefficients(double[] a)
{
this.bestA = Arrays.copyOf(a, a.length); // Deep copy
}
/**
* Called after each iteration of the fit. If the error value has reduced then
* the class should decide if the fit is good enough (set areAchived to true) and can be
* stopped (set notSatisfied to false).
* <p>
* If the fitting process appears to be failing then notSatisfied can be set to false to stop iterating.
* <p>
* Note that if the error value is higher the NonLinearFit class will not update the fit coefficients.
*
* @param oldError
* Previous error value for the fit
* @param newError
* New error value for the fit
* @param a
* Set of m coefficients for the current best fit (matches whichever error is lowest)
*/
public abstract void evaluate(double oldError, double newError, double[] a);
/**
* Increment the current iteration and set the best coefficient values (using {@link #copyCoefficients(double[]) })
* if the fit was improved.
* <p>
* Sets the notSatisfied flag to false if the maximum number of iterations is reached.
*
* @param a The parameters
* @param improved Flag to indicate if the parameters have improved the fit
*/
protected void increment(double[] a, boolean improved)
{
iteration++;
if (improved)
copyCoefficients(a);
// Stop if the maximum iterations have been reached
if (iteration >= maximumIterations)
notSatisfied = false;
}
/**
* Called after each {@link #evaluate(double, double, double[]) } method to check if the fitting should continue
*
* @return True if the stopping criteria have not been met (i.e. fitting should continue)
*/
public boolean areNotSatisfied()
{
return notSatisfied;
}
/**
* Called once {@link #areNotSatisfied()} returns false to check if the stopping criteria were
* successfully achieved or the fitting was terminated.
*
* @return True if the stopping criteria are achieved
*/
public boolean areAchieved()
{
return areAchieved;
}
/**
* @return the iteration
*/
public int getIteration()
{
return iteration;
}
/**
* @param maximumIterations
* the maximumIterations to set
*/
public void setMaximumIterations(int maximumIterations)
{
this.maximumIterations = maximumIterations;
}
/**
* @return the maximumIterations
*/
public int getMaximumIterations()
{
return maximumIterations;
}
/**
* Use this to pass in a recommended minimum number of iterations to the stopping criteria
* <p>
* Note: Implementing classes may ignore this parameter
*
* @param minimumIterations
* the minimumIterations to set
*/
public void setMinimumIterations(int minimumIterations)
{
this.minimumIterations = minimumIterations;
}
/**
* @return the minimumIterations
*/
public int getMinimumIterations()
{
return minimumIterations;
}
/**
* @param log
* the log to set. Used to output fit evaluations for each iteration
*/
public void setLog(Logger log)
{
this.log = log;
}
/**
* @return the log
*/
public Logger getLog()
{
return log;
}
}