Hi Sébastien, ----- Mail original ----- > Author: celestin > Date: Thu Jan 12 07:01:43 2012 > New Revision: 1230419 > > URL: http://svn.apache.org/viewvc?rev=1230419&view=rev > Log: > Implementation of continuous triangular distributions (MATH-731). > Patch contributed by Dennis Hendriks. > > Added: > > commons/proper/math/trunk/src/main/java/org/apache/commons/math/distribution/TriangularDistribution.java > (with props) > > commons/proper/math/trunk/src/test/java/org/apache/commons/math/distribution/TriangularDistributionTest.java > (with props)
You should probably also change the NOTICE file to include the licence text from the original code. Luc > > Added: > commons/proper/math/trunk/src/main/java/org/apache/commons/math/distribution/TriangularDistribution.java > URL: > http://svn.apache.org/viewvc/commons/proper/math/trunk/src/main/java/org/apache/commons/math/distribution/TriangularDistribution.java?rev=1230419&view=auto > ============================================================================== > --- > commons/proper/math/trunk/src/main/java/org/apache/commons/math/distribution/TriangularDistribution.java > (added) > +++ > commons/proper/math/trunk/src/main/java/org/apache/commons/math/distribution/TriangularDistribution.java > Thu Jan 12 07:01:43 2012 > @@ -0,0 +1,260 @@ > +/* > + * Licensed to the Apache Software Foundation (ASF) under one or > more > + * contributor license agreements. See the NOTICE file distributed > with > + * this work for additional information regarding copyright > ownership. > + * The ASF licenses this file to You 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 org.apache.commons.math.distribution; > + > +import org.apache.commons.math.exception.NumberIsTooLargeException; > +import org.apache.commons.math.exception.NumberIsTooSmallException; > +import org.apache.commons.math.exception.OutOfRangeException; > +import org.apache.commons.math.exception.util.LocalizedFormats; > +import org.apache.commons.math.util.FastMath; > + > +/** > + * Implementation of the triangular real distribution. > + * > + * @see <a > href="http://en.wikipedia.org/wiki/Triangular_distribution"> > + * Triangular distribution (Wikipedia)</a> > + * > + * @version $Id$ > + * @since 3.0 > + */ > +public class TriangularDistribution extends AbstractRealDistribution > { > + /** Serializable version identifier. */ > + private static final long serialVersionUID = 20120112L; > + > + /** Lower limit of this distribution (inclusive). */ > + private final double a; > + > + /** Upper limit of this distribution (inclusive). */ > + private final double b; > + > + /** Mode of this distribution. */ > + private final double c; > + > + /** Inverse cumulative probability accuracy. */ > + private final double solverAbsoluteAccuracy; > + > + /** > + * Create a triangular real distribution using the given lower > limit, > + * upper limit, and mode. > + * > + * @param a Lower limit of this distribution (inclusive). > + * @param b Upper limit of this distribution (inclusive). > + * @param c Mode of this distribution. > + * @throws NumberIsTooLargeException if {@code a >= b} or if > {@code c > b} > + * @throws NumberIsTooSmallException if {@code c < a} > + */ > + public TriangularDistribution(double a, double c, double b) > + throws NumberIsTooLargeException, NumberIsTooSmallException > { > + if (a >= b) { > + throw new NumberIsTooLargeException( > + > LocalizedFormats.LOWER_BOUND_NOT_BELOW_UPPER_BOUND, > + a, b, false); > + } > + if (c < a) { > + throw new NumberIsTooSmallException( > + LocalizedFormats.NUMBER_TOO_SMALL, c, a, true); > + } > + if (c > b) { > + throw new NumberIsTooLargeException( > + LocalizedFormats.NUMBER_TOO_LARGE, c, b, true); > + } > + > + this.a = a; > + this.c = c; > + this.b = b; > + solverAbsoluteAccuracy = FastMath.ulp(c); > + } > + > + /** > + * Returns the mode {@code c} of this distribution. > + * > + * @return the mode {@code c} of this distribution > + */ > + public double getMode() { > + return c; > + } > + > + /** {@inheritDoc} */ > + @Override > + protected double getSolverAbsoluteAccuracy() { > + return solverAbsoluteAccuracy; > + } > + > + /** > + * {@inheritDoc} > + * > + * For this distribution {@code P(X = x)} always evaluates to 0. > + * > + * @return 0 > + */ > + public double probability(double x) { > + return 0; > + } > + > + /** > + * {@inheritDoc} > + * > + * For lower limit {@code a}, upper limit {@code b} and mode > {@code c}, the > + * PDF is given by > + * <ul> > + * <li>{@code 2 * (x - a) / [(b - a) * (c - a)]} if {@code a <= > x < c},</li> > + * <li>{@code 2 / (b - a)} if {@code x = c},</li> > + * <li>{@code 2 * (b - x) / [(b - a) * (b - c)]} if {@code c < x > <= b},</li> > + * <li>{@code 0} otherwise. > + * </ul> > + */ > + public double density(double x) { > + if (x < a) { > + return 0; > + } > + if (a <= x && x < c) { > + double divident = 2 * (x - a); > + double divisor = (b - a) * (c - a); > + return divident / divisor; > + } > + if (x == c) { > + return 2 / (b - a); > + } > + if (c < x && x <= b) { > + double divident = 2 * (b - x); > + double divisor = (b - a) * (b - c); > + return divident / divisor; > + } > + return 0; > + } > + > + /** > + * {@inheritDoc} > + * > + * For lower limit {@code a}, upper limit {@code b} and mode > {@code c}, the > + * CDF is given by > + * <ul> > + * <li>{@code 0} if {@code x < a},</li> > + * <li>{@code (x - a)^2 / [(b - a) * (c - a)]} if {@code a <= x > < c},</li> > + * <li>{@code (c - a) / (b - a)} if {@code x = c},</li> > + * <li>{@code 1 - (b - x)^2 / [(b - a) * (b - c)]} if {@code c < > x <= b},</li> > + * <li>{@code 1} if {@code x > b}.</li> > + * </ul> > + */ > + public double cumulativeProbability(double x) { > + if (x < a) { > + return 0; > + } > + if (a <= x && x < c) { > + double divident = (x - a) * (x - a); > + double divisor = (b - a) * (c - a); > + return divident / divisor; > + } > + if (x == c) { > + return (c - a) / (b - a); > + } > + if (c < x && x <= b) { > + double divident = (b - x) * (b - x); > + double divisor = (b - a) * (b - c); > + return 1 - (divident / divisor); > + } > + return 1; > + } > + > + /** > + * {@inheritDoc} > + * > + * For lower limit {@code a}, upper limit {@code b}, and mode > {@code c}, > + * the mean is {@code (a + b + c) / 3}. > + */ > + public double getNumericalMean() { > + return (a + b + c) / 3; > + } > + > + /** > + * {@inheritDoc} > + * > + * For lower limit {@code a}, upper limit {@code b}, and mode > {@code c}, > + * the variance is {@code (a^2 + b^2 + c^2 - a * b - a * c - b * > c) / 18}. > + */ > + public double getNumericalVariance() { > + return (a * a + b * b + c * c - a * b - a * c - b * c) / 18; > + } > + > + /** > + * {@inheritDoc} > + * > + * The lower bound of the support is equal to the lower limit > parameter > + * {@code a} of the distribution. > + * > + * @return lower bound of the support > + */ > + public double getSupportLowerBound() { > + return a; > + } > + > + /** > + * {@inheritDoc} > + * > + * The upper bound of the support is equal to the upper limit > parameter > + * {@code b} of the distribution. > + * > + * @return upper bound of the support > + */ > + public double getSupportUpperBound() { > + return b; > + } > + > + /** {@inheritDoc} */ > + public boolean isSupportLowerBoundInclusive() { > + return true; > + } > + > + /** {@inheritDoc} */ > + public boolean isSupportUpperBoundInclusive() { > + return true; > + } > + > + /** > + * {@inheritDoc} > + * > + * The support of this distribution is connected. > + * > + * @return {@code true} > + */ > + public boolean isSupportConnected() { > + return true; > + } > + > + @Override > + public double inverseCumulativeProbability(double p) > + throws OutOfRangeException { > + if (p < 0.0 || p > 1.0) { > + throw new OutOfRangeException(p, 0, 1); > + } > + if (p == 0.0) { > + return a; > + } > + if (p == 1.0) { > + return b; > + } > + final double pc = (c - a) / (b - a); > + if (p == pc) { > + return c; > + } > + if (p < pc) { > + return a + FastMath.sqrt(p * (b - a) * (c - a)); > + } > + return b - FastMath.sqrt((1 - p) * (b - a) * (b - c)); > + } > +} > > Propchange: > commons/proper/math/trunk/src/main/java/org/apache/commons/math/distribution/TriangularDistribution.java > ------------------------------------------------------------------------------ > svn:eol-style = native > > Propchange: > commons/proper/math/trunk/src/main/java/org/apache/commons/math/distribution/TriangularDistribution.java > ------------------------------------------------------------------------------ > svn:keywords = Author Date Id Revision > > Added: > commons/proper/math/trunk/src/test/java/org/apache/commons/math/distribution/TriangularDistributionTest.java > URL: > http://svn.apache.org/viewvc/commons/proper/math/trunk/src/test/java/org/apache/commons/math/distribution/TriangularDistributionTest.java?rev=1230419&view=auto > ============================================================================== > --- > commons/proper/math/trunk/src/test/java/org/apache/commons/math/distribution/TriangularDistributionTest.java > (added) > +++ > commons/proper/math/trunk/src/test/java/org/apache/commons/math/distribution/TriangularDistributionTest.java > Thu Jan 12 07:01:43 2012 > @@ -0,0 +1,189 @@ > +/* > + * Licensed to the Apache Software Foundation (ASF) under one or > more > + * contributor license agreements. See the NOTICE file distributed > with > + * this work for additional information regarding copyright > ownership. > + * The ASF licenses this file to You 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 org.apache.commons.math.distribution; > + > +import java.util.Arrays; > + > +import org.apache.commons.math.exception.NumberIsTooLargeException; > +import org.apache.commons.math.exception.NumberIsTooSmallException; > +import org.junit.Assert; > +import org.junit.Test; > + > +/** > + * Test cases for {@link TriangularDistribution}. See class javadoc > for > + * {@link RealDistributionAbstractTest} for further details. > + */ > +public class TriangularDistributionTest extends > RealDistributionAbstractTest { > + > + // --- Override tolerance > ------------------------------------------------- > + > + @Override > + public void setUp() throws Exception { > + super.setUp(); > + setTolerance(1e-4); > + } > + > + //--- Implementations for abstract methods > -------------------------------- > + > + /** > + * Creates the default triangular distribution instance to use > in tests. > + */ > + @Override > + public TriangularDistribution makeDistribution() { > + // Left side 5 wide, right side 10 wide. > + return new TriangularDistribution(-3, 2, 12); > + } > + > + /** > + * Creates the default cumulative probability distribution test > input > + * values. > + */ > + @Override > + public double[] makeCumulativeTestPoints() { > + return new double[] { -3.0001, // below > lower limit > + -3.0, // at lower > limit > + -2.0, -1.0, 0.0, 1.0, // on lower > side > + 2.0, // at mode > + 3.0, 4.0, 10.0, 11.0, // on upper > side > + 12.0, // at upper > limit > + 12.0001 // above > upper limit > + }; > + } > + > + /** > + * Creates the default cumulative probability density test > expected values. > + */ > + @Override > + public double[] makeCumulativeTestValues() { > + // Top at 2 / (b - a) = 2 / (12 - -3) = 2 / 15 = 7.5 > + // Area left = 7.5 * 5 * 0.5 = 18.75 (1/3 of the total > area) > + // Area right = 7.5 * 10 * 0.5 = 37.5 (2/3 of the total > area) > + // Area total = 18.75 + 37.5 = 56.25 > + // Derivative left side = 7.5 / 5 = 1.5 > + // Derivative right side = -7.5 / 10 = -0.75 > + double third = 1 / 3.0; > + double left = 18.75; > + double area = 56.25; > + return new double[] { 0.0, > + 0.0, > + 0.75 / area, 3 / area, 6.75 / area, 12 > / area, > + third, > + (left + 7.125) / area, (left + 13.5) / > area, > + (left + 36) / area, (left + 37.125) / > area, > + 1.0, > + 1.0 > + }; > + } > + > + /** > + * Creates the default inverse cumulative probability > distribution test > + * input values. > + */ > + @Override > + public double[] makeInverseCumulativeTestPoints() { > + // Exclude the points outside the limits, as they have > cumulative > + // probability of zero and one, meaning the inverse returns > the > + // limits and not the points outside the limits. > + double[] points = makeCumulativeTestValues(); > + return Arrays.copyOfRange(points, 1, points.length - 1); > + } > + > + /** > + * Creates the default inverse cumulative probability density > test expected > + * values. > + */ > + @Override > + public double[] makeInverseCumulativeTestValues() { > + // Exclude the points outside the limits, as they have > cumulative > + // probability of zero and one, meaning the inverse returns > the > + // limits and not the points outside the limits. > + double[] points = makeCumulativeTestPoints(); > + return Arrays.copyOfRange(points, 1, points.length - 1); > + } > + > + /** Creates the default probability density test expected > values. */ > + @Override > + public double[] makeDensityTestValues() { > + return new double[] { 0, > + 0, > + 2 / 75.0, 4 / 75.0, 6 / 75.0, 8 / > 75.0, > + 10 / 75.0, > + 9 / 75.0, 8 / 75.0, 2 / 75.0, 1 / > 75.0, > + 0, > + 0 > + }; > + } > + > + //--- Additional test cases > ----------------------------------------------- > + > + /** Test lower bound getter. */ > + @Test > + public void testGetLowerBound() { > + TriangularDistribution distribution = makeDistribution(); > + Assert.assertEquals(-3.0, > distribution.getSupportLowerBound(), 0); > + } > + > + /** Test upper bound getter. */ > + @Test > + public void testGetUpperBound() { > + TriangularDistribution distribution = makeDistribution(); > + Assert.assertEquals(12.0, > distribution.getSupportUpperBound(), 0); > + } > + > + /** Test pre-condition for equal lower/upper limit. */ > + @Test(expected=NumberIsTooLargeException.class) > + public void testPreconditions1() { > + new TriangularDistribution(0, 0, 0); > + } > + > + /** Test pre-condition for lower limit larger than upper limit. > */ > + @Test(expected=NumberIsTooLargeException.class) > + public void testPreconditions2() { > + new TriangularDistribution(1, 1, 0); > + } > + > + /** Test pre-condition for mode larger than upper limit. */ > + @Test(expected=NumberIsTooLargeException.class) > + public void testPreconditions3() { > + new TriangularDistribution(0, 2, 1); > + } > + > + /** Test pre-condition for mode smaller than lower limit. */ > + @Test(expected=NumberIsTooSmallException.class) > + public void testPreconditions4() { > + new TriangularDistribution(2, 1, 3); > + } > + > + /** Test mean/variance. */ > + @Test > + public void testMeanVariance() { > + TriangularDistribution dist; > + > + dist = new TriangularDistribution(0, 0.5, 1.0); > + Assert.assertEquals(dist.getNumericalMean(), 0.5, 0); > + Assert.assertEquals(dist.getNumericalVariance(), 1 / 24.0, > 0); > + > + dist = new TriangularDistribution(0, 1, 1); > + Assert.assertEquals(dist.getNumericalMean(), 2 / 3.0, 0); > + Assert.assertEquals(dist.getNumericalVariance(), 1 / 18.0, > 0); > + > + dist = new TriangularDistribution(-3, 2, 12); > + Assert.assertEquals(dist.getNumericalMean(), 3 + (2 / 3.0), > 0); > + Assert.assertEquals(dist.getNumericalVariance(), 175 / 18.0, > 0); > + } > +} > > Propchange: > commons/proper/math/trunk/src/test/java/org/apache/commons/math/distribution/TriangularDistributionTest.java > ------------------------------------------------------------------------------ > svn:eol-style = native > > Propchange: > commons/proper/math/trunk/src/test/java/org/apache/commons/math/distribution/TriangularDistributionTest.java > ------------------------------------------------------------------------------ > svn:keywords = Author Date Id Revision > > > --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@commons.apache.org For additional commands, e-mail: dev-h...@commons.apache.org