package gdsc.smlm.search; import org.apache.commons.math3.util.FastMath; /*----------------------------------------------------------------------------- * GDSC SMLM Software * * Copyright (C) 2016 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. * * This code is based on the ideas expressed in Numerical Recipes in C++, * The Art of Scientific Computing, Second Edition, W.H. Press, * S.A. Teukolsky, W.T. Vetterling, B.P. Flannery (Cambridge University Press, * Cambridge, 2002). *---------------------------------------------------------------------------*/ /** * Check if converged using a tolerance on the score and/or position change, and the number of iterations */ public class ConvergenceToleranceChecker<T extends Comparable<T>> implements ConvergenceChecker<T> { final public double relative, absolute; final public boolean checkScore, checkSequence; final public int maxIterations; private int iterations = 0; /** * Build an instance with specified thresholds. This only check convergence using the score. * * In order to perform only relative checks, the absolute tolerance * must be set to a negative value. In order to perform only absolute * checks, the relative tolerance must be set to a negative value. * * @param relativeThreshold * relative tolerance threshold * @param absoluteThreshold * absolute tolerance threshold * @throws IllegalArgumentException * if none of the convergence criteria are valid */ public ConvergenceToleranceChecker(double relative, double absolute) { this(relative, absolute, true, false, 0); } /** * Build an instance with specified thresholds. * * In order to perform only relative checks, the absolute tolerance * must be set to a negative value. In order to perform only absolute * checks, the relative tolerance must be set to a negative value. * * @param relativeThreshold * relative tolerance threshold * @param absoluteThreshold * absolute tolerance threshold * @param checkScore * Set to true to check the score * @param checkSequence * Set to true to check the position * @param maxIterations * Set above zero to check the iterations (number of time {@link #converged(Chromosome, Chromosome)} is * called) * @throws IllegalArgumentException * if none of the convergence criteria are valid */ public ConvergenceToleranceChecker(double relative, double absolute, boolean checkScore, boolean checkSequence, int maxIterations) { if (maxIterations < 0) maxIterations = 0; boolean canConverge = maxIterations != 0; if (checkScore || checkSequence) canConverge |= (relative > 0 || absolute > 0); if (!canConverge) noConvergenceCriteria(); this.relative = relative; this.absolute = absolute; this.checkScore = checkScore; this.checkSequence = checkSequence; this.maxIterations = maxIterations; } /** * Called by the constructor if there are no convergence criteria. Sub-classes that provide additional convergence * checks must override this to avoid error. * * @throws IllegalArgumentException * if there are no convergence criteria in the constructor */ protected void noConvergenceCriteria() { throw new IllegalArgumentException("No valid convergence criteria"); } /** * Check if the position has converged * * @param p * Previous * @param c * Current * @return True if converged */ private boolean converged(final double[] p, final double[] c) { for (int i = 0; i < p.length; ++i) { if (!converged(p[i], c[i])) { return false; } } return true; } /** * Check if the value has converged * * @param p * Previous * @param c * Current * @return True if converged */ private boolean converged(final double p, final double c) { final double difference = Math.abs(p - c); final double size = FastMath.max(Math.abs(p), Math.abs(c)); if (difference > size * relative && difference > absolute) { return false; } return true; } /* * (non-Javadoc) * * @see gdsc.smlm.search.ConvergenceChecker#converged(gdsc.smlm.search.ScoreResult, gdsc.smlm.search.ScoreResult) */ public boolean converged(SearchResult<T> previous, SearchResult<T> current) { iterations++; if (maxIterations != 0 && iterations >= maxIterations) return true; if (checkScore && converged(previous.score, current.score)) return true; if (checkSequence && converged(previous.point, current.point)) return true; return false; } private boolean converged(T score, T score2) { return score.compareTo(score2) == 0; } /** * @return the iterations */ public int getIterations() { return iterations; } }