123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227 |
- """
- When you need to use random numbers in SymPy library code, import from here
- so there is only one generator working for SymPy. Imports from here should
- behave the same as if they were being imported from Python's random module.
- But only the routines currently used in SymPy are included here. To use others
- import ``rng`` and access the method directly. For example, to capture the
- current state of the generator use ``rng.getstate()``.
- There is intentionally no Random to import from here. If you want
- to control the state of the generator, import ``seed`` and call it
- with or without an argument to set the state.
- Examples
- ========
- >>> from sympy.core.random import random, seed
- >>> assert random() < 1
- >>> seed(1); a = random()
- >>> b = random()
- >>> seed(1); c = random()
- >>> assert a == c
- >>> assert a != b # remote possibility this will fail
- """
- from sympy.utilities.iterables import is_sequence
- from sympy.utilities.misc import as_int
- import random as _random
- rng = _random.Random()
- choice = rng.choice
- random = rng.random
- randint = rng.randint
- randrange = rng.randrange
- sample = rng.sample
- # seed = rng.seed
- shuffle = rng.shuffle
- uniform = rng.uniform
- _assumptions_rng = _random.Random()
- _assumptions_shuffle = _assumptions_rng.shuffle
- def seed(a=None, version=2):
- rng.seed(a=a, version=version)
- _assumptions_rng.seed(a=a, version=version)
- def random_complex_number(a=2, b=-1, c=3, d=1, rational=False, tolerance=None):
- """
- Return a random complex number.
- To reduce chance of hitting branch cuts or anything, we guarantee
- b <= Im z <= d, a <= Re z <= c
- When rational is True, a rational approximation to a random number
- is obtained within specified tolerance, if any.
- """
- from sympy.core.numbers import I
- from sympy.simplify.simplify import nsimplify
- A, B = uniform(a, c), uniform(b, d)
- if not rational:
- return A + I*B
- return (nsimplify(A, rational=True, tolerance=tolerance) +
- I*nsimplify(B, rational=True, tolerance=tolerance))
- def verify_numerically(f, g, z=None, tol=1.0e-6, a=2, b=-1, c=3, d=1):
- """
- Test numerically that f and g agree when evaluated in the argument z.
- If z is None, all symbols will be tested. This routine does not test
- whether there are Floats present with precision higher than 15 digits
- so if there are, your results may not be what you expect due to round-
- off errors.
- Examples
- ========
- >>> from sympy import sin, cos
- >>> from sympy.abc import x
- >>> from sympy.core.random import verify_numerically as tn
- >>> tn(sin(x)**2 + cos(x)**2, 1, x)
- True
- """
- from sympy.core.symbol import Symbol
- from sympy.core.sympify import sympify
- from sympy.core.numbers import comp
- f, g = (sympify(i) for i in (f, g))
- if z is None:
- z = f.free_symbols | g.free_symbols
- elif isinstance(z, Symbol):
- z = [z]
- reps = list(zip(z, [random_complex_number(a, b, c, d) for _ in z]))
- z1 = f.subs(reps).n()
- z2 = g.subs(reps).n()
- return comp(z1, z2, tol)
- def test_derivative_numerically(f, z, tol=1.0e-6, a=2, b=-1, c=3, d=1):
- """
- Test numerically that the symbolically computed derivative of f
- with respect to z is correct.
- This routine does not test whether there are Floats present with
- precision higher than 15 digits so if there are, your results may
- not be what you expect due to round-off errors.
- Examples
- ========
- >>> from sympy import sin
- >>> from sympy.abc import x
- >>> from sympy.core.random import test_derivative_numerically as td
- >>> td(sin(x), x)
- True
- """
- from sympy.core.numbers import comp
- from sympy.core.function import Derivative
- z0 = random_complex_number(a, b, c, d)
- f1 = f.diff(z).subs(z, z0)
- f2 = Derivative(f, z).doit_numerically(z0)
- return comp(f1.n(), f2.n(), tol)
- def _randrange(seed=None):
- """Return a randrange generator.
- ``seed`` can be
- * None - return randomly seeded generator
- * int - return a generator seeded with the int
- * list - the values to be returned will be taken from the list
- in the order given; the provided list is not modified.
- Examples
- ========
- >>> from sympy.core.random import _randrange
- >>> rr = _randrange()
- >>> rr(1000) # doctest: +SKIP
- 999
- >>> rr = _randrange(3)
- >>> rr(1000) # doctest: +SKIP
- 238
- >>> rr = _randrange([0, 5, 1, 3, 4])
- >>> rr(3), rr(3)
- (0, 1)
- """
- if seed is None:
- return randrange
- elif isinstance(seed, int):
- rng.seed(seed)
- return randrange
- elif is_sequence(seed):
- seed = list(seed) # make a copy
- seed.reverse()
- def give(a, b=None, seq=seed):
- if b is None:
- a, b = 0, a
- a, b = as_int(a), as_int(b)
- w = b - a
- if w < 1:
- raise ValueError('_randrange got empty range')
- try:
- x = seq.pop()
- except IndexError:
- raise ValueError('_randrange sequence was too short')
- if a <= x < b:
- return x
- else:
- return give(a, b, seq)
- return give
- else:
- raise ValueError('_randrange got an unexpected seed')
- def _randint(seed=None):
- """Return a randint generator.
- ``seed`` can be
- * None - return randomly seeded generator
- * int - return a generator seeded with the int
- * list - the values to be returned will be taken from the list
- in the order given; the provided list is not modified.
- Examples
- ========
- >>> from sympy.core.random import _randint
- >>> ri = _randint()
- >>> ri(1, 1000) # doctest: +SKIP
- 999
- >>> ri = _randint(3)
- >>> ri(1, 1000) # doctest: +SKIP
- 238
- >>> ri = _randint([0, 5, 1, 2, 4])
- >>> ri(1, 3), ri(1, 3)
- (1, 2)
- """
- if seed is None:
- return randint
- elif isinstance(seed, int):
- rng.seed(seed)
- return randint
- elif is_sequence(seed):
- seed = list(seed) # make a copy
- seed.reverse()
- def give(a, b, seq=seed):
- a, b = as_int(a), as_int(b)
- w = b - a
- if w < 0:
- raise ValueError('_randint got empty range')
- try:
- x = seq.pop()
- except IndexError:
- raise ValueError('_randint sequence was too short')
- if a <= x <= b:
- return x
- else:
- return give(a, b, seq)
- return give
- else:
- raise ValueError('_randint got an unexpected seed')
|