Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1""" 

2 Licensing: 

3 This code is distributed under the MIT license. 

4 

5 

6 Authors: 

7 Original FORTRAN77 version of i4_sobol by Bennett Fox. 

8 MATLAB version by John Burkardt. 

9 PYTHON version by Corrado Chisari 

10 

11 Original Python version of is_prime by Corrado Chisari 

12 

13 Original MATLAB versions of other functions by John Burkardt. 

14 PYTHON versions by Corrado Chisari 

15 

16 Original code is available from 

17 http://people.sc.fsu.edu/~jburkardt/py_src/sobol/sobol.html 

18 

19 Modifications: 

20 Wrapped into Python class [30.10.2017] 

21""" 

22import numpy as np 

23 

24__all__ = ['Sobol'] 

25 

26 

27class Sobol: 

28 def __init__(self): 

29 # Init class variables 

30 self.atmost = None 

31 self.dim_max = None 

32 self.dim_num_save = None 

33 self.initialized = None 

34 self.lastq = None 

35 self.log_max = None 

36 self.maxcol = None 

37 self.poly = None 

38 self.recipd = None 

39 self.seed_save = None 

40 self.v = None 

41 

42 def i4_sobol_generate(self, dim_num, n, skip=1): 

43 """ 

44 i4_sobol_generate generates a Sobol dataset. 

45 

46 Parameters: 

47 Input, integer dim_num, the spatial dimension. 

48 Input, integer N, the number of points to generate. 

49 Input, integer SKIP, the number of initial points to skip. 

50 

51 Output, real R(M,N), the points. 

52 """ 

53 r = np.full((n, dim_num), np.nan) 

54 for j in range(n): 

55 seed = j + skip 

56 r[j, 0:dim_num], next_seed = self.i4_sobol(dim_num, seed) 

57 

58 return r 

59 

60 def i4_bit_hi1(self, n): 

61 """ 

62 i4_bit_hi1 returns the position of the high 1 bit base 2 in an integer. 

63 

64 Example: 

65 +------+-------------+----- 

66 | N | Binary | BIT 

67 +------|-------------+----- 

68 | 0 | 0 | 0 

69 | 1 | 1 | 1 

70 | 2 | 10 | 2 

71 | 3 | 11 | 2 

72 | 4 | 100 | 3 

73 | 5 | 101 | 3 

74 | 6 | 110 | 3 

75 | 7 | 111 | 3 

76 | 8 | 1000 | 4 

77 | 9 | 1001 | 4 

78 | 10 | 1010 | 4 

79 | 11 | 1011 | 4 

80 | 12 | 1100 | 4 

81 | 13 | 1101 | 4 

82 | 14 | 1110 | 4 

83 | 15 | 1111 | 4 

84 | 16 | 10000 | 5 

85 | 17 | 10001 | 5 

86 | 1023 | 1111111111 | 10 

87 | 1024 | 10000000000 | 11 

88 | 1025 | 10000000001 | 11 

89 

90 Parameters: 

91 Input, integer N, the integer to be measured. 

92 N should be nonnegative. If N is nonpositive, 

93 the value will always be 0. 

94 

95 Output, integer BIT, the number of bits base 2. 

96 """ 

97 i = np.floor(n) 

98 bit = 0 

99 while i > 0: 

100 bit += 1 

101 i //= 2 

102 return bit 

103 

104 def i4_bit_lo0(self, n): 

105 """ 

106 I4_BIT_LO0 returns the position of the low 0 bit base 2 in an integer. 

107 

108 Example: 

109 +------+------------+---- 

110 | N | Binary | BIT 

111 +------+------------+---- 

112 | 0 | 0 | 1 

113 | 1 | 1 | 2 

114 | 2 | 10 | 1 

115 | 3 | 11 | 3 

116 | 4 | 100 | 1 

117 | 5 | 101 | 2 

118 | 6 | 110 | 1 

119 | 7 | 111 | 4 

120 | 8 | 1000 | 1 

121 | 9 | 1001 | 2 

122 | 10 | 1010 | 1 

123 | 11 | 1011 | 3 

124 | 12 | 1100 | 1 

125 | 13 | 1101 | 2 

126 | 14 | 1110 | 1 

127 | 15 | 1111 | 5 

128 | 16 | 10000 | 1 

129 | 17 | 10001 | 2 

130 | 1023 | 1111111111 | 1 

131 | 1024 | 0000000000 | 1 

132 | 1025 | 0000000001 | 1 

133 

134 Parameters: 

135 Input, integer N, the integer to be measured. 

136 N should be nonnegative. 

137 

138 Output, integer BIT, the position of the low 1 bit. 

139 """ 

140 bit = 1 

141 i = np.floor(n) 

142 while i != 2 * (i // 2): 

143 bit += 1 

144 i //= 2 

145 return bit 

146 

147 def i4_sobol(self, dim_num, seed): 

148 """ 

149 i4_sobol generates a new quasirandom Sobol vector with each call. 

150 

151 Discussion: 

152 The routine adapts the ideas of Antonov and Saleev. 

153 

154 Reference: 

155 Antonov, Saleev, 

156 USSR Computational Mathematics and Mathematical Physics, 

157 Volume 19, 1980, pages 252 - 256. 

158 

159 Paul Bratley, Bennett Fox, 

160 Algorithm 659: 

161 Implementing Sobol's Quasirandom Sequence Generator, 

162 ACM Transactions on Mathematical Software, 

163 Volume 14, Number 1, pp. 88-100, 1988. 

164 

165 Bennett Fox, 

166 Algorithm 647: 

167 Implementation and Relative Efficiency of Quasirandom 

168 Sequence Generators, 

169 ACM Transactions on Mathematical Software, 

170 Volume 12, Number 4, pp. 362-376, 1986. 

171 

172 Ilya Sobol, 

173 USSR Computational Mathematics and Mathematical Physics, 

174 Volume 16, pp. 236-242, 1977. 

175 

176 Ilya Sobol, Levitan, 

177 The Production of Points Uniformly Distributed in a Multidimensional 

178 Cube (in Russian), 

179 Preprint IPM Akad. Nauk SSSR, 

180 Number 40, Moscow 1976. 

181 

182 Parameters: 

183 Input, integer DIM_NUM, the number of spatial dimensions. 

184 DIM_NUM must satisfy 1 <= DIM_NUM <= 40. 

185 

186 Input/output, integer SEED, the "seed" for the sequence. 

187 This is essentially the index in the sequence of the quasirandom 

188 value to be generated. On output, SEED has been set to the 

189 appropriate next value, usually simply SEED+1. 

190 If SEED is less than 0 on input, it is treated as though it were 0. 

191 An input value of 0 requests the first (0th) element of the sequence. 

192 

193 Output, real QUASI(DIM_NUM), the next quasirandom vector. 

194 """ 

195 

196 # if 'self.initialized' not in list(globals().keys()): 

197 if self.initialized is None: 

198 self.initialized = 0 

199 self.dim_num_save = -1 

200 

201 if not self.initialized or dim_num != self.dim_num_save: 

202 self.initialized = 1 

203 self.dim_max = 40 

204 self.dim_num_save = -1 

205 self.log_max = 30 

206 self.seed_save = -1 

207 

208 # Initialize (part of) V. 

209 self.v = np.zeros((self.dim_max, self.log_max)) 

210 self.v[0:40, 0] = np.transpose([ 

211 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 

212 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 

213 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 

214 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]) 

215 

216 self.v[2:40, 1] = np.transpose([ 

217 1, 3, 1, 3, 1, 3, 3, 1, 

218 3, 1, 3, 1, 3, 1, 1, 3, 1, 3, 

219 1, 3, 1, 3, 3, 1, 3, 1, 3, 1, 

220 3, 1, 1, 3, 1, 3, 1, 3, 1, 3]) 

221 

222 self.v[3:40, 2] = np.transpose([ 

223 7, 5, 1, 3, 3, 7, 5, 

224 5, 7, 7, 1, 3, 3, 7, 5, 1, 1, 

225 5, 3, 3, 1, 7, 5, 1, 3, 3, 7, 

226 5, 1, 1, 5, 7, 7, 5, 1, 3, 3]) 

227 

228 self.v[5:40, 3] = np.transpose([ 

229 1, 7, 9, 13, 11, 

230 1, 3, 7, 9, 5, 13, 13, 11, 3, 15, 

231 5, 3, 15, 7, 9, 13, 9, 1, 11, 7, 

232 5, 15, 1, 15, 11, 5, 3, 1, 7, 9]) 

233 

234 self.v[7:40, 4] = np.transpose([ 

235 9, 3, 27, 

236 15, 29, 21, 23, 19, 11, 25, 7, 13, 17, 

237 1, 25, 29, 3, 31, 11, 5, 23, 27, 19, 

238 21, 5, 1, 17, 13, 7, 15, 9, 31, 9]) 

239 

240 self.v[13:40, 5] = np.transpose([ 

241 37, 33, 7, 5, 11, 39, 63, 

242 27, 17, 15, 23, 29, 3, 21, 13, 31, 25, 

243 9, 49, 33, 19, 29, 11, 19, 27, 15, 25]) 

244 

245 self.v[19:40, 6] = np.transpose([ 

246 13, 

247 33, 115, 41, 79, 17, 29, 119, 75, 73, 105, 

248 7, 59, 65, 21, 3, 113, 61, 89, 45, 107]) 

249 

250 self.v[37:40, 7] = np.transpose([ 

251 7, 23, 39]) 

252 

253 # Set POLY. 

254 self.poly = [ 

255 1, 3, 7, 11, 13, 19, 25, 37, 59, 47, 

256 61, 55, 41, 67, 97, 91, 109, 103, 115, 131, 

257 193, 137, 145, 143, 241, 157, 185, 167, 229, 171, 

258 213, 191, 253, 203, 211, 239, 247, 285, 369, 299] 

259 

260 self.atmost = 2 ** self.log_max - 1 

261 

262 # Find the number of bits in ATMOST. 

263 self.maxcol = self.i4_bit_hi1(self.atmost) 

264 

265 # Initialize row 1 of V. 

266 self.v[0, 0:self.maxcol] = 1 

267 

268 # Things to do only if the dimension changed. 

269 if dim_num != self.dim_num_save: 

270 self.dim_num_save = dim_num 

271 

272 # Initialize the remaining rows of V. 

273 for i in range(2, dim_num + 1): 

274 

275 # The bits of the integer POLY(I) gives the form of 

276 # self.polynomial I. 

277 # Find the degree of self.polynomial I from binary encoding. 

278 j = self.poly[i - 1] 

279 m = 0 

280 j //= 2 

281 while j > 0: 

282 j //= 2 

283 m += 1 

284 

285 # Expand this bit pattern to separate 

286 # components of the logical array INCLUD. 

287 j = self.poly[i - 1] 

288 includ = np.zeros(m) 

289 for k in range(m, 0, -1): 

290 j2 = j // 2 

291 includ[k - 1] = (j != 2 * j2) 

292 j = j2 

293 

294 # Calculate the remaining elements of row I as explained 

295 # in Bratley and Fox, section 2. 

296 for j in range(m + 1, self.maxcol + 1): 

297 newv = self.v[i - 1, j - m - 1] 

298 lseed = 1 

299 for k in range(1, m + 1): 

300 lseed *= 2 

301 if includ[k - 1]: 

302 newv = np.bitwise_xor( 

303 int(newv), 

304 int(lseed * self.v[i - 1, j - k - 1])) 

305 self.v[i - 1, j - 1] = newv 

306 

307 # Multiply columns of V by appropriate power of 2. 

308 lseed = 1 

309 for j in range(self.maxcol - 1, 0, -1): 

310 lseed *= 2 

311 self.v[0:dim_num, j - 1] = self.v[0:dim_num, j - 1] * lseed 

312 

313 # RECIPD is 1/(common denominator of the elements in V). 

314 self.recipd = 1.0 / (2 * lseed) 

315 self.lastq = np.zeros(dim_num) 

316 

317 seed = int(np.floor(seed)) 

318 

319 if seed < 0: 

320 seed = 0 

321 

322 lseed = 1 

323 if seed == 0: 

324 self.lastq = np.zeros(dim_num) 

325 

326 elif seed == self.seed_save + 1: 

327 

328 # Find the position of the right-hand zero in SEED. 

329 lseed = self.i4_bit_lo0(seed) 

330 

331 elif seed <= self.seed_save: 

332 

333 self.seed_save = 0 

334 self.lastq = np.zeros(dim_num) 

335 

336 for seed_temp in range(int(self.seed_save), int(seed)): 

337 lseed = self.i4_bit_lo0(seed_temp) 

338 for i in range(1, dim_num + 1): 

339 self.lastq[i - 1] = np.bitwise_xor( 

340 int(self.lastq[i - 1]), int(self.v[i - 1, lseed - 1])) 

341 

342 lseed = self.i4_bit_lo0(seed) 

343 

344 elif self.seed_save + 1 < seed: 

345 

346 for seed_temp in range(int(self.seed_save + 1), int(seed)): 

347 lseed = self.i4_bit_lo0(seed_temp) 

348 for i in range(1, dim_num + 1): 

349 self.lastq[i - 1] = np.bitwise_xor( 

350 int(self.lastq[i - 1]), int(self.v[i - 1, lseed - 1])) 

351 

352 lseed = self.i4_bit_lo0(seed) 

353 

354 # Check that the user is not calling too many times! 

355 if self.maxcol < lseed: 

356 print('I4_SOBOL - Fatal error!') 

357 print(' Too many calls!') 

358 print(' MAXCOL = %d\n' % self.maxcol) 

359 print(' L = %d\n' % lseed) 

360 return 

361 

362 # Calculate the new components of QUASI. 

363 quasi = np.zeros(dim_num) 

364 for i in range(1, dim_num + 1): 

365 quasi[i - 1] = self.lastq[i - 1] * self.recipd 

366 self.lastq[i - 1] = np.bitwise_xor( 

367 int(self.lastq[i - 1]), int(self.v[i - 1, lseed - 1])) 

368 

369 self.seed_save = seed 

370 seed += 1 

371 

372 return [quasi, seed]