You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by ps...@apache.org on 2011/11/06 08:17:51 UTC

svn commit: r1198165 - in /commons/proper/math/trunk: ./ src/main/java/org/apache/commons/math/exception/util/ src/main/java/org/apache/commons/math/random/ src/main/resources/META-INF/localization/ src/site/xdoc/ src/test/java/org/apache/commons/math/...

Author: psteitz
Date: Sun Nov  6 07:17:50 2011
New Revision: 1198165

URL: http://svn.apache.org/viewvc?rev=1198165&view=rev
Log:
Added stable random generator based on Chambers-Mallows-Stuck method.  Contributed by Pavel Ryhzov.  JIRA: MATH-462.

Added:
    commons/proper/math/trunk/src/main/java/org/apache/commons/math/random/StableRandomGenerator.java   (with props)
    commons/proper/math/trunk/src/test/java/org/apache/commons/math/random/StableRandomGeneratorTest.java   (with props)
Modified:
    commons/proper/math/trunk/pom.xml
    commons/proper/math/trunk/src/main/java/org/apache/commons/math/exception/util/LocalizedFormats.java
    commons/proper/math/trunk/src/main/resources/META-INF/localization/LocalizedFormats_fr.properties
    commons/proper/math/trunk/src/site/xdoc/changes.xml

Modified: commons/proper/math/trunk/pom.xml
URL: http://svn.apache.org/viewvc/commons/proper/math/trunk/pom.xml?rev=1198165&r1=1198164&r2=1198165&view=diff
==============================================================================
--- commons/proper/math/trunk/pom.xml (original)
+++ commons/proper/math/trunk/pom.xml Sun Nov  6 07:17:50 2011
@@ -225,6 +225,9 @@
       <name>Matthew Rowles</name>
     </contributor>
     <contributor>
+      <name>Pavel Ryzhov</name>
+    </contributor>
+    <contributor>
       <name>Joni Salonen</name>
     </contributor>
     <contributor>

Modified: commons/proper/math/trunk/src/main/java/org/apache/commons/math/exception/util/LocalizedFormats.java
URL: http://svn.apache.org/viewvc/commons/proper/math/trunk/src/main/java/org/apache/commons/math/exception/util/LocalizedFormats.java?rev=1198165&r1=1198164&r2=1198165&view=diff
==============================================================================
--- commons/proper/math/trunk/src/main/java/org/apache/commons/math/exception/util/LocalizedFormats.java (original)
+++ commons/proper/math/trunk/src/main/java/org/apache/commons/math/exception/util/LocalizedFormats.java Sun Nov  6 07:17:50 2011
@@ -271,6 +271,8 @@ public enum LocalizedFormats implements 
     OUT_OF_RANGE_ROOT_OF_UNITY_INDEX("out of range root of unity index {0} (must be in [{1};{2}])"),
     OUT_OF_RANGE("out of range"), /* keep */
     OUT_OF_RANGE_SIMPLE("{0} out of [{1}, {2}] range"), /* keep */
+    OUT_OF_RANGE_LEFT("{0} out of ({1}, {2}] range"),
+    OUT_OF_RANGE_RIGHT("{0} out of [{1}, {2}) range"),
     OUTLINE_BOUNDARY_LOOP_OPEN("an outline boundary loop is open"),
     OVERFLOW("overflow"), /* keep */
     OVERFLOW_IN_FRACTION("overflow in fraction {0}/{1}, cannot negate"),

Added: commons/proper/math/trunk/src/main/java/org/apache/commons/math/random/StableRandomGenerator.java
URL: http://svn.apache.org/viewvc/commons/proper/math/trunk/src/main/java/org/apache/commons/math/random/StableRandomGenerator.java?rev=1198165&view=auto
==============================================================================
--- commons/proper/math/trunk/src/main/java/org/apache/commons/math/random/StableRandomGenerator.java (added)
+++ commons/proper/math/trunk/src/main/java/org/apache/commons/math/random/StableRandomGenerator.java Sun Nov  6 07:17:50 2011
@@ -0,0 +1,131 @@
+/*
+ * 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.random;
+
+import org.apache.commons.math.exception.NullArgumentException;
+import org.apache.commons.math.exception.OutOfRangeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * <p>This class provides a stable normalized random generator. It samples from a stable
+ * distribution with location parameter 0 and scale 1.</p>
+ *
+ * <p>The implementation uses the Chambers-Mallows-Stuck method as described in
+ * <i>Handbook of computational statistics: concepts and methods</i> by
+ * James E. Gentle, Wolfgang HŠrdle, Yuichi Mori.</p>
+ *
+ * @version $Id$
+ * @since 3.0
+ */
+public class StableRandomGenerator implements NormalizedRandomGenerator {
+    /** Underlying generator. */
+    private final RandomGenerator generator;
+
+    /** stability parameter */
+    private final double alpha;
+
+    /** skewness parameter */
+    private final double beta;
+
+    /** cache of expression value used in generation */
+    private final double zeta;
+
+    /**
+     * Create a new generator.
+     *
+     * @param generator underlying random generator to use
+     * @param alpha Stability parameter. Must be in range (0, 2]
+     * @param beta Skewness parameter. Must be in range [-1, 1]
+     */
+    public StableRandomGenerator(final RandomGenerator generator, double alpha,
+            double beta) {
+        if (generator == null) {
+            throw new NullArgumentException();
+        }
+
+        if (!(alpha > 0d && alpha <= 2d)) {
+            throw new OutOfRangeException(LocalizedFormats.OUT_OF_RANGE_LEFT,
+                    alpha, 0, 2);
+        }
+
+        if (!(beta >= -1d && beta <= 1d)) {
+            throw new OutOfRangeException(LocalizedFormats.OUT_OF_RANGE_SIMPLE,
+                    beta, -1, 1);
+        }
+
+        this.generator = generator;
+        this.alpha = alpha;
+        this.beta = beta;
+        if (alpha < 2d && beta != 0d) {
+            zeta = beta * FastMath.tan(FastMath.PI * alpha / 2);
+        } else {
+            zeta = 0d;
+        }
+    }
+
+    /**
+     * Generate a random scalar with zero location and unit scale.
+     *
+     * @return a random scalar with zero location and unit scale
+     */
+    public double nextNormalizedDouble() {
+        // we need 2 uniform random numbers to calculate omega and phi
+        double omega = -FastMath.log(generator.nextDouble());
+        double phi = FastMath.PI * (generator.nextDouble() - 0.5);
+
+        // Normal distribution case (Box-Muller algorithm)
+        if (alpha == 2d) {
+            return FastMath.sqrt(2d * omega) * FastMath.sin(phi);
+        }
+
+        double x;
+        // when beta = 0, zeta is zero as well
+        // Thus we can exclude it from the formula
+        if (beta == 0d) {
+            // Cauchy distribution case
+            if (alpha == 1d) {
+                x = FastMath.tan(phi);
+            } else {
+                x = FastMath.pow(omega * FastMath.cos((1 - alpha) * phi),
+                    1d / alpha - 1d) *
+                    FastMath.sin(alpha * phi) /
+                    FastMath.pow(FastMath.cos(phi), 1d / alpha);
+            }
+        } else {
+            // Generic stable distribution
+            double cosPhi = FastMath.cos(phi);
+            // to avoid rounding errors around alpha = 1
+            if (FastMath.abs(alpha - 1d) > 1e-8) {
+                double alphaPhi = alpha * phi;
+                double invAlphaPhi = phi - alphaPhi;
+                x = (FastMath.sin(alphaPhi) + zeta * FastMath.cos(alphaPhi)) / cosPhi *
+                    (FastMath.cos(invAlphaPhi) + zeta * FastMath.sin(invAlphaPhi)) /
+                     FastMath.pow(omega * cosPhi, (1 - alpha) / alpha);
+            } else {
+                double betaPhi = FastMath.PI / 2 + beta * phi;
+                x = 2d / FastMath.PI * (betaPhi * FastMath.tan(phi) - beta *
+                    FastMath.log(FastMath.PI / 2d * omega * cosPhi / betaPhi));
+
+                if (alpha != 1d) {
+                    x = x + beta * FastMath.tan(FastMath.PI * alpha / 2);
+                }
+            }
+        }
+        return x;
+    }
+}

Propchange: commons/proper/math/trunk/src/main/java/org/apache/commons/math/random/StableRandomGenerator.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/proper/math/trunk/src/main/java/org/apache/commons/math/random/StableRandomGenerator.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision Id

Propchange: commons/proper/math/trunk/src/main/java/org/apache/commons/math/random/StableRandomGenerator.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: commons/proper/math/trunk/src/main/resources/META-INF/localization/LocalizedFormats_fr.properties
URL: http://svn.apache.org/viewvc/commons/proper/math/trunk/src/main/resources/META-INF/localization/LocalizedFormats_fr.properties?rev=1198165&r1=1198164&r2=1198165&view=diff
==============================================================================
--- commons/proper/math/trunk/src/main/resources/META-INF/localization/LocalizedFormats_fr.properties (original)
+++ commons/proper/math/trunk/src/main/resources/META-INF/localization/LocalizedFormats_fr.properties Sun Nov  6 07:17:50 2011
@@ -237,6 +237,8 @@ SIGNIFICANCE_LEVEL = niveau de significa
 OUT_OF_ORDER_ABSCISSA_ARRAY = les abscisses doivent \u00eatre en ordre strictement croissant, mais l''\u00e9l\u00e9ment {0} vaut {1} alors que l''\u00e9l\u00e9ment {2} vaut {3}
 OUT_OF_RANGE_ROOT_OF_UNITY_INDEX = l''indice de racine de l''unit\u00e9 {0} est hors du domaine autoris\u00e9 [{1};{2}]
 OUT_OF_RANGE_SIMPLE = {0} hors du domaine [{1}, {2}]
+OUT_OF_RANGE_LEFT = {0} hors du domaine ({1}, {2}]
+OUT_OF_RANGE_RIGHT = {0} hors du domaine [{1}, {2})
 OUT_OF_RANGE = hors domaine
 OVERFLOW = d\u00e9passement de capacit\u00e9
 OVERFLOW_IN_FRACTION = d\u00e9passement de capacit\u00e9 pour la fraction {0}/{1}, son signe ne peut \u00eatre chang\u00e9

Modified: commons/proper/math/trunk/src/site/xdoc/changes.xml
URL: http://svn.apache.org/viewvc/commons/proper/math/trunk/src/site/xdoc/changes.xml?rev=1198165&r1=1198164&r2=1198165&view=diff
==============================================================================
--- commons/proper/math/trunk/src/site/xdoc/changes.xml (original)
+++ commons/proper/math/trunk/src/site/xdoc/changes.xml Sun Nov  6 07:17:50 2011
@@ -52,6 +52,9 @@ The <action> type attribute can be add,u
     If the output is not quite correct, check for invisible trailing spaces!
      -->
     <release version="3.0" date="TBD" description="TBD">
+      <action dev="psteitz" type="add" issue="MATH-462" due-to="Pavel Ryzhof">
+        Added stable random generator based on Chambers-Mallows-Stuck method.
+      </action>
       <action dev="psteitz" type="update" issue="MATH-701">
         Changed the default seed used for RandomDataImpl, AbstractWell and MersenneTwister
         PRNGs to add the system identity hash code of the instance to the current system

Added: commons/proper/math/trunk/src/test/java/org/apache/commons/math/random/StableRandomGeneratorTest.java
URL: http://svn.apache.org/viewvc/commons/proper/math/trunk/src/test/java/org/apache/commons/math/random/StableRandomGeneratorTest.java?rev=1198165&view=auto
==============================================================================
--- commons/proper/math/trunk/src/test/java/org/apache/commons/math/random/StableRandomGeneratorTest.java (added)
+++ commons/proper/math/trunk/src/test/java/org/apache/commons/math/random/StableRandomGeneratorTest.java Sun Nov  6 07:17:50 2011
@@ -0,0 +1,128 @@
+/*
+ * 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.random;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.math.exception.OutOfRangeException;
+import org.apache.commons.math.stat.StatUtils;
+import org.apache.commons.math.stat.descriptive.DescriptiveStatistics;
+
+/**
+ * The class <code>StableRandomGeneratorTest</code> contains tests for the class
+ * {@link <code>StableRandomGenerator</code>}
+ * 
+ * @version $Revision$
+ */
+public class StableRandomGeneratorTest extends TestCase {
+
+    private RandomGenerator rg = new Well19937c(100);
+    private final static int sampleSize = 10000;
+
+    /**
+     * Construct new test instance
+     * 
+     * @param name the test name
+     */
+    public StableRandomGeneratorTest(String name) {
+        super(name);
+    }
+
+    /**
+     * Run the double nextDouble() method test Due to leptokurtic property the
+     * acceptance range is widened.
+     */
+    public void testNextDouble() {
+        StableRandomGenerator generator = new StableRandomGenerator(rg, 1.3,
+                0.1);
+        double[] sample = new double[2 * sampleSize];
+        for (int i = 0; i < sample.length; ++i) {
+            sample[i] = generator.nextNormalizedDouble();
+        }
+        assertEquals(0.0, StatUtils.mean(sample), 0.2);
+    }
+
+    /**
+     * If alpha = 2, than it must be Gaussian distribution
+     */
+    public void testGaussianCase() {
+        StableRandomGenerator generator = new StableRandomGenerator(rg, 2d, 0.0);
+
+        double[] sample = new double[sampleSize];
+        for (int i = 0; i < sample.length; ++i) {
+            sample[i] = generator.nextNormalizedDouble();
+        }
+        assertEquals(0.0, StatUtils.mean(sample), 0.02);
+        assertEquals(1.0, StatUtils.variance(sample), 0.02);
+    }
+
+    /**
+     * If alpha = 1, than it must be Cauchy distribution
+     */
+    public void testCauchyCase() {
+        StableRandomGenerator generator = new StableRandomGenerator(rg, 1d, 0.0);
+        DescriptiveStatistics summary = new DescriptiveStatistics();
+
+        for (int i = 0; i < sampleSize; ++i) {
+            double sample = generator.nextNormalizedDouble();
+            summary.addValue(sample);
+        }
+
+        // Standard Cauchy distribution should have zero median and mode
+        double median = summary.getPercentile(50);
+        assertEquals(0.0, median, 0.2);
+    }
+
+    /**
+     * Input parameter range tests
+     */
+    public void testAlphaRangeBelowZero() {
+        try {
+            StableRandomGenerator generator = new StableRandomGenerator(rg,
+                    -1.0, 0.0);
+        } catch (OutOfRangeException e) {
+            assertEquals(-1.0, e.getArgument());
+        }
+    }
+
+    public void testAlphaRangeAboveTwo() {
+        try {
+            StableRandomGenerator generator = new StableRandomGenerator(rg,
+                    3.0, 0.0);
+        } catch (OutOfRangeException e) {
+            assertEquals(3.0, e.getArgument());
+        }
+    }
+
+    public void testBetaRangeBelowMinusOne() {
+        try {
+            StableRandomGenerator generator = new StableRandomGenerator(rg,
+                    1.0, -2.0);
+        } catch (OutOfRangeException e) {
+            assertEquals(-2.0, e.getArgument());
+        }
+    }
+
+    public void testBetaRangeAboveOne() {
+        try {
+            StableRandomGenerator generator = new StableRandomGenerator(rg,
+                    1.0, 2.0);
+        } catch (OutOfRangeException e) {
+            assertEquals(2.0, e.getArgument());
+        }
+    }
+}
\ No newline at end of file

Propchange: commons/proper/math/trunk/src/test/java/org/apache/commons/math/random/StableRandomGeneratorTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/proper/math/trunk/src/test/java/org/apache/commons/math/random/StableRandomGeneratorTest.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision Id

Propchange: commons/proper/math/trunk/src/test/java/org/apache/commons/math/random/StableRandomGeneratorTest.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain