geone 1.2.10__py311-none-manylinux_2_35_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
geone/randProcess.py ADDED
@@ -0,0 +1,1122 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ # -------------------------------------------------------------------------
5
+ # Python module: 'randProcess.py'
6
+ # author: Julien Straubhaar
7
+ # date: may-2022
8
+ # -------------------------------------------------------------------------
9
+
10
+ """
11
+ Module for miscellaneous algorithms based on random processes.
12
+ """
13
+
14
+ import numpy as np
15
+ import scipy
16
+
17
+ # ----------------------------------------------------------------------------
18
+ def acceptRejectSampler(n, xmin, xmax, f, c=None, g=None, g_rvs=None,
19
+ return_accept_ratio=False,
20
+ max_trial=None, show_progress=False,
21
+ opt_kwargs=None):
22
+ """
23
+ Generates samples according to a given density function.
24
+
25
+ This function generates `n` points (which can be multi-variate) in a
26
+ box-shape domain of lower bound(s) `xmin` and upper bound(s) `xmax`,
27
+ according to a density proportional to the function `f`, based on the
28
+ accept-reject algorithm.
29
+
30
+ Let `g_rvs` a function returning random variates sample(s) from an
31
+ instrumental distribution with density proportional to `g`, and `c` a
32
+ constant such that `c*g(x) >= f(x)` for any `x` (in `[xmin, xmax[` (can be
33
+ multi-dimensional), i.e. `x[i]` in `[xmin[i], xmax[i][` for any i). Let `fd`
34
+ (resp. `gd`) the density function proportional to `f` (resp. `g`); the
35
+ alogrithm consists in the following steps to generate samples `x ~ fd`:
36
+
37
+ - generate `y ~ gd` (using `g_rvs`)
38
+ - generate `u ~ Unif([0,1])`
39
+ - if `u < f(y)/c*g(y)`, then accept `x` (reject `x` otherwise)
40
+
41
+ The default instrumental distribution (if both `g` and `g_rvs` set to `None`)
42
+ is the uniform distribution (`g=1`). If the domain (`[xmin, xmax[`) is
43
+ infinite, the instrumental distribution (`g`, and `g_rvs`) and `c` must be
44
+ specified.
45
+
46
+ Parameters
47
+ ----------
48
+ n : int
49
+ number of sample points
50
+
51
+ xmin : float (or int), or array-like of shape(m,)
52
+ lower bound of each coordinate (m is the space dimension);
53
+ note: component(s) can be set to `-np.inf`
54
+
55
+ xmax : float (or int), or array-like of shape(m,)
56
+ upper bound of each coordinate (m is the space dimension)
57
+ note: component(s) can be set to `np.inf`
58
+
59
+ f : function (`callable`)
60
+ function proportional to target density, `f(x)` returns the target
61
+ density (times a constant) at `x`; with `x` array_like, the last
62
+ axis of `x` denotes the components of the points where the function is
63
+ evaluated
64
+
65
+ c : float (or int), optional
66
+ constant such that (not checked)) `c*g(x) >= f(x)` for all x in
67
+ [xmin, xmax[, with `g(x)=1` if `g` is not specified (`g=None`);
68
+ by default (`c=None`), the domain (`[xmin, xmax[`) must be finite and
69
+ `c` is automatically computed (using the function
70
+ `scipy.optimize.differential_evolution`)
71
+
72
+ g : function (callable), optional
73
+ function proportional to the instrumental density on `[xmin, xmax[`,
74
+ `g(x)` returns the instrumental density (times a constant) at `x`;
75
+ with `x` array_like, the last axis of `x` denotes the components of the
76
+ points where the function is evaluated;
77
+ by default (`g=None`), the domain (`[xmin, xmax[`) must be finite and
78
+ the instrumental distribution considered is uniform (constant
79
+ function `g=1` is considered)
80
+
81
+ g_rvs : function (`callable`), optional
82
+ function returning samples from the instrumental distribution with
83
+ density proportional to `g` on `[xmin, xmax[` (restricted on this
84
+ domain if needed); `g_rvs` must have the keyword arguments `size`
85
+ (the number of sample(s) to draw);
86
+ by default (`None`), uniform instrumental distribution is considered
87
+ (see `g`);
88
+ note: both `g` and `g_rvs` must be specified (or both set to `None`)
89
+
90
+ return_accept_ratio : bool, default: False
91
+ indicates if the acceptance ratio is returned
92
+
93
+ show_progress : bool, default: False
94
+ indicates if progress is displayed (`True`) or not (`False`)
95
+
96
+ opt_kwargs : dict, optional
97
+ keyword arguments to be passed to `scipy.optimize.differential_evolution`
98
+ (do not set `'bounds'` key, bounds are set according to `xmin`, `xmax`)
99
+
100
+ Returns
101
+ -------
102
+ x : 2d-array of shape (n, m), or 1d-array of shape (n,)
103
+ samples according to the target density proportional to `f on the
104
+ domain `[xmin, max[`, `x[i]` is the i-th sample point;
105
+ notes:
106
+
107
+ - if dimension m >= 2: `x` is a 2d-array of shape (n, m)
108
+ - if diemnsion is 1: `x` is an array of shape (n,)
109
+
110
+ t : float, optional
111
+ acceptance ratio, returned if `return_accept_ratio=True`, i.e.
112
+ `t = n/ntot` where `ntot` is the number of points draws in the
113
+ instrumental distribution
114
+ """
115
+ fname = 'acceptRejectSampler'
116
+
117
+ xmin = np.atleast_1d(xmin)
118
+ xmax = np.atleast_1d(xmax)
119
+
120
+ if xmin.ndim != xmax.ndim or np.any(np.isnan(xmin)) or np.any(np.isnan(xmax)) or np.any(xmin >= xmax):
121
+ print(f'ERROR ({fname}): `xmin`, `xmax` not valid')
122
+ return None
123
+
124
+ lx = xmax - xmin
125
+ dim = len(xmin)
126
+
127
+ if np.any(np.isinf(lx)):
128
+ dom_finite = False
129
+ else:
130
+ dom_finite = True
131
+
132
+ if n <= 0:
133
+ x = np.zeros((n, dim))
134
+ if dim == 1:
135
+ x = x.reshape(-1)
136
+ if return_accept_ratio:
137
+ return x, 1.0
138
+ else:
139
+ return x
140
+
141
+ # Set g, g_rvs
142
+ if (g is None and g_rvs is not None) or (g is not None and g_rvs is None):
143
+ print(f'ERROR ({fname}): `g` and `g_rvs` should be both specified')
144
+ return None
145
+
146
+ if g is None:
147
+ if not dom_finite:
148
+ print(f'ERROR ({fname}): `g` and `g_rvs` must be specified when infinite domain is considered')
149
+ return None
150
+ # g
151
+ g = lambda x: 1.0
152
+ # g_rvs
153
+ if dim == 1:
154
+ def g_rvs(size=1):
155
+ return xmin[0] + scipy.stats.uniform.rvs(size=size) * lx[0]
156
+ else:
157
+ def g_rvs(size=1):
158
+ return xmin + scipy.stats.uniform.rvs(size=(size,dim)) * lx
159
+
160
+ if c is None:
161
+ if not dom_finite:
162
+ print(f'ERROR ({fname}): `c` must be specified when infinite domain is considered')
163
+ return None
164
+ h = lambda x: -f(x)/g(x)
165
+ # Compute the min of h(x) with the function scipy.optimize.differential_evolution
166
+ if opt_kwargs is None:
167
+ opt_kwargs = {}
168
+ res = scipy.optimize.differential_evolution(h, bounds=list(zip(xmin, xmax)), **opt_kwargs)
169
+ if not res.success:
170
+ print(f'ERROR ({fname}): `scipy.optimize.differential_evolution` failed {res.message})')
171
+ return None
172
+ # -> res.x realizes the minimum of h(x)
173
+ # -> res.fun is the minimum of h(x)
174
+ # Set c such that c > f(x)/g(x) for all x in the domain
175
+ c = -res.fun + 1.e-3 # add small number to ensure the inequality
176
+
177
+ # Apply accept-reject algo
178
+ naccept = 0
179
+ ntot = 0
180
+ x = []
181
+ if max_trial is None:
182
+ max_trial = np.inf
183
+ if show_progress:
184
+ progress = 0
185
+ progressOld = -1
186
+ while naccept < n:
187
+ nn = n - naccept
188
+ ntot = ntot+nn
189
+ xnew = g_rvs(size=nn)
190
+ ind = np.all((xnew >= xmin, xnew < xmax), axis=0)
191
+ if dim > 1:
192
+ ind = np.all(ind, axis=-1)
193
+ xnew = xnew[ind]
194
+ nn = len(xnew)
195
+ if nn == 0:
196
+ continue
197
+ u = np.random.random(size=nn)
198
+ xnew = xnew[u < (f(xnew)/(c*g(xnew))).reshape(nn)]
199
+ nn = len(xnew)
200
+ if nn == 0:
201
+ continue
202
+ x.extend(xnew)
203
+ naccept = naccept+nn
204
+ if show_progress:
205
+ progress = int(100*naccept/n)
206
+ if progress > progressOld:
207
+ print(f'A-R algo, progress: {progress:3d} %')
208
+ progressOld = progress
209
+ if ntot >= max_trial:
210
+ ok = False
211
+ break
212
+
213
+ x = np.asarray(x)
214
+
215
+ if naccept < n:
216
+ print(f'WARNING ({fname}): sample size is only {naccept}! (increase `max_trial`)')
217
+
218
+ if return_accept_ratio:
219
+ accept_ratio = naccept/ntot
220
+ return x, accept_ratio
221
+ else:
222
+ return x
223
+ # ----------------------------------------------------------------------------
224
+
225
+ # ----------------------------------------------------------------------------
226
+ def poissonPointProcess(mu, xmin=0.0, xmax=1.0, ninterval=None):
227
+ """
228
+ Generates random points following a Poisson point process.
229
+
230
+ Random points are in `[xmin, xmax[` (can be multi-dimensional).
231
+
232
+ Parameters
233
+ ----------
234
+ mu : function (`callable`), or ndarray of floats, or float
235
+ intensity of the Poisson process, i.e. the mean number of points per
236
+ unitary volume:
237
+
238
+ - if a function: (non-homogeneous Poisson point process) \
239
+ `mu(x)` returns the intensity at `x`; with `x` array_like, the last \
240
+ axis of `x` denotes the components of the points where the function is \
241
+ evaluated
242
+ - if a ndarray: (non-homogeneous Poisson point process) \
243
+ `mu[i_n, ..., i_0]` is the intensity on the box \
244
+ `[xmin[j]+i_j*(xmax[j]-xmin[j])/mu.shape[n-j]]`, j = 0,..., n
245
+ - if a float: homogeneous Poisson point process
246
+
247
+ xmin : float (or int), or array-like of shape(m,)
248
+ lower bound of each coordinate
249
+
250
+ xmax : float (or int), or array-like of shape(m,)
251
+ upper bound of each coordinate
252
+
253
+ ninterval : int, or array-like of ints of shape (m,), optional
254
+ used only if `mu` is a function (callable);
255
+ `ninterval` contains the number of interval(s) in which the domain
256
+ `[xmin, xmax[` is subdivided along each axis
257
+
258
+ Returns
259
+ -------
260
+ pts : 2D array of shape (npts, m)
261
+ each row is a random point in the domain `[xmin, xmax[`, the number of
262
+ points (`npts`) follows a Poisson law of the given intensity (`mu`) and
263
+ m is the dimension of the domain
264
+ """
265
+ fname = 'poissonPointProcess'
266
+
267
+ xmin = np.atleast_1d(xmin)
268
+ xmax = np.atleast_1d(xmax)
269
+
270
+ if xmin.ndim != xmax.ndim or xmin.ndim != 1:
271
+ print(f'ERROR ({fname}): `xmin`, `xmax` not valid (dimension or shape)')
272
+ return None
273
+
274
+ if np.any(xmin >= xmax):
275
+ print(f'ERROR ({fname}): `xmin`, `xmax` not valid ((component of) xmin less than or equal to xmax)')
276
+ return None
277
+
278
+ # dimension
279
+ dim = len(xmin)
280
+
281
+ if callable(mu):
282
+ if ninterval is None:
283
+ print(f'ERROR ({fname}): `ninterval` must be specified when a function is passed for the intensity (mu)')
284
+ return None
285
+
286
+ ninterval = np.asarray(ninterval, dtype=int) # possibly 0-dimensional
287
+ if ninterval.size == 1:
288
+ ninterval = ninterval.flat[0] * np.ones(dim)
289
+ elif ninterval.size != dim:
290
+ print(f'ERROR ({fname}): `ninterval` does not have an acceptable size')
291
+ return None
292
+
293
+ if np.any(ninterval < 1):
294
+ print(f'ERROR ({fname}): `ninterval` has negative or zero value')
295
+ return None
296
+
297
+ elif isinstance(mu, np.ndarray):
298
+ if mu.ndim != dim:
299
+ print(f'ERROR ({fname}): inconsistent number of dimension for the ndarray `mu`')
300
+ return None
301
+
302
+ ninterval = mu.shape[::-1]
303
+
304
+ else: # mu is a float
305
+ mu = np.atleast_1d(mu)
306
+ for i in range(dim-1):
307
+ mu = mu[np.newaxis,...]
308
+ # mu is a ndarray with dim dimension of shape (1,...,1) --> grid with one cell
309
+
310
+ ninterval = mu.shape
311
+
312
+ # spacing of the grid cell along each axis
313
+ spa = [(b-a)/n for a, b, n in zip(xmin, xmax, ninterval)]
314
+ # cell volume
315
+ vol_cell = np.prod(spa)
316
+ # cell center along each axis
317
+ x_cell_center = [a + (0.5 + np.arange(n)) * s for a, n, s in zip(xmin, ninterval, spa)]
318
+ # center of each grid cell
319
+ xx_cell_center = np.meshgrid(*x_cell_center[::-1], indexing='ij')[::-1]
320
+ xx_cell_center = np.array([xx.reshape(-1) for xx in xx_cell_center]).T # shape: ncell x dim
321
+
322
+ # Poisson parameter (intensity) for each grid cell
323
+ if callable(mu):
324
+ mu_cell = mu(xx_cell_center)*vol_cell
325
+ else:
326
+ mu_cell = mu.reshape(-1) * vol_cell
327
+
328
+ # Generate number of points in each grid cell (Poisson)
329
+ npts_cell = np.array([scipy.stats.poisson.rvs(m) for m in mu_cell])
330
+
331
+ # Generate random points (uniformly) in each cell
332
+ pts = np.array([np.hstack(
333
+ [a + spa[i] * (np.random.random(size=npts) - 0.5) for a, npts in zip(xx_cell_center[:,i], npts_cell)]
334
+ ) for i in range(dim)]).T
335
+
336
+ return pts
337
+ # ----------------------------------------------------------------------------
338
+
339
+ # ----------------------------------------------------------------------------
340
+ def chentsov1D(n_mean,
341
+ dimension, spacing=1.0, origin=0.0,
342
+ direction_origin=None,
343
+ p_min=None, p_max=None,
344
+ nreal=1):
345
+ """
346
+ Generates a Chentsov's simulation in 1D.
347
+
348
+ The domain of simulation is `[xmin, xmax]`, with `nx` cells along x axis,
349
+ each cell having a length of `dx`, the left side is the origin:
350
+
351
+ - along x axis:
352
+ - `nx = dimension`
353
+ - `dx = spacing`
354
+ - `xmin = origin`
355
+ - `xmax = origin + nx*dx`
356
+
357
+ The simulation consists in:
358
+
359
+ 1. Drawing random hyper-plane (i.e. points in 1D) in the space
360
+ [`p_min`, `p_max`] following a Poisson point process with intensity:
361
+
362
+ * mu = `n_mean` / vol([`p_min`, `p_max`]);
363
+
364
+ the points are given in the parametrized form: p;
365
+ then, for each point p, and with direction_origin = x0
366
+ (the center of the simulation domain by default), the hyper-plane
367
+ (point)
368
+
369
+ * {x : x-x0 = p} (i.e. the point x0 + p)
370
+
371
+ is considered
372
+
373
+ 2. Each hyper-plane (point x0+p) splits the space (R) in two parts
374
+ (two half lines); the value = +1 is set to one part (chosen
375
+ randomly) and the value -1 is set to the other part. Denoting V_i
376
+ the value over the space (R) associated to the i-th hyper-plane
377
+ (point), the value assigned to a grid cell of center x is set to
378
+
379
+ * Z(x) = 0.5 * sum_{i} (V_i(x) - V_i(x0))
380
+
381
+ It corresponds to the number of hyper-planes (points) cut by the
382
+ segment [x0, x].
383
+
384
+ Parameters
385
+ ----------
386
+ n_mean : float
387
+ mean number of hyper-plane drawn (via Poisson process)
388
+
389
+ dimension : int
390
+ `dimension=nx`, number of cells in the 1D simulation grid
391
+
392
+ spacing : float, default: 1.0
393
+ `spacing=dx`, cell size
394
+
395
+ origin : float, default: 0.0
396
+ `origin=ox`, origin of the 1D simulation grid (left border)
397
+
398
+ direction_origin : float, optional
399
+ origin from which the "points" are drawn in the Poisson process
400
+ (see above);
401
+ by default (`None`): the center of the 1D simulation domain is used
402
+
403
+ p_min : float, optional
404
+ minimal value for p (see above);
405
+ by default (`None`): `p_min` is set automatically to "minus half of the
406
+ length of the 1D simulation domain"
407
+
408
+ p_max : float, optional
409
+ maximal value for p (see above);
410
+ by default (`None`): `p_max` is set automatically to "plus half of the
411
+ length of the 1D simulation domain
412
+
413
+ nreal : int, default: 1
414
+ number of realization(s)
415
+
416
+ Returns
417
+ -------
418
+ sim : 2D array of floats of shape (nreal, nx)
419
+ simulations of Z (see above);
420
+ `sim[i, j]`: value of the i-th realisation at grid cell of index j
421
+
422
+ n : 1D array of shape (nreal,)
423
+ numbers of hyper-planes (points) drawn, `n[i]` is the number of
424
+ hyper-planes for the i-th realization
425
+ """
426
+ fname = 'chentsov1D'
427
+
428
+ # Number of realization(s)
429
+ nreal = int(nreal) # cast to int if needed
430
+
431
+ if nreal <= 0:
432
+ print('CHENTSOV1D: nreal <= 0: nothing to do!')
433
+
434
+ nx = dimension
435
+ dx = spacing
436
+ ox = origin
437
+
438
+ if direction_origin is None:
439
+ direction_origin = ox+0.5*nx*dx
440
+
441
+ if p_min is None or p_max is None:
442
+ d = 0.5*nx*dx
443
+ if p_min is None:
444
+ p_min = -d
445
+ if p_max is None:
446
+ p_max = d
447
+
448
+ if p_min >= p_max:
449
+ print(f"ERROR ({fname}): `p_min` is greater than or equal to `p_max`")
450
+ return None
451
+
452
+ # center of each grid cell of the simulation domain
453
+ xc = ox + (0.5 + np.arange(nx)) * dx
454
+
455
+ # Volume of [p_min, p_max]
456
+ vol_poisson_domain = (p_max - p_min)
457
+
458
+ # Set intensity of Poisson process
459
+ mu = n_mean / vol_poisson_domain
460
+
461
+ # Initialization
462
+ z = np.zeros((nreal, nx))
463
+ n = np.zeros(nreal, dtype='int')
464
+
465
+ for k in range(nreal):
466
+ # Draw points via Poisson process
467
+ pts = poissonPointProcess(mu, p_min, p_max)
468
+ n[k] = pts.shape[0]
469
+
470
+ # Defines values of Z in each grid cell
471
+ random_sign = (-1)**np.random.randint(2, size=n[k])
472
+ for i in range(n[k]):
473
+ z[k] = z[k] + (np.sign((xc-direction_origin)-pts[i])+np.sign(pts[i]))*random_sign[i]
474
+
475
+ z = 0.5*z
476
+
477
+ return z, n
478
+ # ----------------------------------------------------------------------------
479
+
480
+ # ----------------------------------------------------------------------------
481
+ def chentsov2D(n_mean,
482
+ dimension, spacing=(1.0, 1.0), origin=(0.0, 0.0),
483
+ direction_origin=None,
484
+ phi_min=0.0, phi_max=np.pi,
485
+ p_min=None, p_max=None,
486
+ nreal=1):
487
+ """
488
+ Generates a Chentsov's simulation in 2D.
489
+
490
+ The domain of simulation is `[xmin, xmax]` x `[ymin x ymax]`,
491
+ with `nx` and `ny` cells along x axis and y axis respectively, each cell
492
+ being a box of size `dx` x `dy`, the lower-left corner is the origin:
493
+
494
+ - along x axis:
495
+ - `nx = dimension[0]`
496
+ - `dx = spacing[0]`
497
+ - `xmin = origin[0]`
498
+ - `xmax = origin[0] + nx*dx`
499
+
500
+ - along y axis:
501
+ - `ny = dimension[1]`
502
+ - `dy = spacing[1]`
503
+ - `ymin = origin[1]`
504
+ - `ymax = origin[1] + ny*dy`
505
+
506
+ The simulation consists in:
507
+
508
+ 1. Drawing random hyper-plane (i.e. lines in 2D):
509
+ considering the space S x [`p_min`, `p_max`], where S is a part of
510
+ the circle of radius 1 in the plane (by default: half circle),
511
+ parametrized via
512
+
513
+ * phi -> (cos(phi), sin(phi)), with phi in [`phi_min`, `phi_max`],
514
+
515
+ some points are drawn randomly in S x [`p_min`, `p_max`] following a
516
+ Poisson point process with intensity
517
+
518
+ * mu = `n_mean` / vol(S x [`p_min`, `p_max`])
519
+
520
+ the points are given in the parametrized form: (phi, p);
521
+ then, for each point (phi, p), and with direction_origin = (x0, y0)
522
+ (the center of the simulation domain by default), the hyper-plane
523
+ (line)
524
+
525
+ * {(x, y) : dot([x-x0, y-y0], [cos(phi), sin(phi)]) = p}
526
+
527
+ (i.e. point (x, y) s.t. the orthogonal projection of (x-x0, y-y0)
528
+ onto the direction (cos(phi), sin(phi)) is equal to p) is considered
529
+
530
+ 2. Each hyper-plane (line) splits the space (R^2) in two parts (two half
531
+ planes); the value = +1 is set to one part (chosen randomly) and the
532
+ value -1 is set to the other part. Denoting V_i the value over the
533
+ space (R^2) associated to the i-th hyper-plane (line), the value
534
+ assigned to a grid cell of center (x, y) is set to
535
+
536
+ * Z(x, y) = 0.5 * sum_{i} (V_i(x, y) - V_i(x0, y0))
537
+
538
+ It corresponds to the number of hyper-planes cut by the segment
539
+ [(x0, y0), (x, y)].
540
+
541
+ Parameters
542
+ ----------
543
+ n_mean : float
544
+ mean number of hyper-plane drawn (via Poisson process)
545
+
546
+ dimension : 2-tuple of ints
547
+ `dimension=(nx, ny)`, number of cells in the 2D simulation grid along
548
+ each axis
549
+
550
+ spacing : 2-tuple of floats, default: (1.0, 1.0)
551
+ `spacing=(dx, dy)`, cell size along each axis
552
+
553
+ origin : 2-tuple of floats, default: (0.0, 0.0)
554
+ `origin=(ox, oy)`, origin of the 2D simulation grid (lower-left corner)
555
+
556
+ direction_origin : sequence of 2 floats, optional
557
+ origin from which the directions are drawn in the Poisson process
558
+ (see above);
559
+ by default (`None`): the center of the 2D simulation domain is used
560
+
561
+ phi_min : float, default: 0.0
562
+ minimal angle for the parametrization of S (part of circle) defining
563
+ the direction (see above)
564
+
565
+ phi_max : float, default: `numpy.pi`
566
+ maximal angle for the parametrization of S (part of circle) defining
567
+ the direction (see above)
568
+
569
+ p_min : float, optional
570
+ minimal value for orthogonal projection (see above);
571
+ by default (`None`): `p_min` is set automatically to "minus half of the
572
+ diagonal of the 2D simulation domain"
573
+
574
+ p_max : float, optional
575
+ maximal value for orthogonal projection (see above);
576
+ by default (`None`): `p_min` is set automatically to "plus half of the
577
+ diagonal of the 2D simulation domain"
578
+
579
+ nreal : int, default: 1
580
+ number of realization(s)
581
+
582
+ Returns
583
+ -------
584
+ sim : 3D array of floats of shape (nreal, ny, nx)
585
+ simulations of Z (see above);
586
+ `sim[i, iy, ix]`: value of the i-th realisation at grid cell of index ix
587
+ (resp. iy) along x (resp. y) axis
588
+
589
+ n : 1D array of shape (nreal,)
590
+ numbers of hyper-planes (lines) drawn, `n[i]` is the number of
591
+ hyper-planes for the i-th realization
592
+ """
593
+ fname = 'chentsov2D'
594
+
595
+ # Number of realization(s)
596
+ nreal = int(nreal) # cast to int if needed
597
+
598
+ if nreal <= 0:
599
+ print('CHENTSOV2D: nreal <= 0: nothing to do!')
600
+
601
+ nx, ny = dimension
602
+ dx, dy = spacing
603
+ ox, oy = origin
604
+
605
+ if direction_origin is None:
606
+ direction_origin = [ox+0.5*nx*dx, oy+0.5*ny*dy]
607
+
608
+ if p_min is None or p_max is None:
609
+ d = 0.5*np.sqrt((nx*dx)**2+(ny*dy)**2)
610
+ if p_min is None:
611
+ p_min = -d
612
+ if p_max is None:
613
+ p_max = d
614
+
615
+ if p_min >= p_max:
616
+ print(f"ERROR ({fname}): `p_min` is greater than or equal to `p_max`")
617
+ return None
618
+
619
+ if phi_min >= phi_max:
620
+ print(f"ERROR ({fname}): 'phi_min' is greater than or equal to 'phi_max'")
621
+ return None
622
+
623
+ # center of each grid cell of the simulation domain
624
+ yc, xc = np.meshgrid(oy + (0.5 + np.arange(ny)) * dy, ox + (0.5 + np.arange(nx)) * dx, indexing='ij')
625
+ xyc = np.array([xc.reshape(-1), yc.reshape(-1)]).T # shape: ncell x 2
626
+
627
+ # Volume of S x [p_min, p_max], (S being parametrized by phi in [phi_min, phi_max])
628
+ vol_poisson_domain = (phi_max - phi_min) * (p_max - p_min)
629
+
630
+ # Defines lines by random points in [phi_min, phi_max] x [p_min, p_max]
631
+ # if callable(mu):
632
+ # def mu_intensity(x):
633
+ # return mu(x) / vol_poisson_domain
634
+ # else:
635
+ # mu_intensity = mu / vol_poisson_domain
636
+
637
+ # Set intensity of Poisson process
638
+ mu = n_mean / vol_poisson_domain
639
+
640
+ # Initialization
641
+ z = np.zeros((nreal, nx*ny))
642
+ n = np.zeros(nreal, dtype='int')
643
+
644
+ for k in range(nreal):
645
+ # Draw points via Poisson process
646
+ pts = poissonPointProcess(mu, [phi_min, p_min], [phi_max, p_max])
647
+ n[k] = pts.shape[0]
648
+
649
+ # Defines values of Z in each grid cell
650
+ random_sign = (-1)**np.random.randint(2, size=n[k])
651
+ # Equivalent method below (4/ is better!)
652
+ # 1/
653
+ # vp = np.sum([np.sign((xyc-direction_origin).dot(np.array([np.cos(a), np.sin(a)]))-p)*rs for a, p, rs in zip(pts[:,0], pts[:,1], random_sign)], axis=0)
654
+ # v0 = np.sum([np.sign(-p)*rs for p, rs in zip(pts[:,1], random_sign)])
655
+ # z = 0.5 *(vp - v0)
656
+ # 2/
657
+ # z = 0.5*np.sum([(np.sign((xyc-direction_origin).dot(np.array([np.cos(a), np.sin(a)]))-p)+np.sign(p))*rs for a, p, rs in zip(pts[:,0], pts[:,1], random_sign)], axis=0)
658
+ # 3/
659
+ # z = 0.5*np.sum([(np.sign((xyc-direction_origin).dot(np.array([np.cos(pts[i,0]), np.sin(pts[i,0])]))-pts[i,1])+np.sign(pts[i,1]))*random_sign[i] for i in range(n[k])], axis=0)
660
+ # 4/
661
+ for i in range(n[k]):
662
+ z[k] = z[k] + (np.sign((xyc-direction_origin).dot(np.array([np.cos(pts[i,0]), np.sin(pts[i,0])]))-pts[i,1])+np.sign(pts[i,1]))*random_sign[i]
663
+
664
+ z = 0.5*z
665
+
666
+ return z.reshape(nreal, ny, nx), n
667
+ # ----------------------------------------------------------------------------
668
+
669
+ # ----------------------------------------------------------------------------
670
+ def chentsov3D(n_mean,
671
+ dimension, spacing=(1.0, 1.0, 1.0), origin=(0.0, 0.0, 0.0),
672
+ direction_origin=None,
673
+ phi_min=0.0, phi_max=2.0*np.pi,
674
+ theta_min=0.0, theta_max=0.5*np.pi,
675
+ p_min=None, p_max=None,
676
+ ninterval_theta=100,
677
+ nreal=1):
678
+ """
679
+ Generates a Chentsov's simulation in 3D.
680
+
681
+ The domain of simulation is
682
+ `[xmin, xmax]` x `[ymin x ymax]` x `[zmin x zmax]`,
683
+ with `nx`, `ny`, `nz` cells along x axis, y axis, z axis respectively, each
684
+ cell being a box of size `dx` x `dy` x `dy`, the bottom-lower-left corner is
685
+ the origin:
686
+
687
+ - along x axis:
688
+ - `nx = dimension[0]`
689
+ - `dx = spacing[0]`
690
+ - `xmin = origin[0]`
691
+ - `xmax = origin[0] + nx*dx`
692
+
693
+ - along y axis:
694
+ - `ny = dimension[1]`
695
+ - `dy = spacing[1]`
696
+ - `ymin = origin[1]`
697
+ - `ymax = origin[1] + ny*dy`
698
+
699
+ - along z axis:
700
+ - `nz = dimension[0]`
701
+ - `dz = spacing[0]`
702
+ - `zmin = origin[0]`
703
+ - `zmax = origin[0] + nz*dz`.
704
+
705
+ The simulation consists in:
706
+
707
+ 1. Drawing random hyper-plane (i.e. planes in 3D):
708
+ considering the space S x [`p_min`, `p_max`], where S is a part of
709
+ the sphere of radius 1 in the 3D space (by default: half sphere),
710
+ parametrized via
711
+
712
+ * (phi, theta) -> (cos(phi)cos(theta), sin(phi)cos(theta), sin(theta)), \
713
+ with phi in [`phi_min`, `phi_max`], theta in [`theta_min`, `theta_max`]
714
+
715
+ some points are drawn randomly in S x [`p_min`, `p_max`] following a
716
+ Poisson point process with intensity
717
+
718
+ * mu = `n_mean` / vol(S x [`p_min`, `p_max`]);
719
+
720
+ the points are given in the parametrized form: (phi, theta, p);
721
+ then, for each point (phi, theta, p), and with
722
+ direction_origin = (x0, y0, z0) (the center of the simulation domain
723
+ by default), the hyper-plane (plane)
724
+
725
+ * {(x, y, z) : dot([x-x0, y-y0, z-z0], [cos(phi)cos(theta), sin(phi)cos(theta), sin(theta)]) = p}
726
+
727
+ (i.e. point (x, y, z) s.t. the orthogonal projection of
728
+ (x-x0, y-y0, z-z0) onto the direction
729
+ (cos(phi)cos(theta), sin(phi)cos(theta), sin(theta)) is equal to p)
730
+ is considered;
731
+
732
+ 2. Each hyper-plane (plane) splits the space (R^3) in two parts;
733
+ the value = +1 is set to one part (chosen randomly) and the value -1
734
+ is set to the other part. Denoting V_i the value over the space (R^3)
735
+ associated to the i-th hyper-plane (plane), the value assigned to a
736
+ grid cell of center (x, y) is set to
737
+
738
+ * Z(x, y) = 0.5 * sum_{i} (V_i(x, y) - V_i(x0, y0))
739
+
740
+ It corresponds to the number of hyper-planes (planes) cut by the
741
+ segment [(x0, y0, z0), (x, y, z)].
742
+
743
+ Parameters
744
+ ----------
745
+ n_mean : float
746
+ mean number of hyper-plane drawn (via Poisson process)
747
+
748
+ dimension : 3-tuple of ints
749
+ `dimension=(nx, ny, nz)`, number of cells in the 3D simulation grid along
750
+ each axis
751
+
752
+ spacing : 3-tuple of floats, default: (1.0,1.0, 1.0)
753
+ `spacing=(dx, dy, dz)`, cell size along each axis
754
+
755
+ origin : 3-tuple of floats, default: (0.0, 0.0, 0.0)
756
+ `origin=(ox, oy, oz)`, origin of the 3D simulation grid (bottom-lower-left
757
+ corner)
758
+
759
+ direction_origin : sequence of 3 floats, optional
760
+ origin from which the directions are drawn in the Poisson process
761
+ (see above);
762
+ by default (`None`): the center of the 3D simulation domain is used
763
+
764
+ phi_min : float, default: 0.0
765
+ minimal angle for the parametrization of S (part of circle) defining
766
+ the direction (see above)
767
+
768
+ phi_max : float, default: `numpy.pi`
769
+ maximal angle for the parametrization of S (part of circle) defining
770
+ the direction (see above)
771
+
772
+ theta_min : float, default: 0.0
773
+ minimal angle for the parametrization of S (part of circle) defining
774
+ the direction (see above)
775
+
776
+ theta_max : float, default: `0.5*numpy.pi`
777
+ maximal angle for the parametrization of S (part of circle) defining
778
+ the direction (see above)
779
+
780
+ p_min : float, optional
781
+ minimal value for orthogonal projection (see above);
782
+ by default (`None`): `p_min` is set automatically to "minus half of the
783
+ diagonal of the 3D simulation domain"
784
+
785
+ p_max : float, optional
786
+ maximal value for orthogonal projection (see above);
787
+ by default (`None`): `p_min` is set automatically to "plus half of the
788
+ diagonal of the 3D simulation domain"
789
+
790
+ ninterval_theta : int, default: 100
791
+ number of sub-intervals in which the interval `[theta_min, theta_max]`
792
+ is subdivided for applying the Poisson process
793
+
794
+ nreal : int, default: 1
795
+ number of realization(s)
796
+
797
+ Returns
798
+ -------
799
+ sim : 4D array of floats of shape (nreal, nz, ny, nx)
800
+ simulations of Z (see above);
801
+ `sim[i, iz, iy, ix]`: value of the i-th realisation at grid cell of
802
+ index ix (resp. iy, iz) along x (resp. y, z) axis
803
+
804
+ n : 1D array of shape (nreal,)
805
+ numbers of hyper-planes (planes) drawn, `n[i]` is the number of
806
+ hyper-planes for the i-th realization
807
+ """
808
+ fname = 'chentsov3D'
809
+
810
+ # Number of realization(s)
811
+ nreal = int(nreal) # cast to int if needed
812
+
813
+ if nreal <= 0:
814
+ print('CHENTSOV3D: nreal <= 0: nothing to do!')
815
+
816
+ nx, ny, nz = dimension
817
+ dx, dy, dz = spacing
818
+ ox, oy, oz = origin
819
+
820
+ if direction_origin is None:
821
+ direction_origin = [ox+0.5*nx*dx, oy+0.5*ny*dy, oz+0.5*nz*dz]
822
+
823
+ if p_min is None or p_max is None:
824
+ d = 0.5*np.sqrt((nx*dx)**2+(ny*dy)**2+(nz*dz)**2)
825
+ if p_min is None:
826
+ p_min = -d
827
+ if p_max is None:
828
+ p_max = d
829
+
830
+ if p_min >= p_max:
831
+ print(f"ERROR ({fname}): `p_min` is greater than or equal to `p_max`")
832
+ return None
833
+
834
+ if phi_min >= phi_max:
835
+ print(f"ERROR ({fname}): `phi_min` is greater than or equal to `phi_max`")
836
+ return None
837
+
838
+ if theta_min >= theta_max:
839
+ print(f"ERROR ({fname}): `theta_min` is greater than or equal to `theta_max`")
840
+ return None
841
+
842
+ # center of each grid cell of the simulation domain
843
+ zc, yc, xc = np.meshgrid(oz + (0.5 + np.arange(nz)) * dz, oy + (0.5 + np.arange(ny)) * dy, ox + (0.5 + np.arange(nx)) * dx, indexing='ij')
844
+ xyzc = np.array([xc.reshape(-1), yc.reshape(-1), zc.reshape(-1)]).T # shape: ncell x 3
845
+
846
+ # Volume of S x [p_min, p_max], (S being parametrized by phi in [phi_min, phi_max], and theta in [theta_min, theta_max])
847
+ vol_poisson_domain = (phi_max - phi_min) * (np.sin(theta_max) - np.sin(theta_min)) * (p_max - p_min)
848
+
849
+ # Set intensity of Poisson process as a function accounting for jacobian of the parametrization of S
850
+ def mu(x):
851
+ return n_mean * np.cos(x[:, 1])/ vol_poisson_domain # x = (phi, theta), cos(x[:, 1] = cos(theta)
852
+
853
+ # Initialization
854
+ z = np.zeros((nreal, nx*ny*nz))
855
+ n = np.zeros(nreal, dtype='int')
856
+
857
+ for k in range(nreal):
858
+ # Draw points via Poisson process
859
+ pts = poissonPointProcess(mu, [phi_min, theta_min, p_min], [phi_max, theta_max, p_max], ninterval=[1, ninterval_theta, 1])
860
+ n[k] = pts.shape[0]
861
+
862
+ # Defines values of Z in each grid cell
863
+ random_sign = (-1)**np.random.randint(2, size=n[k])
864
+ # 4/
865
+ for i in range(n[k]):
866
+ z[k] = z[k] + (np.sign((xyzc-direction_origin).dot(np.array([np.cos(pts[i,0])*np.cos(pts[i,1]), np.sin(pts[i,0])*np.cos(pts[i,1]), np.sin(pts[i,1])]))-pts[i,2])+np.sign(pts[i,2]))*random_sign[i]
867
+ z = 0.5*z
868
+
869
+ return z.reshape(nreal, nz, ny, nx), n
870
+ # ----------------------------------------------------------------------------
871
+
872
+ if __name__ == "__main__":
873
+ print("Module 'geone.randProcess'.")
874
+
875
+ ##### OLD BELOW #####
876
+ # # ----------------------------------------------------------------------------
877
+ # def acceptRejectSampler(n, xmin, xmax, f, c=None, g=None, g_rvs=None,
878
+ # return_accept_ratio=False,
879
+ # max_trial=None, show_progress=False):
880
+ # """
881
+ # Generates samples according to a given density function.
882
+ #
883
+ # This function generates `n` points in a box-shape domain of lower bound(s)
884
+ # `xmin` and upper bound(s) `xmax`, according to a density proportional to the
885
+ # function `f` are generated, based on the accept-reject algorithm.
886
+ #
887
+ # Let `g_rvs` a function returning random variates sample(s) from an
888
+ # instrumental distribution with density proportional to `g`, and `c` a
889
+ # constant such that `c*g(x) >= f(x)` for any `x` (in `[xmin, xmax[` (can be
890
+ # multi-dimensional), i.e. `x[i]` in `[xmin[i], xmax[i][` for any i). Let `fd`
891
+ # (resp. `gd`) the density function proportional to `f` (resp. `g`); the
892
+ # alogrithm consists in the following steps to generate samples `x ~ fd`:
893
+ # - generate `y ~ gd` (using `g_rvs`)
894
+ # - generate `u ~ Unif([0,1])`
895
+ # - if `u < f(y)/c*g(y)`, then accept `x` (reject `x` otherwise)
896
+ #
897
+ # If the instrumental distribution is not specified (both `g` and `g_rvs` set
898
+ # to `None`), then:
899
+ # - the uniform distribution if the domain `[xmin, xmax[` is finite
900
+ # - the multi-normal distribution, centered at a point maximizing `f`, with
901
+ # a variance 1 (covariance matrix I), if the domain `[xmin, xmax[` is infinite
902
+ #
903
+ # Parameters
904
+ # ----------
905
+ # n : int
906
+ # number of sample points
907
+ # xmin : float (or int), or array-like of shape(m,)
908
+ # lower bound of each coordinate (m is the space dimension);
909
+ # note: component(s) can be set to `-np.inf`
910
+ # xmax : float (or int), or array-like of shape(m,)
911
+ # upper bound of each coordinate (m is the space dimension)
912
+ # note: component(s) can be set to `np.inf`
913
+ # f : function (callable)
914
+ # function proportional to target density, `f(x)` returns the target
915
+ # density (times a constant) at `x`; with `x` array_like, the last
916
+ # axis of `x` denotes the components of the points where the function is
917
+ # evaluated
918
+ # c : float (or int), optional
919
+ # constant such that (not checked)) `c*g(x) >= f(x)` for all x in
920
+ # [xmin, xmax[, with `g(x)=1` if `g` is not specified (`g=None`);
921
+ # by default (`c=None`), `c` is automatically computed (using the function
922
+ # `scipy.optimize.minimize`)
923
+ # g : function (callable), optional
924
+ # function proportional to the instrumental density on `[xmin, xmax[`,
925
+ # `g(x)` returns the instrumental density (times a constant) at `x`;
926
+ # with `x` array_like, the last axis of `x` denotes the components of the
927
+ # points where the function is evaluated;
928
+ # by default (`g=None`): the instrumental distribution considered is
929
+ # - uniform (constant function `g=1` is considered), if the domain
930
+ # `[xmin, xmax[` is finite
931
+ # - (multi-)normal density of variance 1 (covariance matrix I), centered
932
+ # at a point maximizing `f`, otherwise;
933
+ # g_rvs : function (callable), optional
934
+ # function returning samples from the instrumental distribution with
935
+ # density proportional to `g` on `[xmin, xmax[` (restricted on this
936
+ # domain if needed); `g_rvs` must have the keyword arguments `size`
937
+ # (the number of sample(s) to draw)
938
+ # by default: uniform or non-correlated multi-normal instrumental
939
+ # distribution is considered (see `g`);
940
+ # note: both `g` and `g_rvs` must be specified (or both set to `None`)
941
+ # return_accept_ratio : bool, default: False
942
+ # indicates if the acceptance ratio is returned
943
+ # show_progress : bool, default: False
944
+ # indicates if progress is displayed (True) or not (False)
945
+ #
946
+ # Returns
947
+ # -------
948
+ # x : 2d-array of shape (n, m), or 1d-array of shape (n,)
949
+ # samples according to the target density proportional to `f on the
950
+ # domain `[xmin, max[`, `x[i]` is the i-th sample point;
951
+ # notes:
952
+ # - if dimension m >= 2: `x` is a 2d-array of shape (n, m)
953
+ # - if diemnsion is 1: `x` is an array of shape (n,)
954
+ # t : float, optional
955
+ # acceptance ratio, returned if `return_accept_ratio=True`, i.e.
956
+ # `t = n/ntot` where `ntot` is the number of points draws in the
957
+ # instrumental distribution
958
+ # """
959
+ # fname = 'acceptRejectSampler'
960
+ #
961
+ # xmin = np.atleast_1d(xmin)
962
+ # xmax = np.atleast_1d(xmax)
963
+ #
964
+ # if xmin.ndim != xmax.ndim or np.any(np.isnan(xmin)) or np.any(np.isnan(xmax)) or np.any(xmin >= xmax):
965
+ # print(f'ERROR ({fname}): `xmin`, `xmax` not valid')
966
+ # return None
967
+ #
968
+ # lx = xmax - xmin
969
+ # dim = len(xmin)
970
+ #
971
+ # x = np.zeros((n, dim)) # initialize random samples (one sample by row)
972
+ # if n <= 0:
973
+ # if return_accept_ratio:
974
+ # return x, 1.0
975
+ # else:
976
+ # return x
977
+ #
978
+ # # Set g, g_rvs, and c
979
+ # if (g is None and g_rvs is not None) or (g is not None and g_rvs is None):
980
+ # print(f'ERROR ({fname}): `g` and `g_rvs` should be both specified')
981
+ # return None
982
+ #
983
+ # mu = None # not necessarily used
984
+ # if c is None:
985
+ # if g is None:
986
+ # h = lambda x: -f(x)
987
+ # else:
988
+ # h = lambda x: -f(x)/g(x)
989
+ # # Compute the min of h(x) with the function scipy.optimize.minimize
990
+ # # x0: initial guess (random)
991
+ # x0 = xmin + np.random.random(size=dim)*lx
992
+ # for i, binf in enumerate(np.isinf(x0)):
993
+ # if binf:
994
+ # x0[i] = min(xmax[i], max(xmin[i], 0.0))
995
+ # res = scipy.optimize.minimize(h, x0, bounds=list(zip(xmin, xmax)))
996
+ # if not res.success:
997
+ # print(f'ERROR ({fname}): `scipy.optimize.minimize` failed {res.message})')
998
+ # return None
999
+ # # -> res.x realizes the minimum of h(x)
1000
+ # # -> res.fun is the minimum of h(x)
1001
+ # mu = res.x
1002
+ # # Set c such that c > f(x)/g(x) for all x in the domain
1003
+ # c = -res.fun + 1.e-3 # add small number to ensure the inequality
1004
+ #
1005
+ # if g is None:
1006
+ # if np.any((np.isinf(xmin), np.isinf(xmax))):
1007
+ # if mu is None:
1008
+ # # Compute the min of h(x) = (f(x)-c)**2 with the function scipy.optimize.minimize
1009
+ # h = lambda x: (f(x)-c)**2
1010
+ # # x0: initial guess (random)
1011
+ # x0 = xmin + np.random.random(size=dim)*lx
1012
+ # for i, binf in enumerate(np.isinf(x0)):
1013
+ # if binf:
1014
+ # x0[i] = min(xmax[i], max(xmin[i], 0.0))
1015
+ # res = scipy.optimize.minimize(h, x0, bounds=list(zip(xmin, xmax)))
1016
+ # if not res.success:
1017
+ # print(f'ERROR ({fname}): `scipy.optimize.minimize` failed {res.message})')
1018
+ # return None
1019
+ # # -> res.x is the minimum of h(x)
1020
+ # mu = res.x
1021
+ # # Set instrumental pdf proportional to: g = exp(-1/2 sum_i((x[i]-mu[i])**2))
1022
+ # # Set g, g_rvs
1023
+ # g = lambda x: np.exp(-0.5*np.sum((np.atleast_2d(x)-mu)**2), axis=1)
1024
+ # g_rvs = scipy.stats.multivariate_normal(mean=mu).rvs
1025
+ #
1026
+ # # Update c
1027
+ # # Compute the min of h(x) = -f(x)/g(x) with the function scipy.optimize.minimize
1028
+ # h = lambda x: -f(x)/g(x)
1029
+ # # x0: initial guess
1030
+ # x0 = mu
1031
+ # res = scipy.optimize.minimize(h, x0, bounds=list(zip(xmin, xmax)))
1032
+ # if not res.success:
1033
+ # print(f'ERROR ({fname}): `scipy.optimize.minimize` failed {res.message})')
1034
+ # return None
1035
+ # # -> res.fun is the minimum of h(x)
1036
+ #
1037
+ # # Set c such that c > f(x)/g(x) for all x in the domain
1038
+ # c = -res.fun + 1.e-3 # add small number to ensure the inequality
1039
+ #
1040
+ # else:
1041
+ # # Set instrumental pdf proportional to: g = 1 (uniform distribution)
1042
+ # # Set g, g_rvs
1043
+ # g = lambda x: 1.0
1044
+ # def g_rvs(size=1):
1045
+ # return xmin + scipy.stats.uniform.rvs(size=(size,dim)) * lx
1046
+ #
1047
+ # # Apply accept-reject algo
1048
+ # naccept = 0
1049
+ # ntot = 0
1050
+ # x = []
1051
+ # if max_trial is None:
1052
+ # max_trial = np.inf
1053
+ # if show_progress:
1054
+ # progress = 0
1055
+ # progressOld = -1
1056
+ # while naccept < n:
1057
+ # nn = n - naccept
1058
+ # ntot = ntot+nn
1059
+ # xnew = g_rvs(size=nn)
1060
+ # # print('1', xnew)
1061
+ # # print('1b', np.all(np.all((xnew >= xmin, xnew < xmax), axis=0), axis=-1))
1062
+ # xnew = xnew[np.all(np.all((xnew >= xmin, xnew < xmax), axis=0), axis=-1)]
1063
+ # # print('2', xnew)
1064
+ # nn = len(xnew)
1065
+ # if nn == 0:
1066
+ # continue
1067
+ # u = np.random.random(size=nn)
1068
+ # xnew = xnew[u < (f(xnew)/(c*g(xnew))).reshape(nn)]
1069
+ # # print('3', xnew)
1070
+ # nn = len(xnew)
1071
+ # if nn == 0:
1072
+ # continue
1073
+ # x.extend(xnew)
1074
+ # naccept = naccept+nn
1075
+ # if show_progress:
1076
+ # progress = int(100*naccept/n)
1077
+ # if progress > progressOld:
1078
+ # print(f'A-R algo, progress: {progress:3d} %')
1079
+ # progressOld = progress
1080
+ # if ntot >= max_trial:
1081
+ # ok = False
1082
+ # break
1083
+ #
1084
+ # x = np.asarray(x)
1085
+ # if dim == 1:
1086
+ # x = x.reshape(-1)
1087
+ #
1088
+ # # # Apply accept-reject algo
1089
+ # # naccept = 0
1090
+ # # ntot = 0
1091
+ # # if max_trial is None:
1092
+ # # max_trial = np.inf
1093
+ # # if show_progress:
1094
+ # # progress = 0
1095
+ # # progressOld = -1
1096
+ # # while naccept < n:
1097
+ # # ntot = ntot+1
1098
+ # # xnew = g_rvs()
1099
+ # # if np.any((xnew < xmin, xnew >= xmax)):
1100
+ # # continue
1101
+ # # u = np.random.random()
1102
+ # # if u < f(xnew)/(c*g(xnew)):
1103
+ # # x[naccept] = xnew
1104
+ # # naccept = naccept+1
1105
+ # # if show_progress:
1106
+ # # progress = int(100*naccept/n)
1107
+ # # if progress > progressOld:
1108
+ # # print(f'A-R algo, progress: {progress:3d} %')
1109
+ # # progressOld = progress
1110
+ # # if ntot >= max_trial:
1111
+ # # ok = False
1112
+ # # break
1113
+ #
1114
+ # if naccept < n:
1115
+ # print(f'WARNING: sample size is only {naccept}! (increase `max_trial`)')
1116
+ #
1117
+ # if return_accept_ratio:
1118
+ # accept_ratio = naccept/ntot
1119
+ # return x, accept_ratio
1120
+ # else:
1121
+ # return x
1122
+ # # ----------------------------------------------------------------------------