package gdsc.smlm.fitting.nonlinear.gradient; import java.util.Arrays; import gdsc.smlm.function.Gradient1Procedure; import gdsc.smlm.function.Gradient2Function; import gdsc.smlm.function.Gradient2Procedure; import gdsc.smlm.function.PoissonCalculator; import gdsc.smlm.function.ValueProcedure; /*----------------------------------------------------------------------------- * GDSC SMLM Software * * Copyright (C) 2017 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. *---------------------------------------------------------------------------*/ /** * Calculates the Newton-Raphson update vector for a Poisson process using the first and second partial derivatives. * <p> * Ref: Smith et al, (2010). Fast, single-molecule localisation that achieves theoretically minimum uncertainty. * Nature Methods 7, 373-375 (supplementary note), Eq. 12. */ public class NewtonRaphsonGradient2Procedure implements ValueProcedure, Gradient1Procedure, Gradient2Procedure { protected final double[] x; protected final Gradient2Function func; /** * The number of gradients */ public final int n; /** * The first derivative of the Poisson log likelihood with respect to each parameter */ public final double[] d1; /** * The second derivative of the Poisson log likelihood with respect to each parameter */ public final double[] d2; /** Counter */ protected int k; /** * The value of the function. This is updated by calls to {@link #computeValue(double[])}, * {@link #computeFirstDerivative(double[])}, {@link #computeUpdate(double[])} */ public final double[] u; /** * @param x * Data to fit (must be positive, i.e. the value of a Poisson process) * @param func * Gradient function (must produce a strictly positive value, i.e. the mean of a Poisson process) */ public NewtonRaphsonGradient2Procedure(final double[] x, final Gradient2Function func) { this.x = x; this.u = new double[x.length]; this.func = func; this.n = func.getNumberOfGradients(); d1 = new double[n]; d2 = new double[n]; } /** * Calculates the Newton-Raphson update vector for a Poisson process as the first derivative divided by the second * derivative. * * @param a * Set of coefficients for the function * @return the update vector of the function's parameters */ public double[] computeUpdate(final double[] a) { k = 0; reset2(); func.initialise2(a); func.forEach((Gradient2Procedure) this); return getUpdate(); } /** * Reset the first and second derivative vectors */ protected void reset2() { Arrays.fill(d1, 0); Arrays.fill(d2, 0); } /** * Calculates the Newton-Raphson update vector for a Poisson process. Variables are named as per the Smith, et al * (2010) paper. * * {@inheritDoc} * * @see gdsc.smlm.function.Gradient2Procedure#execute(double, double[], double[]) */ public void execute(double uk, double[] duk_dt, double[] d2uk_dt2) { u[k] = uk; final double xk = x[k++]; final double xk_uk_minus1 = xk / uk - 1.0; final double xk_uk2 = xk / (uk * uk); for (int i = 0; i < n; i++) { d1[i] += duk_dt[i] * xk_uk_minus1; d2[i] += d2uk_dt2[i] * xk_uk_minus1 - duk_dt[i] * duk_dt[i] * xk_uk2; } } /** * Gets the update vector of the function's parameters (size n). * * @return the update vector */ public double[] getUpdate() { double[] update = new double[n]; for (int i = 0; i < n; i++) update[i] = d1[i] / d2[i]; return update; } /** * Calculates the first derivative of the Poisson log likelihood with respect to each parameter. * * @param a * Set of coefficients for the function * @return the first derivative of the Poisson log likelihood with respect to each parameter */ public double[] computeFirstDerivative(final double[] a) { k = 0; reset1(); func.initialise1(a); func.forEach((Gradient1Procedure) this); return d1; } /** * Reset the first derivative vector */ protected void reset1() { Arrays.fill(d1, 0); } /** * Variables are named as per the Smith, et al (2010) paper. * * {@inheritDoc} * * @see gdsc.smlm.function.Gradient1Procedure#execute(double, double[]) */ public void execute(double uk, double[] duk_dt) { u[k] = uk; final double xk = x[k++]; final double xk_uk_minus1 = xk / uk - 1.0; for (int i = 0; i < n; i++) { d1[i] += duk_dt[i] * xk_uk_minus1; } } /** * Compute the value of the function. * * @param a * the a * @return the double[] */ public double[] computeValue(final double[] a) { k = 0; func.initialise0(a); func.forEach((ValueProcedure) this); return u; } /** * Variables are named as per the Smith, et al (2010) paper. * * {@inheritDoc} * * @see gdsc.smlm.function.ValueProcedure#execute(double) */ public void execute(double uk) { u[k++] = uk; } /** * Calculates the Poisson log likelihood. * * @param a * Set of coefficients for the function * @return the Poisson log likelihood */ public double computeLogLikelihood(final double[] a) { computeValue(a); return PoissonCalculator.logLikelihood(u, x); } /** * Calculates the Poisson log likelihood ratio. * * @param a * Set of coefficients for the function * @return the Poisson log likelihood ratio */ public double computeLogLikelihoodRatio(final double[] a) { computeValue(a); return PoissonCalculator.logLikelihoodRatio(u, x); } }