Coverage for /usr/lib/python3/dist-packages/sympy/core/random.py: 21%
91 statements
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
1"""
2When you need to use random numbers in SymPy library code, import from here
3so there is only one generator working for SymPy. Imports from here should
4behave the same as if they were being imported from Python's random module.
5But only the routines currently used in SymPy are included here. To use others
6import ``rng`` and access the method directly. For example, to capture the
7current state of the generator use ``rng.getstate()``.
9There is intentionally no Random to import from here. If you want
10to control the state of the generator, import ``seed`` and call it
11with or without an argument to set the state.
13Examples
14========
16>>> from sympy.core.random import random, seed
17>>> assert random() < 1
18>>> seed(1); a = random()
19>>> b = random()
20>>> seed(1); c = random()
21>>> assert a == c
22>>> assert a != b # remote possibility this will fail
24"""
25from sympy.utilities.iterables import is_sequence
26from sympy.utilities.misc import as_int
28import random as _random
29rng = _random.Random()
31choice = rng.choice
32random = rng.random
33randint = rng.randint
34randrange = rng.randrange
35sample = rng.sample
36# seed = rng.seed
37shuffle = rng.shuffle
38uniform = rng.uniform
40_assumptions_rng = _random.Random()
41_assumptions_shuffle = _assumptions_rng.shuffle
44def seed(a=None, version=2):
45 rng.seed(a=a, version=version)
46 _assumptions_rng.seed(a=a, version=version)
49def random_complex_number(a=2, b=-1, c=3, d=1, rational=False, tolerance=None):
50 """
51 Return a random complex number.
53 To reduce chance of hitting branch cuts or anything, we guarantee
54 b <= Im z <= d, a <= Re z <= c
56 When rational is True, a rational approximation to a random number
57 is obtained within specified tolerance, if any.
58 """
59 from sympy.core.numbers import I
60 from sympy.simplify.simplify import nsimplify
61 A, B = uniform(a, c), uniform(b, d)
62 if not rational:
63 return A + I*B
64 return (nsimplify(A, rational=True, tolerance=tolerance) +
65 I*nsimplify(B, rational=True, tolerance=tolerance))
68def verify_numerically(f, g, z=None, tol=1.0e-6, a=2, b=-1, c=3, d=1):
69 """
70 Test numerically that f and g agree when evaluated in the argument z.
72 If z is None, all symbols will be tested. This routine does not test
73 whether there are Floats present with precision higher than 15 digits
74 so if there are, your results may not be what you expect due to round-
75 off errors.
77 Examples
78 ========
80 >>> from sympy import sin, cos
81 >>> from sympy.abc import x
82 >>> from sympy.core.random import verify_numerically as tn
83 >>> tn(sin(x)**2 + cos(x)**2, 1, x)
84 True
85 """
86 from sympy.core.symbol import Symbol
87 from sympy.core.sympify import sympify
88 from sympy.core.numbers import comp
89 f, g = (sympify(i) for i in (f, g))
90 if z is None:
91 z = f.free_symbols | g.free_symbols
92 elif isinstance(z, Symbol):
93 z = [z]
94 reps = list(zip(z, [random_complex_number(a, b, c, d) for _ in z]))
95 z1 = f.subs(reps).n()
96 z2 = g.subs(reps).n()
97 return comp(z1, z2, tol)
100def test_derivative_numerically(f, z, tol=1.0e-6, a=2, b=-1, c=3, d=1):
101 """
102 Test numerically that the symbolically computed derivative of f
103 with respect to z is correct.
105 This routine does not test whether there are Floats present with
106 precision higher than 15 digits so if there are, your results may
107 not be what you expect due to round-off errors.
109 Examples
110 ========
112 >>> from sympy import sin
113 >>> from sympy.abc import x
114 >>> from sympy.core.random import test_derivative_numerically as td
115 >>> td(sin(x), x)
116 True
117 """
118 from sympy.core.numbers import comp
119 from sympy.core.function import Derivative
120 z0 = random_complex_number(a, b, c, d)
121 f1 = f.diff(z).subs(z, z0)
122 f2 = Derivative(f, z).doit_numerically(z0)
123 return comp(f1.n(), f2.n(), tol)
126def _randrange(seed=None):
127 """Return a randrange generator.
129 ``seed`` can be
131 * None - return randomly seeded generator
132 * int - return a generator seeded with the int
133 * list - the values to be returned will be taken from the list
134 in the order given; the provided list is not modified.
136 Examples
137 ========
139 >>> from sympy.core.random import _randrange
140 >>> rr = _randrange()
141 >>> rr(1000) # doctest: +SKIP
142 999
143 >>> rr = _randrange(3)
144 >>> rr(1000) # doctest: +SKIP
145 238
146 >>> rr = _randrange([0, 5, 1, 3, 4])
147 >>> rr(3), rr(3)
148 (0, 1)
149 """
150 if seed is None:
151 return randrange
152 elif isinstance(seed, int):
153 rng.seed(seed)
154 return randrange
155 elif is_sequence(seed):
156 seed = list(seed) # make a copy
157 seed.reverse()
159 def give(a, b=None, seq=seed):
160 if b is None:
161 a, b = 0, a
162 a, b = as_int(a), as_int(b)
163 w = b - a
164 if w < 1:
165 raise ValueError('_randrange got empty range')
166 try:
167 x = seq.pop()
168 except IndexError:
169 raise ValueError('_randrange sequence was too short')
170 if a <= x < b:
171 return x
172 else:
173 return give(a, b, seq)
174 return give
175 else:
176 raise ValueError('_randrange got an unexpected seed')
179def _randint(seed=None):
180 """Return a randint generator.
182 ``seed`` can be
184 * None - return randomly seeded generator
185 * int - return a generator seeded with the int
186 * list - the values to be returned will be taken from the list
187 in the order given; the provided list is not modified.
189 Examples
190 ========
192 >>> from sympy.core.random import _randint
193 >>> ri = _randint()
194 >>> ri(1, 1000) # doctest: +SKIP
195 999
196 >>> ri = _randint(3)
197 >>> ri(1, 1000) # doctest: +SKIP
198 238
199 >>> ri = _randint([0, 5, 1, 2, 4])
200 >>> ri(1, 3), ri(1, 3)
201 (1, 2)
202 """
203 if seed is None:
204 return randint
205 elif isinstance(seed, int):
206 rng.seed(seed)
207 return randint
208 elif is_sequence(seed):
209 seed = list(seed) # make a copy
210 seed.reverse()
212 def give(a, b, seq=seed):
213 a, b = as_int(a), as_int(b)
214 w = b - a
215 if w < 0:
216 raise ValueError('_randint got empty range')
217 try:
218 x = seq.pop()
219 except IndexError:
220 raise ValueError('_randint sequence was too short')
221 if a <= x <= b:
222 return x
223 else:
224 return give(a, b, seq)
225 return give
226 else:
227 raise ValueError('_randint got an unexpected seed')