|
""" |
|
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 |
|
|
|
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) |
|
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) |
|
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') |
|
|