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

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()``. 

8 

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. 

12 

13Examples 

14======== 

15 

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 

23 

24""" 

25from sympy.utilities.iterables import is_sequence 

26from sympy.utilities.misc import as_int 

27 

28import random as _random 

29rng = _random.Random() 

30 

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 

39 

40_assumptions_rng = _random.Random() 

41_assumptions_shuffle = _assumptions_rng.shuffle 

42 

43 

44def seed(a=None, version=2): 

45 rng.seed(a=a, version=version) 

46 _assumptions_rng.seed(a=a, version=version) 

47 

48 

49def random_complex_number(a=2, b=-1, c=3, d=1, rational=False, tolerance=None): 

50 """ 

51 Return a random complex number. 

52 

53 To reduce chance of hitting branch cuts or anything, we guarantee 

54 b <= Im z <= d, a <= Re z <= c 

55 

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

66 

67 

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. 

71 

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. 

76 

77 Examples 

78 ======== 

79 

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) 

98 

99 

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. 

104 

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. 

108 

109 Examples 

110 ======== 

111 

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) 

124 

125 

126def _randrange(seed=None): 

127 """Return a randrange generator. 

128 

129 ``seed`` can be 

130 

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. 

135 

136 Examples 

137 ======== 

138 

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() 

158 

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

177 

178 

179def _randint(seed=None): 

180 """Return a randint generator. 

181 

182 ``seed`` can be 

183 

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. 

188 

189 Examples 

190 ======== 

191 

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() 

211 

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