[Python-3000-checkins] r59942 - in python/branches/py3k: Doc/library/random.rst Lib/random.py Lib/test/test_generators.py Lib/test/test_random.py Misc/NEWS Modules/_randommodule.c

raymond.hettinger python-3000-checkins at python.org
Mon Jan 14 00:40:32 CET 2008


Author: raymond.hettinger
Date: Mon Jan 14 00:40:30 2008
New Revision: 59942

Modified:
   python/branches/py3k/Doc/library/random.rst
   python/branches/py3k/Lib/random.py
   python/branches/py3k/Lib/test/test_generators.py
   python/branches/py3k/Lib/test/test_random.py
   python/branches/py3k/Misc/NEWS
   python/branches/py3k/Modules/_randommodule.c
Log:
Remove defunct parts of the random module

Modified: python/branches/py3k/Doc/library/random.rst
==============================================================================
--- python/branches/py3k/Doc/library/random.rst	(original)
+++ python/branches/py3k/Doc/library/random.rst	Mon Jan 14 00:40:30 2008
@@ -28,25 +28,14 @@
 
 The functions supplied by this module are actually bound methods of a hidden
 instance of the :class:`random.Random` class.  You can instantiate your own
-instances of :class:`Random` to get generators that don't share state.  This is
-especially useful for multi-threaded programs, creating a different instance of
-:class:`Random` for each thread, and using the :meth:`jumpahead` method to make
-it likely that the generated sequences seen by each thread don't overlap.
+instances of :class:`Random` to get generators that don't share state.
 
 Class :class:`Random` can also be subclassed if you want to use a different
 basic generator of your own devising: in that case, override the :meth:`random`,
-:meth:`seed`, :meth:`getstate`, :meth:`setstate` and :meth:`jumpahead` methods.
+:meth:`seed`, :meth:`getstate`, and :meth:`setstate`.
 Optionally, a new generator can supply a :meth:`getrandombits` method --- this
 allows :meth:`randrange` to produce selections over an arbitrarily large range.
 
-As an example of subclassing, the :mod:`random` module provides the
-:class:`WichmannHill` class that implements an alternative generator in pure
-Python.  The class provides a backward compatible way to reproduce results from
-earlier versions of Python, which used the Wichmann-Hill algorithm as the core
-generator.  Note that this Wichmann-Hill generator can no longer be recommended:
-its period is too short by contemporary standards, and the sequence generated is
-known to fail some stringent randomness tests.  See the references below for a
-recent variant that repairs these flaws.
 
 Bookkeeping functions:
 
@@ -79,17 +68,6 @@
    the time :func:`setstate` was called.
 
 
-.. function:: jumpahead(n)
-
-   Change the internal state to one different from and likely far away from the
-   current state.  *n* is a non-negative integer which is used to scramble the
-   current state vector.  This is most useful in multi-threaded programs, in
-   conjuction with multiple instances of the :class:`Random` class:
-   :meth:`setstate` or :meth:`seed` can be used to force all instances into the
-   same internal state, and then :meth:`jumpahead` can be used to force the
-   instances' states far apart.
-
-
 .. function:: getrandbits(k)
 
    Returns a python integer with *k* random bits. This method is supplied with
@@ -224,24 +202,6 @@
 
 Alternative Generators:
 
-.. class:: WichmannHill([seed])
-
-   Class that implements the Wichmann-Hill algorithm as the core generator. Has all
-   of the same methods as :class:`Random` plus the :meth:`whseed` method described
-   below.  Because this class is implemented in pure Python, it is not threadsafe
-   and may require locks between calls.  The period of the generator is
-   6,953,607,871,644 which is small enough to require care that two independent
-   random sequences do not overlap.
-
-
-.. function:: whseed([x])
-
-   This is obsolete, supplied for bit-level compatibility with versions of Python
-   prior to 2.1. See :func:`seed` for details.  :func:`whseed` does not guarantee
-   that distinct integer arguments yield distinct internal states, and can yield no
-   more than about 2\*\*24 distinct internal states in all.
-
-
 .. class:: SystemRandom([seed])
 
    Class that uses the :func:`os.urandom` function for generating random numbers
@@ -281,6 +241,4 @@
    equidistributed uniform pseudorandom number generator", ACM Transactions on
    Modeling and Computer Simulation Vol. 8, No. 1, January pp.3-30 1998.
 
-   Wichmann, B. A. & Hill, I. D., "Algorithm AS 183: An efficient and portable
-   pseudo-random number generator", Applied Statistics 31 (1982) 188-190.
 

Modified: python/branches/py3k/Lib/random.py
==============================================================================
--- python/branches/py3k/Lib/random.py	(original)
+++ python/branches/py3k/Lib/random.py	Mon Jan 14 00:40:30 2008
@@ -30,9 +30,6 @@
 
 * The period is 2**19937-1.
 * It is one of the most extensively tested generators in existence.
-* Without a direct way to compute N steps forward, the semantics of
-  jumpahead(n) are weakened to simply jump to another distant state and rely
-  on the large period to avoid overlapping sequences.
 * The random() method is implemented in C, executes in a single Python step,
   and is, therefore, threadsafe.
 
@@ -49,7 +46,7 @@
            "randrange","shuffle","normalvariate","lognormvariate",
            "expovariate","vonmisesvariate","gammavariate",
            "gauss","betavariate","paretovariate","weibullvariate",
-           "getstate","setstate","jumpahead", "WichmannHill", "getrandbits",
+           "getstate","setstate", "getrandbits",
            "SystemRandom"]
 
 NV_MAGICCONST = 4 * _exp(-0.5)/_sqrt(2.0)
@@ -70,14 +67,11 @@
     """Random number generator base class used by bound module functions.
 
     Used to instantiate instances of Random to get generators that don't
-    share state.  Especially useful for multi-threaded programs, creating
-    a different instance of Random for each thread, and using the jumpahead()
-    method to ensure that the generated sequences seen by each thread don't
-    overlap.
+    share state.
 
     Class Random can also be subclassed if you want to use a different basic
     generator of your own devising: in that case, override the following
-    methods:  random(), seed(), getstate(), setstate() and jumpahead().
+    methods:  random(), seed(), getstate(), and setstate().
     Optionally, implement a getrandombits() method so that randrange()
     can cover arbitrarily large ranges.
 
@@ -615,156 +609,6 @@
         u = 1.0 - self.random()
         return alpha * pow(-_log(u), 1.0/beta)
 
-## -------------------- Wichmann-Hill -------------------
-
-class WichmannHill(Random):
-
-    VERSION = 1     # used by getstate/setstate
-
-    def seed(self, a=None):
-        """Initialize internal state from hashable object.
-
-        None or no argument seeds from current time or from an operating
-        system specific randomness source if available.
-
-        If a is not None or an int or long, hash(a) is used instead.
-
-        If a is an int or long, a is used directly.  Distinct values between
-        0 and 27814431486575L inclusive are guaranteed to yield distinct
-        internal states (this guarantee is specific to the default
-        Wichmann-Hill generator).
-        """
-
-        if a is None:
-            try:
-                a = int(_hexlify(_urandom(16)), 16)
-            except NotImplementedError:
-                import time
-                a = int(time.time() * 256) # use fractional seconds
-
-        if not isinstance(a, int):
-            a = hash(a)
-
-        a, x = divmod(a, 30268)
-        a, y = divmod(a, 30306)
-        a, z = divmod(a, 30322)
-        self._seed = int(x)+1, int(y)+1, int(z)+1
-
-        self.gauss_next = None
-
-    def random(self):
-        """Get the next random number in the range [0.0, 1.0)."""
-
-        # Wichman-Hill random number generator.
-        #
-        # Wichmann, B. A. & Hill, I. D. (1982)
-        # Algorithm AS 183:
-        # An efficient and portable pseudo-random number generator
-        # Applied Statistics 31 (1982) 188-190
-        #
-        # see also:
-        #        Correction to Algorithm AS 183
-        #        Applied Statistics 33 (1984) 123
-        #
-        #        McLeod, A. I. (1985)
-        #        A remark on Algorithm AS 183
-        #        Applied Statistics 34 (1985),198-200
-
-        # This part is thread-unsafe:
-        # BEGIN CRITICAL SECTION
-        x, y, z = self._seed
-        x = (171 * x) % 30269
-        y = (172 * y) % 30307
-        z = (170 * z) % 30323
-        self._seed = x, y, z
-        # END CRITICAL SECTION
-
-        # Note:  on a platform using IEEE-754 double arithmetic, this can
-        # never return 0.0 (asserted by Tim; proof too long for a comment).
-        return (x/30269.0 + y/30307.0 + z/30323.0) % 1.0
-
-    def getstate(self):
-        """Return internal state; can be passed to setstate() later."""
-        return self.VERSION, self._seed, self.gauss_next
-
-    def setstate(self, state):
-        """Restore internal state from object returned by getstate()."""
-        version = state[0]
-        if version == 1:
-            version, self._seed, self.gauss_next = state
-        else:
-            raise ValueError("state with version %s passed to "
-                             "Random.setstate() of version %s" %
-                             (version, self.VERSION))
-
-    def jumpahead(self, n):
-        """Act as if n calls to random() were made, but quickly.
-
-        n is an int, greater than or equal to 0.
-
-        Example use:  If you have 2 threads and know that each will
-        consume no more than a million random numbers, create two Random
-        objects r1 and r2, then do
-            r2.setstate(r1.getstate())
-            r2.jumpahead(1000000)
-        Then r1 and r2 will use guaranteed-disjoint segments of the full
-        period.
-        """
-
-        if not n >= 0:
-            raise ValueError("n must be >= 0")
-        x, y, z = self._seed
-        x = int(x * pow(171, n, 30269)) % 30269
-        y = int(y * pow(172, n, 30307)) % 30307
-        z = int(z * pow(170, n, 30323)) % 30323
-        self._seed = x, y, z
-
-    def __whseed(self, x=0, y=0, z=0):
-        """Set the Wichmann-Hill seed from (x, y, z).
-
-        These must be integers in the range [0, 256).
-        """
-
-        if not type(x) == type(y) == type(z) == int:
-            raise TypeError('seeds must be integers')
-        if not (0 <= x < 256 and 0 <= y < 256 and 0 <= z < 256):
-            raise ValueError('seeds must be in range(0, 256)')
-        if 0 == x == y == z:
-            # Initialize from current time
-            import time
-            t = int(time.time() * 256)
-            t = int((t&0xffffff) ^ (t>>24))
-            t, x = divmod(t, 256)
-            t, y = divmod(t, 256)
-            t, z = divmod(t, 256)
-        # Zero is a poor seed, so substitute 1
-        self._seed = (x or 1, y or 1, z or 1)
-
-        self.gauss_next = None
-
-    def whseed(self, a=None):
-        """Seed from hashable object's hash code.
-
-        None or no argument seeds from current time.  It is not guaranteed
-        that objects with distinct hash codes lead to distinct internal
-        states.
-
-        This is obsolete, provided for compatibility with the seed routine
-        used prior to Python 2.1.  Use the .seed() method instead.
-        """
-
-        if a is None:
-            self.__whseed()
-            return
-        a = hash(a)
-        a, x = divmod(a, 256)
-        a, y = divmod(a, 256)
-        a, z = divmod(a, 256)
-        x = (x + a) % 256 or 1
-        y = (y + a) % 256 or 1
-        z = (z + a) % 256 or 1
-        self.__whseed(x, y, z)
-
 ## --------------- Operating System Random Source  ------------------
 
 class SystemRandom(Random):
@@ -789,10 +633,9 @@
         x = int(_hexlify(_urandom(bytes)), 16)
         return x >> (bytes * 8 - k)             # trim excess bits
 
-    def _stub(self, *args, **kwds):
+    def seed(self, *args, **kwds):
         "Stub method.  Not used for a system random number generator."
         return None
-    seed = jumpahead = _stub
 
     def _notimplemented(self, *args, **kwds):
         "Method should not be called for a system random number generator."
@@ -866,7 +709,6 @@
 weibullvariate = _inst.weibullvariate
 getstate = _inst.getstate
 setstate = _inst.setstate
-jumpahead = _inst.jumpahead
 getrandbits = _inst.getrandbits
 
 if __name__ == '__main__':

Modified: python/branches/py3k/Lib/test/test_generators.py
==============================================================================
--- python/branches/py3k/Lib/test/test_generators.py	(original)
+++ python/branches/py3k/Lib/test/test_generators.py	Mon Jan 14 00:40:30 2008
@@ -444,7 +444,7 @@
 >>> roots = sets[:]
 
 >>> import random
->>> gen = random.WichmannHill(42)
+>>> gen = random.Random(42)
 >>> while 1:
 ...     for s in sets:
 ...         print(" %s->%s" % (s, s.find()), end='')
@@ -458,29 +458,29 @@
 ...     else:
 ...         break
  A->A B->B C->C D->D E->E F->F G->G H->H I->I J->J K->K L->L M->M
-merged D into G
- A->A B->B C->C D->G E->E F->F G->G H->H I->I J->J K->K L->L M->M
-merged C into F
- A->A B->B C->F D->G E->E F->F G->G H->H I->I J->J K->K L->L M->M
+merged I into A
+ A->A B->B C->C D->D E->E F->F G->G H->H I->A J->J K->K L->L M->M
+merged D into C
+ A->A B->B C->C D->C E->E F->F G->G H->H I->A J->J K->K L->L M->M
+merged K into H
+ A->A B->B C->C D->C E->E F->F G->G H->H I->A J->J K->H L->L M->M
 merged L into A
- A->A B->B C->F D->G E->E F->F G->G H->H I->I J->J K->K L->A M->M
-merged H into E
- A->A B->B C->F D->G E->E F->F G->G H->E I->I J->J K->K L->A M->M
-merged B into E
- A->A B->E C->F D->G E->E F->F G->G H->E I->I J->J K->K L->A M->M
+ A->A B->B C->C D->C E->E F->F G->G H->H I->A J->J K->H L->A M->M
+merged E into A
+ A->A B->B C->C D->C E->A F->F G->G H->H I->A J->J K->H L->A M->M
+merged B into G
+ A->A B->G C->C D->C E->A F->F G->G H->H I->A J->J K->H L->A M->M
+merged A into F
+ A->F B->G C->C D->C E->F F->F G->G H->H I->F J->J K->H L->F M->M
+merged H into G
+ A->F B->G C->C D->C E->F F->F G->G H->G I->F J->J K->G L->F M->M
+merged F into J
+ A->J B->G C->C D->C E->J F->J G->G H->G I->J J->J K->G L->J M->M
+merged M into C
+ A->J B->G C->C D->C E->J F->J G->G H->G I->J J->J K->G L->J M->C
 merged J into G
- A->A B->E C->F D->G E->E F->F G->G H->E I->I J->G K->K L->A M->M
-merged E into G
- A->A B->G C->F D->G E->G F->F G->G H->G I->I J->G K->K L->A M->M
-merged M into G
- A->A B->G C->F D->G E->G F->F G->G H->G I->I J->G K->K L->A M->G
-merged I into K
- A->A B->G C->F D->G E->G F->F G->G H->G I->K J->G K->K L->A M->G
-merged K into A
- A->A B->G C->F D->G E->G F->F G->G H->G I->A J->G K->A L->A M->G
-merged F into A
- A->A B->G C->A D->G E->G F->A G->G H->G I->A J->G K->A L->A M->G
-merged A into G
+ A->G B->G C->C D->C E->G F->G G->G H->G I->G J->G K->G L->G M->C
+merged C into G
  A->G B->G C->G D->G E->G F->G G->G H->G I->G J->G K->G L->G M->G
 
 """

Modified: python/branches/py3k/Lib/test/test_random.py
==============================================================================
--- python/branches/py3k/Lib/test/test_random.py	(original)
+++ python/branches/py3k/Lib/test/test_random.py	Mon Jan 14 00:40:30 2008
@@ -42,21 +42,6 @@
         self.assertRaises(TypeError, self.gen.seed, 1, 2)
         self.assertRaises(TypeError, type(self.gen), [])
 
-    def test_jumpahead(self):
-        self.gen.seed()
-        state1 = self.gen.getstate()
-        self.gen.jumpahead(100)
-        state2 = self.gen.getstate()    # s/b distinct from state1
-        self.assertNotEqual(state1, state2)
-        self.gen.jumpahead(100)
-        state3 = self.gen.getstate()    # s/b distinct from state2
-        self.assertNotEqual(state2, state3)
-
-        self.assertRaises(TypeError, self.gen.jumpahead)  # needs an arg
-        self.assertRaises(TypeError, self.gen.jumpahead, "ick")  # wrong type
-        self.assertRaises(TypeError, self.gen.jumpahead, 2.3)  # wrong type
-        self.assertRaises(TypeError, self.gen.jumpahead, 2, 3)  # too many
-
     def test_sample(self):
         # For the entire allowable range of 0 <= k <= N, validate that
         # the sample is of the correct length and contains only unique items
@@ -157,48 +142,6 @@
             f.close()
             self.assertEqual(r.randrange(1000), value)
 
-class WichmannHill_TestBasicOps(TestBasicOps):
-    gen = random.WichmannHill()
-
-    def test_setstate_first_arg(self):
-        self.assertRaises(ValueError, self.gen.setstate, (2, None, None))
-
-    def test_strong_jumpahead(self):
-        # tests that jumpahead(n) semantics correspond to n calls to random()
-        N = 1000
-        s = self.gen.getstate()
-        self.gen.jumpahead(N)
-        r1 = self.gen.random()
-        # now do it the slow way
-        self.gen.setstate(s)
-        for i in range(N):
-            self.gen.random()
-        r2 = self.gen.random()
-        self.assertEqual(r1, r2)
-
-    def test_gauss_with_whseed(self):
-        # Ensure that the seed() method initializes all the hidden state.  In
-        # particular, through 2.2.1 it failed to reset a piece of state used
-        # by (and only by) the .gauss() method.
-
-        for seed in 1, 12, 123, 1234, 12345, 123456, 654321:
-            self.gen.whseed(seed)
-            x1 = self.gen.random()
-            y1 = self.gen.gauss(0, 1)
-
-            self.gen.whseed(seed)
-            x2 = self.gen.random()
-            y2 = self.gen.gauss(0, 1)
-
-            self.assertEqual(x1, x2)
-            self.assertEqual(y1, y2)
-
-    def test_bigrand(self):
-        # Verify warnings are raised when randrange is too large for random()
-        with test_support.catch_warning():
-            warnings.filterwarnings("error", "Underlying random")
-            self.assertRaises(UserWarning, self.gen.randrange, 2**60)
-
 class SystemRandom_TestBasicOps(TestBasicOps):
     gen = random.SystemRandom()
 
@@ -214,10 +157,6 @@
         # Doesn't need to do anything except not fail
         self.gen.seed(100)
 
-    def test_jumpahead(self):
-        # Doesn't need to do anything except not fail
-        self.gen.jumpahead(100)
-
     def test_gauss(self):
         self.gen.gauss_next = None
         self.gen.seed(100)
@@ -541,8 +480,7 @@
 
 
 def test_main(verbose=None):
-    testclasses =    [WichmannHill_TestBasicOps,
-                      MersenneTwister_TestBasicOps,
+    testclasses =    [MersenneTwister_TestBasicOps,
                       TestDistributions,
                       TestModule]
 

Modified: python/branches/py3k/Misc/NEWS
==============================================================================
--- python/branches/py3k/Misc/NEWS	(original)
+++ python/branches/py3k/Misc/NEWS	Mon Jan 14 00:40:30 2008
@@ -352,6 +352,9 @@
 Library
 -------
 
+- Removed defunct parts of the random module (the Wichmann-Hill generator
+  and the jumpahead() method).
+
 - Patch #467924: add ZipFile.extract() and ZipFile.extractall() in the
   zipfile module.
 

Modified: python/branches/py3k/Modules/_randommodule.c
==============================================================================
--- python/branches/py3k/Modules/_randommodule.c	(original)
+++ python/branches/py3k/Modules/_randommodule.c	Mon Jan 14 00:40:30 2008
@@ -369,72 +369,6 @@
 	return Py_None;
 }
 
-/*
-Jumpahead should be a fast way advance the generator n-steps ahead, but
-lacking a formula for that, the next best is to use n and the existing
-state to create a new state far away from the original.
-
-The generator uses constant spaced additive feedback, so shuffling the
-state elements ought to produce a state which would not be encountered
-(in the near term) by calls to random().  Shuffling is normally
-implemented by swapping the ith element with another element ranging
-from 0 to i inclusive.  That allows the element to have the possibility
-of not being moved.  Since the goal is to produce a new, different
-state, the swap element is ranged from 0 to i-1 inclusive.  This assures
-that each element gets moved at least once.
-
-To make sure that consecutive calls to jumpahead(n) produce different
-states (even in the rare case of involutory shuffles), i+1 is added to
-each element at position i.  Successive calls are then guaranteed to
-have changing (growing) values as well as shuffled positions.
-
-Finally, the self->index value is set to N so that the generator itself
-kicks in on the next call to random().	This assures that all results
-have been through the generator and do not just reflect alterations to
-the underlying state.
-*/
-
-static PyObject *
-random_jumpahead(RandomObject *self, PyObject *n)
-{
-	long i, j;
-	PyObject *iobj;
-	PyObject *remobj;
-	unsigned long *mt, tmp;
-
-	if (!PyLong_Check(n)) {
-		PyErr_Format(PyExc_TypeError, "jumpahead requires an "
-			     "integer, not '%s'",
-			     Py_TYPE(n)->tp_name);
-		return NULL;
-	}
-
-	mt = self->state;
-	for (i = N-1; i > 1; i--) {
-		iobj = PyLong_FromLong(i);
-		if (iobj == NULL)
-			return NULL;
-		remobj = PyNumber_Remainder(n, iobj);
-		Py_DECREF(iobj);
-		if (remobj == NULL)
-			return NULL;
-		j = PyLong_AsLong(remobj);
-		Py_DECREF(remobj);
-		if (j == -1L && PyErr_Occurred())
-			return NULL;
-		tmp = mt[i];
-		mt[i] = mt[j];
-		mt[j] = tmp;
-	}
-
-	for (i = 0; i < N; i++)
-		mt[i] += i+1;
-
-	self->index = N;
-	Py_INCREF(Py_None);
-	return Py_None;
-}
-
 static PyObject *
 random_getrandbits(RandomObject *self, PyObject *args)
 {
@@ -506,9 +440,6 @@
 		PyDoc_STR("getstate() -> tuple containing the current state.")},
 	{"setstate",	  (PyCFunction)random_setstate,  METH_O,
 		PyDoc_STR("setstate(state) -> None.  Restores generator state.")},
-	{"jumpahead",	(PyCFunction)random_jumpahead,	METH_O,
-		PyDoc_STR("jumpahead(int) -> None.  Create new state from "
-			  "existing state and integer.")},
 	{"getrandbits",	(PyCFunction)random_getrandbits,  METH_VARARGS,
 		PyDoc_STR("getrandbits(k) -> x.  Generates a long int with "
 			  "k random bits.")},


More information about the Python-3000-checkins mailing list