com.analog.lyric.math.DimpleRandom.java Source code

Java tutorial

Introduction

Here is the source code for com.analog.lyric.math.DimpleRandom.java

Source

/*******************************************************************************
*   Copyright 2015 Analog Devices, Inc.
*
*   Licensed under the Apache License, Version 2.0 (the "License");
*   you may not use this file except in compliance with the License.
*   You may obtain a copy of the License at
*
*       http://www.apache.org/licenses/LICENSE-2.0
*
*   Unless required by applicable law or agreed to in writing, software
*   distributed under the License is distributed on an "AS IS" BASIS,
*   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*   See the License for the specific language governing permissions and
*   limitations under the License.
********************************************************************************/

package com.analog.lyric.math;

import java.util.Random;

import org.apache.commons.math3.distribution.BetaDistribution;
import org.apache.commons.math3.distribution.BinomialDistribution;
import org.apache.commons.math3.random.MersenneTwister;
import org.apache.commons.math3.random.RandomAdaptor;
import org.apache.commons.math3.random.RandomGenerator;
import org.eclipse.jdt.annotation.NonNullByDefault;

import com.analog.lyric.dimple.environment.DimpleEnvironment;

import cern.jet.random.engine.RandomEngine;
import net.jcip.annotations.NotThreadSafe;

/**
 * Extended implementation of {@link Random} for use in Dimple
 * <p>
 * Most users will want to use {@link DimpleEnvironment#activeRandom()} rather than constructing
 * a new instance.
 * <p>
 * @since 0.08
 * @author Christopher Barber
 */
@NotThreadSafe
public class DimpleRandom extends RandomAdaptor {
    /*-------
     * State
     */

    private static final long serialVersionUID = 1L;

    final RandomGenerator _randGenerator;

    private BetaDistribution _randBeta;
    private BinomialDistribution _randBinomial;

    // For now, continue to use the CERN Gamma implementation. It is somewhere between 40-100% faster
    // and the Apache implementation causes tests to fail in a way that suggest that it might not be as
    // numerically accurate or stable. We should look at the next Apache release (3.6) to see if it does
    // anything about this...
    private RandomEngine _randEngine;
    private cern.jet.random.Gamma _randGamma;

    private long _seed;

    /*--------------
     * Construction
     */

    /**
     * Construct using specified underlying random generator and seed.
     * <p>
     * @since 0.08
     */
    public DimpleRandom(RandomGenerator randomGenerator, long seed) {
        super(randomGenerator);
        _randGenerator = randomGenerator;
        _seed = seed;
        _randEngine = new cern.jet.random.engine.MersenneTwister((int) seed);
        _randGamma = new cern.jet.random.Gamma(1, 1, _randEngine);
        _randBeta = new BetaDistribution(_randGenerator, 1, 1);
        _randBinomial = new BinomialDistribution(_randGenerator, 1, 0.5);
    }

    /**
     * Construct using specified underlying random generator and a randomly generated seed.
     * <p>
     * The random value used to seed the generator can be obtained via {@link #getSeed()}.
     * <p>
     * @since 0.08
     */
    public DimpleRandom(RandomGenerator randomGenerator) {
        this(randomGenerator, randomGenerator.nextLong());
    }

    /**
     * Construct with specified seed.
     * <p>
     * @since 0.08
     */
    public DimpleRandom(long seed) {
        this(new MersenneTwister(seed), seed);
    }

    /**
     * Construct with randomly generated seed.
     * <p>
     * The random value used to seed the generator can be obtained via {@link #getSeed()}.
     * @since 0.08
     */
    public DimpleRandom() {
        this(new MersenneTwister());
        setSeed(nextLong());
    }

    /*----------------
     * Random methods
     */

    @Override
    public void setSeed(long seed) {
        super.setSeed(seed);
        _seed = seed;
        _randEngine = new cern.jet.random.engine.MersenneTwister((int) seed);
        _randGamma = new cern.jet.random.Gamma(1, 1, _randEngine);
    }

    @Override
    public void setSeed(int seed) {
        setSeed(seed | (long) seed << 32L);
    }

    @NonNullByDefault(false)
    @Override
    public void setSeed(int[] seed) {
        long lseed = 0;
        for (int i : seed) {
            lseed *= 13;
            lseed += i;
        }
        setSeed(lseed);
    }

    /*----------------------
     * DimpleRandom methods
     */

    /**
     * The underlying Apache random generator.
     * <p>
     * May be used with Apache's distribution classes.
     * <p>
     * @since 0.08
     */
    public RandomGenerator getGenerator() {
        return _randGenerator;
    }

    /**
     * Returns seed used to initialize random generator.
     * <p>
     * Returns seed set by one of the {@link #setSeed} methods or randomly generated in constructor.
     * @since 0.08
     */
    public long getSeed() {
        return _seed;
    }

    /**
     * Returns sample from beta distribution with alpha and beta parameters set to one.
     * @since 0.08
     * @see #nextBeta(double, double)
     */
    public double nextBeta() {
        return nextBeta(1.0, 1.0);
    }

    /**
     * Returns sample from beta distribution with specified alpha and beta parameters.
     * @since 0.08
     */
    public double nextBeta(double alpha, double beta) {
        BetaDistribution randBeta = _randBeta;

        if (randBeta.getAlpha() != alpha || randBeta.getBeta() != beta) {
            randBeta = new BetaDistribution(_randGenerator, alpha, beta);
            _randBeta = randBeta;
        }

        return randBeta.sample();
    }

    /**
     * Returns sample from bernoulli distribution with parameter p set to .5.
     * @since 0.08
     * @see #nextBernoulli(double)
     */
    public int nextBernoulli() {
        return nextBernoulli(.5);
    }

    /**
     * Returns sample from bernoulli distribution with given probability of drawing 1.
     * @since 0.08
     */
    public int nextBernoulli(double p) {
        return nextBinomial(1, p);
    }

    /**
     * Returns sample from beta distribution with specified alpha and beta parameters.
     * @since 0.08
     */
    public int nextBinomial(int n, double p) {
        // randBinomial doesn't accept zero N value or 1 or 0 p value
        if (n <= 0)
            return 0;
        else if (p <= 0)
            return 0;
        else if (p >= 1)
            return n;

        BinomialDistribution randBinomial = _randBinomial;

        if (randBinomial.getNumberOfTrials() != n || randBinomial.getProbabilityOfSuccess() != p) {
            _randBinomial = randBinomial = new BinomialDistribution(_randGenerator, n, p);
        }

        return randBinomial.sample();
    }

    /**
     * Returns boolean with given probability {@code p} of being {@code true}.
     * @since 0.08
     */
    public boolean nextBoolean(double p) {
        return nextBernoulli(p) == 1;
    }

    /**
     * Returns sample from gamma distribution with alpha and beta parameters set to one.
     * @since 0.08
     * @see #nextBeta(double, double)
     */
    public double nextGamma() {
        return nextGamma(1.0, 1.0);
    }

    /**
     * Returns sample from gamma distribution with specified alpha and beta parameters.
     * @since 0.08
     */
    public double nextGamma(double alpha, double beta) {
        return _randGamma.nextDouble(alpha, beta);
    }

}