Search in sources :

Example 36 with PoissonDistribution

use of uk.ac.sussex.gdsc.smlm.math3.distribution.PoissonDistribution in project shifu by ShifuML.

the class AbstractNNWorker method sampleWeights.

protected float sampleWeights(float label) {
    float sampleWeights = 1f;
    // sample negative or kFoldCV, sample rate is 1d
    double sampleRate = (modelConfig.getTrain().getSampleNegOnly() || this.isKFoldCV) ? 1d : modelConfig.getTrain().getBaggingSampleRate();
    int classValue = (int) (label + 0.01f);
    if (!modelConfig.isBaggingWithReplacement()) {
        Random random = null;
        if (this.isStratifiedSampling) {
            random = baggingRandomMap.get(classValue);
            if (random == null) {
                random = DTrainUtils.generateRandomBySampleSeed(modelConfig.getTrain().getBaggingSampleSeed(), CommonConstants.NOT_CONFIGURED_BAGGING_SEED);
                baggingRandomMap.put(classValue, random);
            }
        } else {
            random = baggingRandomMap.get(0);
            if (random == null) {
                random = DTrainUtils.generateRandomBySampleSeed(modelConfig.getTrain().getBaggingSampleSeed(), CommonConstants.NOT_CONFIGURED_BAGGING_SEED);
                baggingRandomMap.put(0, random);
            }
        }
        if (random.nextDouble() <= sampleRate) {
            sampleWeights = 1f;
        } else {
            sampleWeights = 0f;
        }
    } else {
        // replacement
        if (this.isStratifiedSampling) {
            PoissonDistribution rng = this.baggingRngMap.get(classValue);
            if (rng == null) {
                rng = new PoissonDistribution(sampleRate);
                this.baggingRngMap.put(classValue, rng);
            }
            sampleWeights = rng.sample();
        } else {
            PoissonDistribution rng = this.baggingRngMap.get(0);
            if (rng == null) {
                rng = new PoissonDistribution(sampleRate);
                this.baggingRngMap.put(0, rng);
            }
            sampleWeights = rng.sample();
        }
    }
    return sampleWeights;
}
Also used : PoissonDistribution(org.apache.commons.math3.distribution.PoissonDistribution) Random(java.util.Random)

Example 37 with PoissonDistribution

use of uk.ac.sussex.gdsc.smlm.math3.distribution.PoissonDistribution in project GDSC-SMLM by aherbert.

the class PoissonLikelihoodWrapperTest method cumulativeProbabilityIsOneFromLikelihood.

private void cumulativeProbabilityIsOneFromLikelihood(final double mu) {
    // Determine upper limit for a Poisson
    final int limit = new PoissonDistribution(mu).inverseCumulativeProbability(0.999);
    // Expand to allow for the gain
    final int n = (int) Math.ceil(limit / alpha);
    // Evaluate all values from zero to large n
    final double[] k = SimpleArrayUtils.newArray(n, 0, 1.0);
    final double[] a = new double[1];
    final double[] g = new double[1];
    final NonLinearFunction nlf = new DummyNonLinearFunction(mu);
    PoissonLikelihoodWrapper func = new PoissonLikelihoodWrapper(nlf, a, Arrays.copyOf(k, n), n, alpha);
    // Keep evaluating up until no difference
    final double changeTolerance = 1e-6;
    double total = 0;
    double pvalue = 0;
    int index = 0;
    while (index < n) {
        final double nll = func.computeLikelihood(index);
        final double nll2 = func.computeLikelihood(g, index);
        index++;
        Assertions.assertEquals(nll, nll2, 1e-10, "computeLikelihood(i)");
        total += nll;
        final double pp = StdMath.exp(-nll);
        // logger.fine(FunctionUtils.getSupplier("mu=%f, o=%f, i=%d, pp=%f", mu, mu / alpha, i, pp);
        pvalue += pp;
        if (pvalue > 0.5 && pp / pvalue < changeTolerance) {
            break;
        }
    }
    logger.log(TestLogUtils.getRecord(LOG_LEVEL, "mu=%f, limit=%d, p=%f", mu, limit, pvalue));
    Assertions.assertEquals(1, pvalue, 0.02, () -> String.format("mu=%f", mu));
    // Check the function can compute the same total
    func = new PoissonLikelihoodWrapper(nlf, a, k, index, alpha);
    final double sum = func.computeLikelihood();
    final double sum2 = func.computeLikelihood(g);
    final DoubleDoubleBiPredicate predicate = TestHelper.doublesAreClose(1e-10, 0);
    TestAssertions.assertTest(total, sum, predicate, "computeLikelihood");
    TestAssertions.assertTest(total, sum2, predicate, "computeLikelihood with gradient");
}
Also used : PoissonDistribution(org.apache.commons.math3.distribution.PoissonDistribution) DoubleDoubleBiPredicate(uk.ac.sussex.gdsc.test.api.function.DoubleDoubleBiPredicate)

Example 38 with PoissonDistribution

use of uk.ac.sussex.gdsc.smlm.math3.distribution.PoissonDistribution in project GDSC-SMLM by aherbert.

the class ScmosLikelihoodWrapperTest method cumulativeProbabilityIsOneWithRealDataForCountAbove8.

@Test
void cumulativeProbabilityIsOneWithRealDataForCountAbove8() {
    for (final double mu : photons) {
        // Determine upper limit for a Poisson
        double max = new PoissonDistribution(mu).inverseCumulativeProbability(P_LIMIT);
        // Determine lower limit
        final double sd = Math.sqrt(mu);
        double min = (int) Math.max(0, mu - 4 * sd);
        // Map to observed values using the gain and offset
        max = max * G + O;
        min = min * G + O;
        cumulativeProbabilityIsOneWithRealData(mu, min, max, mu > 8);
    }
}
Also used : PoissonDistribution(org.apache.commons.math3.distribution.PoissonDistribution) SeededTest(uk.ac.sussex.gdsc.test.junit5.SeededTest) Test(org.junit.jupiter.api.Test)

Example 39 with PoissonDistribution

use of uk.ac.sussex.gdsc.smlm.math3.distribution.PoissonDistribution in project GDSC-SMLM by aherbert.

the class CameraModelAnalysis method convolveHistogram.

/**
 * Convolve the histogram. The output is a discrete probability distribution.
 *
 * @param settings the settings
 * @return The histogram
 */
private static double[][] convolveHistogram(CameraModelAnalysisSettings settings) {
    // Find the range of the Poisson
    final PoissonDistribution poisson = new PoissonDistribution(settings.getPhotons());
    final int maxn = poisson.inverseCumulativeProbability(UPPER);
    final double gain = getGain(settings);
    final double noise = getReadNoise(settings);
    final boolean debug = false;
    // Build the Probability Mass/Density Function (PDF) of the distribution:
    // either a Poisson (PMF) or Poisson-Gamma (PDF). The PDF is 0 at all
    // values apart from the step interval.
    // Note: The Poisson-Gamma is computed without the Dirac delta contribution
    // at c=0. This allows correct convolution with the Gaussian of the dirac delta
    // and the rest of the Poisson-Gamma (so matching the simulation).
    final TDoubleArrayList list = new TDoubleArrayList();
    double step;
    String name;
    int upsample = 100;
    // Store the Dirac delta value at c=0. This must be convolved separately.
    double dirac = 0;
    // EM-CCD
    if (settings.getMode() == MODE_EM_CCD) {
        name = "Poisson-Gamma";
        final double m = gain;
        final double p = settings.getPhotons();
        dirac = PoissonGammaFunction.dirac(p);
        // Chose whether to compute a discrete PMF or a PDF using the approximation.
        // Note: The delta function at c=0 is from the PMF of the Poisson. So it is
        // a discrete contribution. This is omitted from the PDF and handled in
        // a separate convolution.
        // noise != 0;
        final boolean discrete = false;
        if (discrete) {
            // Note: This is obsolete as the Poisson-Gamma function is continuous.
            // Sampling it at integer intervals is not valid, especially for low gain.
            // The Poisson-Gamma PDF should be integrated to form a discrete PMF.
            step = 1.0;
            double upper;
            if (settings.getPhotons() < 20) {
                upper = maxn;
            } else {
                // Approximate reasonable range of Poisson as a Gaussian
                upper = settings.getPhotons() + 3 * Math.sqrt(settings.getPhotons());
            }
            final GammaDistribution gamma = new GammaDistribution(null, upper, gain, GammaDistribution.DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
            final int maxc = (int) gamma.inverseCumulativeProbability(0.999);
            final int minn = Math.max(1, poisson.inverseCumulativeProbability(LOWER));
            // See Ulbrich & Isacoff (2007). Nature Methods 4, 319-321, SI equation 3.
            // Note this is not a convolution of a single Gamma distribution since the shape
            // is modified not the count. So it is a convolution of a distribution made with
            // a gamma of fixed count and variable shape.
            // The count=0 is a special case.
            list.add(PoissonGammaFunction.poissonGammaN(0, p, m));
            final long total = (maxn - minn) * (long) maxc;
            if (total < 1000000) {
                // Full computation
                // G(c) = sum n { (1 / n!) p^n e^-p (1 / ((n-1!)m^n)) c^n-1 e^-c/m }
                // Compute as a log
                // - log(n!) + n*log(p)-p -log((n-1)!) - n * log(m) + (n-1) * log(c) -c/m
                // Note: Both methods work
                LogFactorialCache lfc = LOG_FACTORIAL_CACHE.get().get();
                if (lfc == null) {
                    lfc = new LogFactorialCache();
                    LOG_FACTORIAL_CACHE.set(new SoftReference<>(lfc));
                }
                lfc.ensureRange(minn - 1, maxn);
                final double[] f = new double[maxn + 1];
                final double logm = Math.log(m);
                final double logp = Math.log(p);
                for (int n = minn; n <= maxn; n++) {
                    f[n] = -lfc.getLogFactorialUnsafe(n) + n * logp - p - lfc.getLogFactorialUnsafe(n - 1) - n * logm;
                }
                // double total2 = total;
                for (int c = 1; c <= maxc; c++) {
                    double sum = 0;
                    final double c_m = c / m;
                    final double logc = Math.log(c);
                    for (int n = minn; n <= maxn; n++) {
                        sum += StdMath.exp(f[n] + (n - 1) * logc - c_m);
                    }
                    // sum2 += pd[n] * gd[n].density(c);
                    list.add(sum);
                // total += sum;
                // This should match the approximation
                // double approx = PoissonGammaFunction.poissonGamma(c, p, m);
                // total2 += approx;
                // System.out.printf("c=%d sum=%g approx=%g error=%g\n", c, sum2, approx,
                // uk.ac.sussex.gdsc.core.utils.DoubleEquality.relativeError(sum2, approx));
                }
            // System.out.printf("sum=%g approx=%g error=%g\n", total, total2,
            // uk.ac.sussex.gdsc.core.utils.DoubleEquality.relativeError(total, total2));
            } else {
                // Approximate
                for (int c = 1; c <= maxc; c++) {
                    list.add(PoissonGammaFunction.poissonGammaN(c, p, m));
                }
            }
        } else {
            // This integrates the PDF using the approximation and up-samples together.
            // Compute the sampling interval.
            step = 1.0 / upsample;
            // Reset
            upsample = 1;
            // Compute the integral of [-step/2:step/2] for each point.
            // Use trapezoid integration.
            final double step_2 = step / 2;
            double prev = PoissonGammaFunction.poissonGammaN(0, p, m);
            double next = PoissonGammaFunction.poissonGammaN(step_2, p, m);
            list.add((prev + next) * 0.25);
            double max = 0;
            for (int i = 1; ; i++) {
                // Each remaining point is modelling a PMF for the range [-step/2:step/2]
                prev = next;
                next = PoissonGammaFunction.poissonGammaN(i * step + step_2, p, m);
                final double pp = (prev + next) * 0.5;
                if (max < pp) {
                    max = pp;
                }
                if (pp / max < 1e-5) {
                    // Use this if non-zero since it has been calculated
                    if (pp != 0) {
                        list.add(pp);
                    }
                    break;
                }
                list.add(pp);
            }
        }
        // Ensure the combined sum of PDF and Dirac is 1
        final double expected = 1 - dirac;
        // Require an odd number to get an even number (n) of sub-intervals:
        if (list.size() % 2 == 0) {
            list.add(0);
        }
        final double[] g = list.toArray();
        // Number of sub intervals
        final int n = g.length - 1;
        // h = (a-b) / n = sub-interval width
        final double h = 1;
        double sum2 = 0;
        double sum4 = 0;
        for (int j = 1; j <= n / 2 - 1; j++) {
            sum2 += g[2 * j];
        }
        for (int j = 1; j <= n / 2; j++) {
            sum4 += g[2 * j - 1];
        }
        final double sum = (h / 3) * (g[0] + 2 * sum2 + 4 * sum4 + g[n]);
        // Check
        // System.out.printf("Sum=%g Expected=%g\n", sum * step, expected);
        SimpleArrayUtils.multiply(g, expected / sum);
        list.resetQuick();
        list.add(g);
    } else {
        name = "Poisson";
        // Apply fixed gain. Just change the step interval of the PMF.
        step = gain;
        for (int n = 0; n <= maxn; n++) {
            list.add(poisson.probability(n));
        }
        final double p = poisson.probability(list.size());
        if (p != 0) {
            list.add(p);
        }
    }
    // Debug
    if (debug) {
        final String title = name;
        final Plot plot = new Plot(title, "x", "y");
        plot.addPoints(SimpleArrayUtils.newArray(list.size(), 0, step), list.toArray(), Plot.LINE);
        ImageJUtils.display(title, plot);
    }
    double zero = 0;
    double[] pg = list.toArray();
    // Sample Gaussian
    if (noise > 0) {
        step /= upsample;
        pg = list.toArray();
        // Convolve with Gaussian kernel
        final double[] kernel = GaussianKernel.makeGaussianKernel(Math.abs(noise) / step, 6, true);
        if (upsample != 1) {
            // Use scaled convolution. This is faster that zero filling distribution g.
            pg = Convolution.convolve(kernel, pg, upsample);
        } else if (dirac > 0.01) {
            // The Poisson-Gamma may be stepped at low mean causing wrap artifacts in the FFT.
            // This is a problem if most of the probability is in the Dirac.
            // Otherwise it can be ignored and the FFT version is OK.
            pg = Convolution.convolve(kernel, pg);
        } else {
            pg = Convolution.convolveFast(kernel, pg);
        }
        // The convolution will have created a larger array so we must adjust the offset for this
        final int radius = kernel.length / 2;
        zero -= radius * step;
        // Add convolution of the dirac delta function.
        if (dirac != 0) {
            // We only need to convolve the Gaussian at c=0
            for (int i = 0; i < kernel.length; i++) {
                pg[i] += kernel[i] * dirac;
            }
        }
        // Debug
        if (debug) {
            String title = "Gaussian";
            Plot plot = new Plot(title, "x", "y");
            plot.addPoints(SimpleArrayUtils.newArray(kernel.length, -radius * step, step), kernel, Plot.LINE);
            ImageJUtils.display(title, plot);
            title = name + "-Gaussian";
            plot = new Plot(title, "x", "y");
            plot.addPoints(SimpleArrayUtils.newArray(pg.length, zero, step), pg, Plot.LINE);
            ImageJUtils.display(title, plot);
        }
        zero = downSampleCdfToPmf(settings, list, step, zero, pg, 1.0);
        pg = list.toArray();
        zero = (int) Math.floor(zero);
        step = 1.0;
    // No convolution means we have the Poisson PMF/Poisson-Gamma PDF already
    } else if (step != 1) {
        // Sample to 1.0 pixel step interval.
        if (settings.getMode() == MODE_EM_CCD) {
            // Poisson-Gamma PDF
            zero = downSampleCdfToPmf(settings, list, step, zero, pg, 1 - dirac);
            pg = list.toArray();
            zero = (int) Math.floor(zero);
            // Add the dirac delta function.
            if (dirac != 0) {
                // Note: zero is the start of the x-axis. This value should be -1.
                assert (int) zero == -1;
                // Use as an offset to find the actual zero.
                pg[-(int) zero] += dirac;
            }
        } else {
            // Poisson PMF
            // Simple non-interpolated expansion.
            // This should be used when there is no Gaussian convolution.
            final double[] pd = pg;
            list.resetQuick();
            // Account for rounding.
            final Round round = getRound(settings);
            final int maxc = round.round(pd.length * step + 1);
            pg = new double[maxc];
            for (int n = pd.length; n-- > 0; ) {
                pg[round.round(n * step)] += pd[n];
            }
            if (pg[0] != 0) {
                list.add(0);
                list.add(pg);
                pg = list.toArray();
                zero--;
            }
        }
        step = 1.0;
    } else {
        // Add the dirac delta function.
        list.setQuick(0, list.getQuick(0) + dirac);
    }
    return new double[][] { SimpleArrayUtils.newArray(pg.length, zero, step), pg };
}
Also used : PoissonDistribution(uk.ac.sussex.gdsc.smlm.math3.distribution.PoissonDistribution) LogFactorialCache(uk.ac.sussex.gdsc.smlm.function.LogFactorialCache) Plot(ij.gui.Plot) TDoubleArrayList(gnu.trove.list.array.TDoubleArrayList) GammaDistribution(org.apache.commons.math3.distribution.GammaDistribution)

Example 40 with PoissonDistribution

use of uk.ac.sussex.gdsc.smlm.math3.distribution.PoissonDistribution in project GDSC-SMLM by aherbert.

the class GradientCalculatorSpeedTest method mleGradientCalculatorComputesLikelihood.

@SeededTest
void mleGradientCalculatorComputesLikelihood() {
    // @formatter:off
    final NonLinearFunction func = new NonLinearFunction() {

        double u;

        @Override
        public void initialise(double[] a) {
            u = a[0];
        }

        @Override
        public int[] gradientIndices() {
            return null;
        }

        @Override
        public double eval(int x, double[] dyda) {
            return 0;
        }

        @Override
        public double eval(int x) {
            return u;
        }

        @Override
        public double evalw(int x, double[] dyda, double[] w) {
            return 0;
        }

        @Override
        public double evalw(int x, double[] w) {
            return 0;
        }

        @Override
        public boolean canComputeWeights() {
            return false;
        }

        @Override
        public int getNumberOfGradients() {
            return 0;
        }
    };
    // @formatter:on
    final DoubleDoubleBiPredicate predicate = TestHelper.doublesAreClose(1e-10, 0);
    final int[] xx = SimpleArrayUtils.natural(100);
    final double[] xxx = SimpleArrayUtils.newArray(100, 0, 1.0);
    for (final double u : new double[] { 0.79, 2.5, 5.32 }) {
        double ll = 0;
        double oll = 0;
        final PoissonDistribution pd = new PoissonDistribution(u);
        // The logLikelihood function for the entire array of observations is then asserted.
        for (final int x : xx) {
            double obs = PoissonCalculator.likelihood(u, x);
            double exp = pd.probability(x);
            TestAssertions.assertTest(exp, obs, predicate, "likelihood");
            obs = PoissonCalculator.logLikelihood(u, x);
            exp = pd.logProbability(x);
            TestAssertions.assertTest(exp, obs, predicate, "log likelihood");
            oll += obs;
            ll += exp;
        }
        final MleGradientCalculator gc = new MleGradientCalculator(1);
        final double o = gc.logLikelihood(xxx, new double[] { u }, func);
        Assertions.assertEquals(oll, o, "sum log likelihood should exactly match the PoissonCalculator");
        TestAssertions.assertTest(ll, o, predicate, "sum log likelihood");
    }
}
Also used : PoissonDistribution(org.apache.commons.math3.distribution.PoissonDistribution) DoubleDoubleBiPredicate(uk.ac.sussex.gdsc.test.api.function.DoubleDoubleBiPredicate) NonLinearFunction(uk.ac.sussex.gdsc.smlm.function.NonLinearFunction) SeededTest(uk.ac.sussex.gdsc.test.junit5.SeededTest)

Aggregations

PoissonDistribution (org.apache.commons.math3.distribution.PoissonDistribution)39 DoubleDoubleBiPredicate (uk.ac.sussex.gdsc.test.api.function.DoubleDoubleBiPredicate)7 SeededTest (uk.ac.sussex.gdsc.test.junit5.SeededTest)5 Test (org.junit.jupiter.api.Test)4 IOException (java.io.IOException)3 ArrayList (java.util.ArrayList)3 Random (java.util.Random)3 RandomGenerator (org.apache.commons.math3.random.RandomGenerator)3 Test (org.junit.Test)3 SqlScalarFunction (com.facebook.presto.metadata.SqlScalarFunction)2 Description (com.facebook.presto.spi.function.Description)2 ScalarFunction (com.facebook.presto.spi.function.ScalarFunction)2 SqlType (com.facebook.presto.spi.function.SqlType)2 TDigest.createTDigest (com.facebook.presto.tdigest.TDigest.createTDigest)2 DecimalOperators.modulusScalarFunction (com.facebook.presto.type.DecimalOperators.modulusScalarFunction)2 Sets (com.google.cloud.dataflow.sdk.repackaged.com.google.common.collect.Sets)2 java.util (java.util)2 GuaguaRuntimeException (ml.shifu.guagua.GuaguaRuntimeException)2 GridSearch (ml.shifu.shifu.core.dtrain.gs.GridSearch)2 PoissonDistribution (uk.ac.sussex.gdsc.smlm.math3.distribution.PoissonDistribution)2