test_random.py 20 KB
Newer Older
1
#!/usr/bin/env python3
2 3

import unittest
4
import random
5
import time
6
import pickle
7
import warnings
8
from math import log, exp, pi, fsum, sin
9
from test import support
10 11 12 13 14 15 16 17

class TestBasicOps(unittest.TestCase):
    # Superclass with tests common to all generators.
    # Subclasses must arrange for self.gen to retrieve the Random instance
    # to be tested.

    def randomlist(self, n):
        """Helper function to make a list of random numbers"""
18
        return [self.gen.random() for i in range(n)]
19 20 21 22

    def test_autoseed(self):
        self.gen.seed()
        state1 = self.gen.getstate()
23
        time.sleep(0.1)
24 25 26 27 28 29 30 31 32 33 34 35 36
        self.gen.seed()      # diffent seeds at different times
        state2 = self.gen.getstate()
        self.assertNotEqual(state1, state2)

    def test_saverestore(self):
        N = 1000
        self.gen.seed()
        state = self.gen.getstate()
        randseq = self.randomlist(N)
        self.gen.setstate(state)    # should regenerate the same sequence
        self.assertEqual(randseq, self.randomlist(N))

    def test_seedargs(self):
37
        for arg in [None, 0, 0, 1, 1, -1, -1, 10**20, -(10**20),
38 39
                    3.14, 1+2j, 'a', tuple('abc')]:
            self.gen.seed(arg)
40
        for arg in [list(range(3)), dict(one=1)]:
41
            self.assertRaises(TypeError, self.gen.seed, arg)
42
        self.assertRaises(TypeError, self.gen.seed, 1, 2, 3, 4)
43
        self.assertRaises(TypeError, type(self.gen), [])
44

45 46 47 48 49 50 51
    def test_choice(self):
        choice = self.gen.choice
        with self.assertRaises(IndexError):
            choice([])
        self.assertEqual(choice([50]), 50)
        self.assertIn(choice([25, 75]), [25, 75])

52 53 54 55
    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
        N = 100
56 57
        population = range(N)
        for k in range(N+1):
58 59
            s = self.gen.sample(population, k)
            self.assertEqual(len(s), k)
60
            uniq = set(s)
61
            self.assertEqual(len(uniq), k)
62
            self.assertTrue(uniq <= set(population))
63
        self.assertEqual(self.gen.sample([], 0), [])  # test edge case N==k==0
64

65 66 67 68 69 70 71
    def test_sample_distribution(self):
        # For the entire allowable range of 0 <= k <= N, validate that
        # sample generates all possible permutations
        n = 5
        pop = range(n)
        trials = 10000  # large num prevents false negatives without slowing normal case
        def factorial(n):
72 73 74
            if n == 0:
                return 1
            return n * factorial(n - 1)
75
        for k in range(n):
76
            expected = factorial(n) // factorial(n-k)
77
            perms = {}
78
            for i in range(trials):
79 80 81 82 83 84
                perms[tuple(self.gen.sample(pop, k))] = None
                if len(perms) == expected:
                    break
            else:
                self.fail()

85 86
    def test_sample_inputs(self):
        # SF bug #801342 -- population can be any iterable defining __len__()
87
        self.gen.sample(set(range(20)), 2)
88
        self.gen.sample(range(20), 2)
89
        self.gen.sample(range(20), 2)
90 91 92
        self.gen.sample(str('abcdefghijklmnopqrst'), 2)
        self.gen.sample(tuple('abcdefghijklmnopqrst'), 2)

93
    def test_sample_on_dicts(self):
94
        self.assertRaises(TypeError, self.gen.sample, dict.fromkeys('abcdef'), 2)
95

96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
    def test_gauss(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.seed(seed)
            x1 = self.gen.random()
            y1 = self.gen.gauss(0, 1)

            self.gen.seed(seed)
            x2 = self.gen.random()
            y2 = self.gen.gauss(0, 1)

            self.assertEqual(x1, x2)
            self.assertEqual(y1, y2)

113 114
    def test_pickling(self):
        state = pickle.dumps(self.gen)
115
        origseq = [self.gen.random() for i in range(10)]
116
        newgen = pickle.loads(state)
117
        restoredseq = [newgen.random() for i in range(10)]
118
        self.assertEqual(origseq, restoredseq)
119

120 121 122 123 124 125 126 127
    def test_bug_1727780(self):
        # verify that version-2-pickles can be loaded
        # fine, whether they are created on 32-bit or 64-bit
        # platforms, and that version-3-pickles load fine.
        files = [("randv2_32.pck", 780),
                 ("randv2_64.pck", 866),
                 ("randv3.pck", 343)]
        for file, value in files:
128
            f = open(support.findfile(file),"rb")
129 130
            r = pickle.load(f)
            f.close()
131 132 133 134 135 136 137 138 139
            self.assertEqual(int(r.random()*1000), value)

    def test_bug_9025(self):
        # Had problem with an uneven distribution in int(n*random())
        # Verify the fix by checking that distributions fall within expectations.
        n = 100000
        randrange = self.gen.randrange
        k = sum(randrange(6755399441055744) % 3 == 2 for i in range(n))
        self.assertTrue(0.30 < k/n < .37, (k/n))
140

141 142
class SystemRandom_TestBasicOps(TestBasicOps):
    gen = random.SystemRandom()
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167

    def test_autoseed(self):
        # Doesn't need to do anything except not fail
        self.gen.seed()

    def test_saverestore(self):
        self.assertRaises(NotImplementedError, self.gen.getstate)
        self.assertRaises(NotImplementedError, self.gen.setstate, None)

    def test_seedargs(self):
        # Doesn't need to do anything except not fail
        self.gen.seed(100)

    def test_gauss(self):
        self.gen.gauss_next = None
        self.gen.seed(100)
        self.assertEqual(self.gen.gauss_next, None)

    def test_pickling(self):
        self.assertRaises(NotImplementedError, pickle.dumps, self.gen)

    def test_53_bits_per_float(self):
        # This should pass whenever a C double has 53 bit precision.
        span = 2 ** 53
        cum = 0
168
        for i in range(100):
169 170 171 172 173 174 175 176
            cum |= int(self.gen.random() * span)
        self.assertEqual(cum, span-1)

    def test_bigrand(self):
        # The randrange routine should build-up the required number of bits
        # in stages so that all bit positions are active.
        span = 2 ** 500
        cum = 0
177
        for i in range(100):
178
            r = self.gen.randrange(span)
179
            self.assertTrue(0 <= r < span)
180 181 182 183 184 185 186 187 188
            cum |= r
        self.assertEqual(cum, span-1)

    def test_bigrand_ranges(self):
        for i in [40,80, 160, 200, 211, 250, 375, 512, 550]:
            start = self.gen.randrange(2 ** i)
            stop = self.gen.randrange(2 ** (i-2))
            if stop <= start:
                return
189
            self.assertTrue(start <= self.gen.randrange(start, stop) < stop)
190 191 192 193

    def test_rangelimits(self):
        for start, stop in [(-2,0), (-(2**60)-2,-(2**60)), (2**60,2**60+2)]:
            self.assertEqual(set(range(start,stop)),
194
                set([self.gen.randrange(start,stop) for i in range(100)]))
195 196 197

    def test_genrandbits(self):
        # Verify ranges
198
        for k in range(1, 1000):
199
            self.assertTrue(0 <= self.gen.getrandbits(k) < 2**k)
200 201 202 203 204

        # Verify all bits active
        getbits = self.gen.getrandbits
        for span in [1, 2, 3, 4, 31, 32, 32, 52, 53, 54, 119, 127, 128, 129]:
            cum = 0
205
            for i in range(100):
206 207 208 209 210 211 212 213 214 215 216 217 218 219
                cum |= getbits(span)
            self.assertEqual(cum, 2**span-1)

        # Verify argument checking
        self.assertRaises(TypeError, self.gen.getrandbits)
        self.assertRaises(TypeError, self.gen.getrandbits, 1, 2)
        self.assertRaises(ValueError, self.gen.getrandbits, 0)
        self.assertRaises(ValueError, self.gen.getrandbits, -1)
        self.assertRaises(TypeError, self.gen.getrandbits, 10.1)

    def test_randbelow_logic(self, _log=log, int=int):
        # check bitcount transition points:  2**i and 2**(i+1)-1
        # show that: k = int(1.001 + _log(n, 2))
        # is equal to or one greater than the number of bits in n
220
        for i in range(1, 1000):
221
            n = 1 << i # check an exact power of two
222 223 224
            numbits = i+1
            k = int(1.00001 + _log(n, 2))
            self.assertEqual(k, numbits)
225
            self.assertEqual(n, 2**(k-1))
226 227 228

            n += n - 1      # check 1 below the next power of two
            k = int(1.00001 + _log(n, 2))
229
            self.assertIn(k, [numbits, numbits+1])
230
            self.assertTrue(2**k > n > 2**(k-2))
231 232 233 234

            n -= n >> 15     # check a little farther below the next power of two
            k = int(1.00001 + _log(n, 2))
            self.assertEqual(k, numbits)        # note the stronger assertion
235
            self.assertTrue(2**k > n > 2**(k-1))   # note the stronger assertion
236 237


238 239 240
class MersenneTwister_TestBasicOps(TestBasicOps):
    gen = random.Random()

241 242 243 244 245 246 247 248 249 250 251
    def test_guaranteed_stable(self):
        # These sequences are guaranteed to stay the same across versions of python
        self.gen.seed(3456147, version=1)
        self.assertEqual([self.gen.random().hex() for i in range(4)],
            ['0x1.ac362300d90d2p-1', '0x1.9d16f74365005p-1',
             '0x1.1ebb4352e4c4dp-1', '0x1.1a7422abf9c11p-1'])
        self.gen.seed("the quick brown fox", version=2)
        self.assertEqual([self.gen.random().hex() for i in range(4)],
            ['0x1.1294009b9eda4p-2', '0x1.2ff96171b0010p-1',
             '0x1.459e0989bd8e0p-5', '0x1.8b5f55892ddcbp-1'])

252 253 254 255 256 257 258 259 260 261 262 263 264
    def test_setstate_first_arg(self):
        self.assertRaises(ValueError, self.gen.setstate, (1, None, None))

    def test_setstate_middle_arg(self):
        # Wrong type, s/b tuple
        self.assertRaises(TypeError, self.gen.setstate, (2, None, None))
        # Wrong length, s/b 625
        self.assertRaises(ValueError, self.gen.setstate, (2, (1,2,3), None))
        # Wrong type, s/b tuple of 625 ints
        self.assertRaises(TypeError, self.gen.setstate, (2, ('a',)*625, None))
        # Last element s/b an int also
        self.assertRaises(TypeError, self.gen.setstate, (2, (0,)*624+('a',), None))

265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290
    def test_referenceImplementation(self):
        # Compare the python implementation with results from the original
        # code.  Create 2000 53-bit precision random floats.  Compare only
        # the last ten entries to show that the independent implementations
        # are tracking.  Here is the main() function needed to create the
        # list of expected random numbers:
        #    void main(void){
        #         int i;
        #         unsigned long init[4]={61731, 24903, 614, 42143}, length=4;
        #         init_by_array(init, length);
        #         for (i=0; i<2000; i++) {
        #           printf("%.15f ", genrand_res53());
        #           if (i%5==4) printf("\n");
        #         }
        #     }
        expected = [0.45839803073713259,
                    0.86057815201978782,
                    0.92848331726782152,
                    0.35932681119782461,
                    0.081823493762449573,
                    0.14332226470169329,
                    0.084297823823520024,
                    0.53814864671831453,
                    0.089215024911993401,
                    0.78486196105372907]

291
        self.gen.seed(61731 + (24903<<32) + (614<<64) + (42143<<96))
292 293 294 295 296 297 298 299 300 301 302
        actual = self.randomlist(2000)[-10:]
        for a, e in zip(actual, expected):
            self.assertAlmostEqual(a,e,places=14)

    def test_strong_reference_implementation(self):
        # Like test_referenceImplementation, but checks for exact bit-level
        # equality.  This should pass on any box where C double contains
        # at least 53 bits of precision (the underlying algorithm suffers
        # no rounding errors -- all results are exact).
        from math import ldexp

303 304 305 306 307 308 309 310 311 312 313
        expected = [0x0eab3258d2231f,
                    0x1b89db315277a5,
                    0x1db622a5518016,
                    0x0b7f9af0d575bf,
                    0x029e4c4db82240,
                    0x04961892f5d673,
                    0x02b291598e4589,
                    0x11388382c15694,
                    0x02dad977c9e1fe,
                    0x191d96d4d334c6]
        self.gen.seed(61731 + (24903<<32) + (614<<64) + (42143<<96))
314 315
        actual = self.randomlist(2000)[-10:]
        for a, e in zip(actual, expected):
316
            self.assertEqual(int(ldexp(a, 53)), e)
317 318 319 320 321 322 323

    def test_long_seed(self):
        # This is most interesting to run in debug mode, just to make sure
        # nothing blows up.  Under the covers, a dynamically resized array
        # is allocated, consuming space proportional to the number of bits
        # in the seed.  Unfortunately, that's a quadratic-time algorithm,
        # so don't make this horribly big.
324
        seed = (1 << (10000 * 8)) - 1  # about 10K bytes
325
        self.gen.seed(seed)
326

327 328 329 330
    def test_53_bits_per_float(self):
        # This should pass whenever a C double has 53 bit precision.
        span = 2 ** 53
        cum = 0
331
        for i in range(100):
332 333 334 335 336 337 338 339
            cum |= int(self.gen.random() * span)
        self.assertEqual(cum, span-1)

    def test_bigrand(self):
        # The randrange routine should build-up the required number of bits
        # in stages so that all bit positions are active.
        span = 2 ** 500
        cum = 0
340
        for i in range(100):
341
            r = self.gen.randrange(span)
342
            self.assertTrue(0 <= r < span)
343 344 345 346 347 348 349 350 351
            cum |= r
        self.assertEqual(cum, span-1)

    def test_bigrand_ranges(self):
        for i in [40,80, 160, 200, 211, 250, 375, 512, 550]:
            start = self.gen.randrange(2 ** i)
            stop = self.gen.randrange(2 ** (i-2))
            if stop <= start:
                return
352
            self.assertTrue(start <= self.gen.randrange(start, stop) < stop)
353 354 355

    def test_rangelimits(self):
        for start, stop in [(-2,0), (-(2**60)-2,-(2**60)), (2**60,2**60+2)]:
356
            self.assertEqual(set(range(start,stop)),
357
                set([self.gen.randrange(start,stop) for i in range(100)]))
358 359 360 361 362

    def test_genrandbits(self):
        # Verify cross-platform repeatability
        self.gen.seed(1234567)
        self.assertEqual(self.gen.getrandbits(100),
363
                         97904845777343510404718956115)
364
        # Verify ranges
365
        for k in range(1, 1000):
366
            self.assertTrue(0 <= self.gen.getrandbits(k) < 2**k)
367 368 369 370 371

        # Verify all bits active
        getbits = self.gen.getrandbits
        for span in [1, 2, 3, 4, 31, 32, 32, 52, 53, 54, 119, 127, 128, 129]:
            cum = 0
372
            for i in range(100):
373 374 375
                cum |= getbits(span)
            self.assertEqual(cum, 2**span-1)

376 377 378 379 380 381 382
        # Verify argument checking
        self.assertRaises(TypeError, self.gen.getrandbits)
        self.assertRaises(TypeError, self.gen.getrandbits, 'a')
        self.assertRaises(TypeError, self.gen.getrandbits, 1, 2)
        self.assertRaises(ValueError, self.gen.getrandbits, 0)
        self.assertRaises(ValueError, self.gen.getrandbits, -1)

383 384 385 386
    def test_randbelow_logic(self, _log=log, int=int):
        # check bitcount transition points:  2**i and 2**(i+1)-1
        # show that: k = int(1.001 + _log(n, 2))
        # is equal to or one greater than the number of bits in n
387
        for i in range(1, 1000):
388
            n = 1 << i # check an exact power of two
389 390 391
            numbits = i+1
            k = int(1.00001 + _log(n, 2))
            self.assertEqual(k, numbits)
392
            self.assertEqual(n, 2**(k-1))
393 394 395

            n += n - 1      # check 1 below the next power of two
            k = int(1.00001 + _log(n, 2))
396
            self.assertIn(k, [numbits, numbits+1])
397
            self.assertTrue(2**k > n > 2**(k-2))
398 399 400 401

            n -= n >> 15     # check a little farther below the next power of two
            k = int(1.00001 + _log(n, 2))
            self.assertEqual(k, numbits)        # note the stronger assertion
402
            self.assertTrue(2**k > n > 2**(k-1))   # note the stronger assertion
403

404 405 406 407 408
    def test_randrange_bug_1590891(self):
        start = 1000000000000
        stop = -100000000000000000000
        step = -200
        x = self.gen.randrange(start, stop, step)
409
        self.assertTrue(stop < x <= start)
410 411
        self.assertEqual((x+stop)%step, 0)

412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428
def gamma(z, sqrt2pi=(2.0*pi)**0.5):
    # Reflection to right half of complex plane
    if z < 0.5:
        return pi / sin(pi*z) / gamma(1.0-z)
    # Lanczos approximation with g=7
    az = z + (7.0 - 0.5)
    return az ** (z-0.5) / exp(az) * sqrt2pi * fsum([
        0.9999999999995183,
        676.5203681218835 / z,
        -1259.139216722289 / (z+1.0),
        771.3234287757674 / (z+2.0),
        -176.6150291498386 / (z+3.0),
        12.50734324009056 / (z+4.0),
        -0.1385710331296526 / (z+5.0),
        0.9934937113930748e-05 / (z+6.0),
        0.1659470187408462e-06 / (z+7.0),
    ])
429

Raymond Hettinger's avatar
Raymond Hettinger committed
430 431 432 433
class TestDistributions(unittest.TestCase):
    def test_zeroinputs(self):
        # Verify that distributions can handle a series of zero inputs'
        g = random.Random()
434
        x = [g.random() for i in range(50)] + [0.0]*5
Raymond Hettinger's avatar
Raymond Hettinger committed
435 436 437 438 439 440 441 442 443 444 445 446
        g.random = x[:].pop; g.uniform(1,10)
        g.random = x[:].pop; g.paretovariate(1.0)
        g.random = x[:].pop; g.expovariate(1.0)
        g.random = x[:].pop; g.weibullvariate(1.0, 1.0)
        g.random = x[:].pop; g.normalvariate(0.0, 1.0)
        g.random = x[:].pop; g.gauss(0.0, 1.0)
        g.random = x[:].pop; g.lognormvariate(0.0, 1.0)
        g.random = x[:].pop; g.vonmisesvariate(0.0, 1.0)
        g.random = x[:].pop; g.gammavariate(0.01, 1.0)
        g.random = x[:].pop; g.gammavariate(1.0, 1.0)
        g.random = x[:].pop; g.gammavariate(200.0, 1.0)
        g.random = x[:].pop; g.betavariate(3.0, 3.0)
Christian Heimes's avatar
Christian Heimes committed
447
        g.random = x[:].pop; g.triangular(0.0, 1.0, 1.0/3.0)
Raymond Hettinger's avatar
Raymond Hettinger committed
448

449 450 451 452 453
    def test_avg_std(self):
        # Use integration to test distribution average and standard deviation.
        # Only works for distributions which do not consume variates in pairs
        g = random.Random()
        N = 5000
454
        x = [i/float(N) for i in range(1,N)]
455 456
        for variate, args, mu, sigmasqrd in [
                (g.uniform, (1.0,10.0), (10.0+1.0)/2, (10.0-1.0)**2/12),
Christian Heimes's avatar
Christian Heimes committed
457
                (g.triangular, (0.0, 1.0, 1.0/3.0), 4.0/9.0, 7.0/9.0/18.0),
458 459 460 461 462 463 464
                (g.expovariate, (1.5,), 1/1.5, 1/1.5**2),
                (g.paretovariate, (5.0,), 5.0/(5.0-1),
                                  5.0/((5.0-1)**2*(5.0-2))),
                (g.weibullvariate, (1.0, 3.0), gamma(1+1/3.0),
                                  gamma(1+2/3.0)-gamma(1+1/3.0)**2) ]:
            g.random = x[:].pop
            y = []
465
            for i in range(len(x)):
466 467 468 469 470 471 472 473 474
                try:
                    y.append(variate(*args))
                except IndexError:
                    pass
            s1 = s2 = 0
            for e in y:
                s1 += e
                s2 += (e - mu) ** 2
            N = len(y)
475 476
            self.assertAlmostEqual(s1/N, mu, places=2)
            self.assertAlmostEqual(s2/(N-1), sigmasqrd, places=2)
477

478 479 480 481 482 483
class TestModule(unittest.TestCase):
    def testMagicConstants(self):
        self.assertAlmostEqual(random.NV_MAGICCONST, 1.71552776992141)
        self.assertAlmostEqual(random.TWOPI, 6.28318530718)
        self.assertAlmostEqual(random.LOG4, 1.38629436111989)
        self.assertAlmostEqual(random.SG_MAGICCONST, 2.50407739677627)
484

485 486
    def test__all__(self):
        # tests validity but not completeness of the __all__ list
487
        self.assertTrue(set(random.__all__) <= set(dir(random)))
488

489 490 491 492 493 494 495 496
    def test_random_subclass_with_kwargs(self):
        # SF bug #1486663 -- this used to erroneously raise a TypeError
        class Subclass(random.Random):
            def __init__(self, newarg=None):
                random.Random.__init__(self)
        Subclass(newarg=1)


Raymond Hettinger's avatar
Raymond Hettinger committed
497
def test_main(verbose=None):
498
    testclasses =    [MersenneTwister_TestBasicOps,
Raymond Hettinger's avatar
Raymond Hettinger committed
499
                      TestDistributions,
500 501
                      TestModule]

502
    try:
503
        random.SystemRandom().random()
504 505 506
    except NotImplementedError:
        pass
    else:
507
        testclasses.append(SystemRandom_TestBasicOps)
508

509
    support.run_unittest(*testclasses)
510

Raymond Hettinger's avatar
Raymond Hettinger committed
511 512 513
    # verify reference counting
    import sys
    if verbose and hasattr(sys, "gettotalrefcount"):
514
        counts = [None] * 5
515
        for i in range(len(counts)):
516
            support.run_unittest(*testclasses)
517
            counts[i] = sys.gettotalrefcount()
518
        print(counts)
Raymond Hettinger's avatar
Raymond Hettinger committed
519

520
if __name__ == "__main__":
Raymond Hettinger's avatar
Raymond Hettinger committed
521
    test_main(verbose=True)