[Python-checkins] python/nondist/sandbox/twister random.py,1.6,1.7

rhettinger@users.sourceforge.net rhettinger@users.sourceforge.net
Mon, 23 Dec 2002 21:13:43 -0800


Update of /cvsroot/python/python/nondist/sandbox/twister
In directory sc8-pr-cvs1:/tmp/cvs-serv29048

Modified Files:
	random.py 
Log Message:
Used "import as" to simplify the import of _random.

Replace the module docstring's WichmannHill specific comments with
comments specific to the Mersenne Twister.

Moved the non-statistical tests to test_random.py.



Index: random.py
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/twister/random.py,v
retrieving revision 1.6
retrieving revision 1.7
diff -C2 -d -r1.6 -r1.7
*** random.py	23 Dec 2002 04:31:35 -0000	1.6
--- random.py	24 Dec 2002 05:13:41 -0000	1.7
***************
*** 25,77 ****
             von Mises
  
! Translated from anonymously contributed C/C++ source.
! 
! Multi-threading note:  the random number generator used here is not thread-
! safe; it is possible that two calls return the same random value.  However,
! you can instantiate a different instance of Random() in each thread to get
! generators that don't share state, then use .setstate() and .jumpahead() to
! move the generators to disjoint segments of the full period.  For example,
! 
! def create_generators(num, delta, firstseed=None):
!     ""\"Return list of num distinct generators.
!     Each generator has its own unique segment of delta elements from
!     Random.random()'s full period.
!     Seed the first generator with optional arg firstseed (default is
!     None, to seed from current time).
!     ""\"
! 
!     from random import Random
!     g = Random(firstseed)
!     result = [g]
!     for i in range(num - 1):
!         laststate = g.getstate()
!         g = Random()
!         g.setstate(laststate)
!         g.jumpahead(delta)
!         result.append(g)
!     return result
! 
! gens = create_generators(10, 1000000)
! 
! That creates 10 distinct generators, which can be passed out to 10 distinct
! threads.  The generators don't share state so can be called safely in
! parallel.  So long as no thread calls its g.random() more than a million
! times (the second argument to create_generators), the sequences seen by
! each thread will not overlap.
! 
! The period of the underlying Wichmann-Hill generator is 6,953,607,871,644,
! and that limits how far this technique can be pushed.
  
! Just for fun, note that since we know the period, .jumpahead() can also be
! used to "move backward in time":
  
- >>> g = Random(42)  # arbitrary
- >>> g.random()
- 0.25420336316883324
- >>> g.jumpahead(6953607871644L - 1) # move *back* one
- >>> g.random()
- 0.25420336316883324
  """
- # XXX The docstring sucks.
  
  from math import log as _log, exp as _exp, pi as _pi, e as _e
--- 25,40 ----
             von Mises
  
! General notes on the underlying Mersenne Twister core generator:
  
! * The period is 2**19937-1.
! * It passes the Diehard battery of tests for randomness
! * 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.
  
  """
  
  from math import log as _log, exp as _exp, pi as _pi, e as _e
***************
*** 83,113 ****
             "cunifvariate","expovariate","vonmisesvariate","gammavariate",
             "stdgamma","gauss","betavariate","paretovariate","weibullvariate",
!            "getstate","setstate","jumpahead","whseed"]
! 
! def _verify(name, computed, expected):
!     if abs(computed - expected) > 1e-7:
!         raise ValueError(
!             "computed value for %s deviates too much "
!             "(computed %g, expected %g)" % (name, computed, expected))
  
  NV_MAGICCONST = 4 * _exp(-0.5)/_sqrt(2.0)
- _verify('NV_MAGICCONST', NV_MAGICCONST, 1.71552776992141)
- 
  TWOPI = 2.0*_pi
- _verify('TWOPI', TWOPI, 6.28318530718)
- 
  LOG4 = _log(4.0)
- _verify('LOG4', LOG4, 1.38629436111989)
- 
  SG_MAGICCONST = 1.0 + _log(4.5)
- _verify('SG_MAGICCONST', SG_MAGICCONST, 2.50407739677627)
- 
- del _verify
  
  # Translated by Guido van Rossum from C source provided by
! # Adrian Baddeley.
  
! import MersenneTwister
! CoreGenerator = MersenneTwister.Random
  
  class Random(CoreGenerator):
--- 46,61 ----
             "cunifvariate","expovariate","vonmisesvariate","gammavariate",
             "stdgamma","gauss","betavariate","paretovariate","weibullvariate",
!            "getstate","setstate","jumpahead"]
  
  NV_MAGICCONST = 4 * _exp(-0.5)/_sqrt(2.0)
  TWOPI = 2.0*_pi
  LOG4 = _log(4.0)
  SG_MAGICCONST = 1.0 + _log(4.5)
  
  # Translated by Guido van Rossum from C source provided by
! # Adrian Baddeley.  Adapted by Raymond Hettinger for use with
! # the Mersenne Twister core generator.
  
! from _random import Random as CoreGenerator
  
  class Random(CoreGenerator):
***************
*** 131,135 ****
          """Initialize an instance.
  
!         Optional argument "x" controls seeding, as for Random.seed().
          """
          self.gauss_next = None
--- 79,83 ----
          """Initialize an instance.
  
!         Optional argument x controls seeding, as for Random.seed().
          """
          self.gauss_next = None
***************
*** 143,147 ****
          """Restore internal state from object returned by getstate()."""
          version = state[0]
!         if version == 1:
              version, internalstate, self.gauss_next = state
              CoreGenerator.setstate(self, internalstate)
--- 91,95 ----
          """Restore internal state from object returned by getstate()."""
          version = state[0]
!         if version == 2:
              version, internalstate, self.gauss_next = state
              CoreGenerator.setstate(self, internalstate)
***************
*** 262,265 ****
--- 210,216 ----
          This is especially fast and space efficient for sampling from a
          large population:   sample(xrange(10000000), 60)
+ 
+         Optional arg random is a 0-argument function returning a random
+         float in [0.0, 1.0); by default, the standard random.random.
          """
  
***************
*** 784,797 ****
                (avg, stddev, smallest, largest)
  
- def _test_sample(n):
-     # For the entire allowable range of 0 <= k <= n, validate that
-     # the sample is of the correct length and contains only unique items
-     population = xrange(n)
-     for k in xrange(n+1):
-         s = sample(population, k)
-         uniq = dict.fromkeys(s)
-         assert len(uniq) == len(s) == k
-         assert None not in uniq
- 
  def _sample_generator(n, k):
      # Return a fixed element from the sample.  Validates random ordering.
--- 735,738 ----
***************
*** 799,806 ****
  
  def _test(N=2000):
-     print 'TWOPI         =', TWOPI
-     print 'LOG4          =', LOG4
-     print 'NV_MAGICCONST =', NV_MAGICCONST
-     print 'SG_MAGICCONST =', SG_MAGICCONST
      _test_generator(N, 'random()')
      _test_generator(N, 'normalvariate(0.0, 1.0)')
--- 740,743 ----
***************
*** 824,849 ****
      _test_generator(N, '_sample_generator(50, 5)')  # expected s.d.: 14.4
      _test_generator(N, '_sample_generator(50, 45)') # expected s.d.: 14.4
-     _test_sample(500)
- 
-     if isinstance(_inst, WichmannHill):
-         # Test jumpahead.
-         s = getstate()
-         jumpahead(N)
-         r1 = random()
-         # now do it the slow way
-         setstate(s)
-         for i in range(N):
-             random()
-         r2 = random()
-         if r1 != r2:
-             raise ValueError("jumpahead test failed " + `(N, r1, r2)`)
  
  # Create one instance, seeded from current time, and export its methods
! # as module-level functions.  The functions are not threadsafe, and state
! # is shared across all uses (both in the user's code and in the Python
! # libraries), but that's fine for most programs and is easier for the
! # casual user than making them instantiate their own Random() instance.
  
! _inst = Random()        # or _inst = WichmannHill()
  seed = _inst.seed
  random = _inst.random
--- 761,772 ----
      _test_generator(N, '_sample_generator(50, 5)')  # expected s.d.: 14.4
      _test_generator(N, '_sample_generator(50, 45)') # expected s.d.: 14.4
  
  # Create one instance, seeded from current time, and export its methods
! # as module-level functions.  The functions share state across all uses
! #(both in the user's code and in the Python libraries), but that's fine
! # for most programs and is easier for the casual user than making them
! # instantiate their own Random() instance.
  
! _inst = Random()
  seed = _inst.seed
  random = _inst.random
***************
*** 868,875 ****
  setstate = _inst.setstate
  jumpahead = _inst.jumpahead
- try:
-     whseed = _inst.whseed
- except AttributeError:
-     pass
  
  if __name__ == '__main__':
--- 791,794 ----