prtools 2.0.0__tar.gz → 2.0.2__tar.gz
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.
- {prtools-2.0.0/prtools.egg-info → prtools-2.0.2}/PKG-INFO +1 -1
- {prtools-2.0.0 → prtools-2.0.2}/prtools/__init__.py +3 -1
- {prtools-2.0.0 → prtools-2.0.2}/prtools/array.py +76 -0
- {prtools-2.0.0 → prtools-2.0.2}/prtools/convolve.py +26 -10
- {prtools-2.0.0 → prtools-2.0.2}/prtools/shape.py +62 -4
- {prtools-2.0.0 → prtools-2.0.2}/prtools/zernike.py +14 -12
- {prtools-2.0.0 → prtools-2.0.2/prtools.egg-info}/PKG-INFO +1 -1
- {prtools-2.0.0 → prtools-2.0.2}/tests/test_array.py +42 -1
- {prtools-2.0.0 → prtools-2.0.2}/LICENSE +0 -0
- {prtools-2.0.0 → prtools-2.0.2}/NOTICE +0 -0
- {prtools-2.0.0 → prtools-2.0.2}/README.rst +0 -0
- {prtools-2.0.0 → prtools-2.0.2}/prtools/_array_api.py +0 -0
- {prtools-2.0.0 → prtools-2.0.2}/prtools/cost.py +0 -0
- {prtools-2.0.0 → prtools-2.0.2}/prtools/fourier.py +0 -0
- {prtools-2.0.0 → prtools-2.0.2}/prtools/jax.py +0 -0
- {prtools-2.0.0 → prtools-2.0.2}/prtools/misc.py +0 -0
- {prtools-2.0.0 → prtools-2.0.2}/prtools/morphology.py +0 -0
- {prtools-2.0.0 → prtools-2.0.2}/prtools/segmented.py +0 -0
- {prtools-2.0.0 → prtools-2.0.2}/prtools/sparse.py +0 -0
- {prtools-2.0.0 → prtools-2.0.2}/prtools/stats.py +0 -0
- {prtools-2.0.0 → prtools-2.0.2}/prtools.egg-info/SOURCES.txt +0 -0
- {prtools-2.0.0 → prtools-2.0.2}/prtools.egg-info/dependency_links.txt +0 -0
- {prtools-2.0.0 → prtools-2.0.2}/prtools.egg-info/requires.txt +0 -0
- {prtools-2.0.0 → prtools-2.0.2}/prtools.egg-info/top_level.txt +0 -0
- {prtools-2.0.0 → prtools-2.0.2}/pyproject.toml +0 -0
- {prtools-2.0.0 → prtools-2.0.2}/setup.cfg +0 -0
- {prtools-2.0.0 → prtools-2.0.2}/tests/test_convolve.py +0 -0
- {prtools-2.0.0 → prtools-2.0.2}/tests/test_fourier.py +0 -0
- {prtools-2.0.0 → prtools-2.0.2}/tests/test_jax.py +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
__version__ = '2.0.
|
|
1
|
+
__version__ = '2.0.2'
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
from prtools._array_api import use
|
|
@@ -8,6 +8,7 @@ from prtools.array import (
|
|
|
8
8
|
pad,
|
|
9
9
|
crop,
|
|
10
10
|
boundary,
|
|
11
|
+
sample,
|
|
11
12
|
rebin,
|
|
12
13
|
rescale,
|
|
13
14
|
normpow,
|
|
@@ -55,6 +56,7 @@ from prtools.shape import (
|
|
|
55
56
|
circle,
|
|
56
57
|
hexagon,
|
|
57
58
|
rectangle,
|
|
59
|
+
ellipse,
|
|
58
60
|
spider,
|
|
59
61
|
sin,
|
|
60
62
|
waffle
|
|
@@ -326,6 +326,82 @@ def boundary(x, threshold=0):
|
|
|
326
326
|
return rmin, rmax, cmin, cmax
|
|
327
327
|
|
|
328
328
|
|
|
329
|
+
def sample(x, oversample, offset=0, axes=(-2, -1), indexing='ij'):
|
|
330
|
+
"""Extract samples from an oversampled array.
|
|
331
|
+
|
|
332
|
+
The sampled axes are tiled into contiguous blocks of ``oversample``
|
|
333
|
+
samples, with one sample being taken from each block. No averaging or
|
|
334
|
+
filtering is performed.
|
|
335
|
+
|
|
336
|
+
Parameters
|
|
337
|
+
----------
|
|
338
|
+
x : array_like
|
|
339
|
+
Array to sample
|
|
340
|
+
oversample : int or tuple of ints
|
|
341
|
+
Decimation factor. Providing two values defines asymmetric
|
|
342
|
+
oversampling with ordering interpreted according to ``indexing``.
|
|
343
|
+
offset : int or tuple of ints, optional
|
|
344
|
+
Position of the sample with each block, measured from
|
|
345
|
+
``oversample//2``. The sub-sampled position reachable by ``offset`` is
|
|
346
|
+
quantized to ``1/oversample``. Default is 0. If two values are
|
|
347
|
+
provided, their ordering is interpreted according to ``indexing``.
|
|
348
|
+
axes : (2,) sequence of ints, optional
|
|
349
|
+
Axes over which to sample. If not provided, the last two axes are
|
|
350
|
+
used.
|
|
351
|
+
indexing : {'ij', 'xy'}, optional
|
|
352
|
+
Matrix (‘ij’, default) or cartesian (‘xy’) indexing of ``x`` grid.
|
|
353
|
+
|
|
354
|
+
Returns
|
|
355
|
+
-------
|
|
356
|
+
x : ndarray
|
|
357
|
+
Each sampled axis has length ``x.shape[axis]//oversample``. If axis
|
|
358
|
+
length is not a multiple of ``oversample``, samples taken beyond the
|
|
359
|
+
last whole block are ignored.
|
|
360
|
+
|
|
361
|
+
Examples
|
|
362
|
+
--------
|
|
363
|
+
.. code-block:: python
|
|
364
|
+
|
|
365
|
+
>>> psf = prtools.calcpsf(amp, opd, wave, sampling, (32, 32),
|
|
366
|
+
... oversample=8)
|
|
367
|
+
>>> k = prtools.pixel_kernel(psf.shape, oversample=8)
|
|
368
|
+
>>> native = prtools.sample(prtools.fftconv(psf, k), 8)
|
|
369
|
+
>>> native.shape
|
|
370
|
+
(32, 32)
|
|
371
|
+
|
|
372
|
+
"""
|
|
373
|
+
xp = array_namespace(x)
|
|
374
|
+
x = xp.asarray(x)
|
|
375
|
+
|
|
376
|
+
if indexing not in ('ij', 'xy'):
|
|
377
|
+
raise ValueError("Valid values for indexing are 'xy' and 'ij'.")
|
|
378
|
+
|
|
379
|
+
oversample = tuple(int(v) for v in np.broadcast_to(oversample, (2,)))
|
|
380
|
+
offset = tuple(int(v) for v in np.broadcast_to(offset, (2,)))
|
|
381
|
+
axes = tuple(int(a) % x.ndim for a in axes)
|
|
382
|
+
|
|
383
|
+
if indexing == 'xy':
|
|
384
|
+
offset = (-offset[1], offset[0])
|
|
385
|
+
oversample = (oversample[1], oversample[0])
|
|
386
|
+
|
|
387
|
+
idx = [slice(None)] * x.ndim
|
|
388
|
+
|
|
389
|
+
for ax, m, o in zip(axes, oversample, offset):
|
|
390
|
+
|
|
391
|
+
lo, hi = -(m // 2), m - 1 - m // 2
|
|
392
|
+
if not lo <= o <= hi:
|
|
393
|
+
raise ValueError(f'offset {o} lies outside one pixel for oversample '
|
|
394
|
+
f'{m} on axis {ax}; require {lo} <= offset <= {hi}')
|
|
395
|
+
|
|
396
|
+
n = x.shape[ax] // m
|
|
397
|
+
start = m // 2 + o
|
|
398
|
+
stop = start + m * n
|
|
399
|
+
|
|
400
|
+
idx[ax] = slice(start, stop, m)
|
|
401
|
+
|
|
402
|
+
return x[tuple(idx)]
|
|
403
|
+
|
|
404
|
+
|
|
329
405
|
def rebin(x, factor):
|
|
330
406
|
"""Rebin an array by an integer factor.
|
|
331
407
|
|
|
@@ -7,7 +7,7 @@ def fftconv(array, kernel, axes=(-2, -1), normalize_kernel=True,
|
|
|
7
7
|
fft_array=True, fft_kernel=False, fftshift_kernel=False):
|
|
8
8
|
r"""Convolve an array with a kernel using the FFT.
|
|
9
9
|
|
|
10
|
-
The
|
|
10
|
+
The convolution is computed as
|
|
11
11
|
|
|
12
12
|
.. math::
|
|
13
13
|
|
|
@@ -18,12 +18,13 @@ def fftconv(array, kernel, axes=(-2, -1), normalize_kernel=True,
|
|
|
18
18
|
array : array_like
|
|
19
19
|
Array to be convolved with ``kernel``.
|
|
20
20
|
kernel : array_like
|
|
21
|
-
Convolution kernel. Should have the same shape as ``array``.
|
|
21
|
+
Convolution kernel. Should have the same shape as ``array``. May be
|
|
22
|
+
given in either the frequency or spatial domain, as indicated by
|
|
23
|
+
``fft_kernel``. The kernel is applied exactly as supplied. See Notes
|
|
24
|
+
for normalization details.
|
|
22
25
|
axes : sequence of ints, optional
|
|
23
26
|
Axes over which to apply the convolution. If not given, the last two
|
|
24
27
|
axes are used.
|
|
25
|
-
normalize_kernel : bool, optional
|
|
26
|
-
If True (default), kernel is normalized so that ``sum(kernel) == 1``.
|
|
27
28
|
fft_array : bool, optional
|
|
28
29
|
If True (default), the array is assumed to be provided in the spatial
|
|
29
30
|
domain and its FFT will be computed by this function. If False, the
|
|
@@ -46,6 +47,24 @@ def fftconv(array, kernel, axes=(-2, -1), normalize_kernel=True,
|
|
|
46
47
|
--------
|
|
47
48
|
:func:`~prtools.gauss_blur`
|
|
48
49
|
:func:`~prtools.pixelate`
|
|
50
|
+
|
|
51
|
+
Notes
|
|
52
|
+
-----
|
|
53
|
+
**Kernel normalization** This function does not normalize or scale the
|
|
54
|
+
supplied kernel in any way.
|
|
55
|
+
|
|
56
|
+
The sum of ``array`` is conserved when the kernel's transfer function is
|
|
57
|
+
unity at zero frequency. Depending on which domain the kernel is supplied
|
|
58
|
+
in, the kernel normaliztion approach is different:
|
|
59
|
+
|
|
60
|
+
* A frequency-domain kernel (``fft_kernel=False``) should be scaled such
|
|
61
|
+
that ``kernel[0,0] == 1``. Note that ``kernel[0,0]`` is the
|
|
62
|
+
zero-frequency term only for a kernel in standard (unshifted) FFT layout.
|
|
63
|
+
If the kernel origin lies at the center of the array
|
|
64
|
+
(``fftshift_kernel=True``), the DC term is at ``kernel[nr//2, nc//2]``.
|
|
65
|
+
|
|
66
|
+
* A spatial-domain kernal(``fft_kernel=True``) should have
|
|
67
|
+
``sum(kernel) == 1``.
|
|
49
68
|
"""
|
|
50
69
|
xp = array_namespace(array)
|
|
51
70
|
|
|
@@ -53,9 +72,6 @@ def fftconv(array, kernel, axes=(-2, -1), normalize_kernel=True,
|
|
|
53
72
|
k = xp.fft.fftshift(kernel) if fftshift_kernel else kernel
|
|
54
73
|
k = xp.fft.fft2(k) if fft_kernel else k
|
|
55
74
|
|
|
56
|
-
if normalize_kernel:
|
|
57
|
-
k = k / xp.sum(k)
|
|
58
|
-
|
|
59
75
|
return xp.fft.ifft2(a*k, axes=axes).real
|
|
60
76
|
|
|
61
77
|
|
|
@@ -312,10 +328,10 @@ def gauss_kernel(shape, sigma, oversample=1, pixelscale=1.0, fftshift=False,
|
|
|
312
328
|
return k
|
|
313
329
|
|
|
314
330
|
|
|
315
|
-
def pixel_kernel(shape, oversample=1, pixelscale=1.0, fftshift=False,
|
|
331
|
+
def pixel_kernel(shape, oversample=1, pixelscale=1.0, fftshift=False,
|
|
316
332
|
xp=None):
|
|
317
333
|
r"""2D pixel MTF filter kernel
|
|
318
|
-
|
|
334
|
+
|
|
319
335
|
This function returns a normalized 2D sinc function sized to represent
|
|
320
336
|
the transfer function of an idealized square pixel.
|
|
321
337
|
|
|
@@ -361,7 +377,7 @@ def pixel_kernel(shape, oversample=1, pixelscale=1.0, fftshift=False,
|
|
|
361
377
|
x1 = xp.fft.fftfreq(shape[0], d=pixelscale[0]) * oversample
|
|
362
378
|
x2 = xp.fft.fftfreq(shape[1], d=pixelscale[1]) * oversample
|
|
363
379
|
|
|
364
|
-
k =
|
|
380
|
+
k = sinc(x1, x2, indexing='ij', xp=xp)
|
|
365
381
|
|
|
366
382
|
if fftshift:
|
|
367
383
|
return xp.fft.fftshift(k)
|
|
@@ -219,6 +219,68 @@ def rectangle(shape, width, height, shift=(0, 0), angle=0, antialias=True,
|
|
|
219
219
|
return rect
|
|
220
220
|
|
|
221
221
|
|
|
222
|
+
def ellipse(shape, xradius, yradius, shift=(0,0), angle=0, antialias=True,
|
|
223
|
+
indexing='ij'):
|
|
224
|
+
"""Draw an ellipse
|
|
225
|
+
|
|
226
|
+
Parameters
|
|
227
|
+
----------
|
|
228
|
+
shape : array_like
|
|
229
|
+
Size of output in pixels (nrows, ncols)
|
|
230
|
+
xradius : float
|
|
231
|
+
Semi-axis of the ellipse in pixels in the x (horizontal) direction.
|
|
232
|
+
yradius : float
|
|
233
|
+
Semi-axis of the ellipse in pixels in the y (vertical) direction.
|
|
234
|
+
shift : (2,) array_like, optional
|
|
235
|
+
How far to shift center in float (rows, cols). Default is (0, 0).
|
|
236
|
+
angle : float, optional
|
|
237
|
+
Rotation of the ellipse in degrees counterclockwise from horizontal.
|
|
238
|
+
Default is 0.
|
|
239
|
+
antialias : bool, optional
|
|
240
|
+
If True (default), the shape edges are antialiased.
|
|
241
|
+
indexing : {'ij', 'xy'}, optional
|
|
242
|
+
Matrix ('ij', default) or cartesian ('xy') indexing of output.
|
|
243
|
+
|
|
244
|
+
Returns
|
|
245
|
+
-------
|
|
246
|
+
ndarray
|
|
247
|
+
|
|
248
|
+
Examples
|
|
249
|
+
--------
|
|
250
|
+
.. plot::
|
|
251
|
+
:include-source:
|
|
252
|
+
:context: reset
|
|
253
|
+
:scale: 50
|
|
254
|
+
|
|
255
|
+
>>> ell = prtools.ellipse(shape=(256, 256), xradius=100, yradius=50)
|
|
256
|
+
>>> plt.imshow(ell, cmap='gray')
|
|
257
|
+
|
|
258
|
+
.. plot::
|
|
259
|
+
:include-source:
|
|
260
|
+
:context: reset
|
|
261
|
+
:scale: 50
|
|
262
|
+
|
|
263
|
+
>>> ell = prtools.ellipse(shape=(256, 256), xradius=100, yradius=50, angle=30)
|
|
264
|
+
>>> plt.imshow(ell, cmap='gray')
|
|
265
|
+
|
|
266
|
+
"""
|
|
267
|
+
a, b = yradius, xradius
|
|
268
|
+
rr, cc = mesh(shape, shift, angle, indexing=indexing)
|
|
269
|
+
|
|
270
|
+
rho = np.sqrt((rr / a)**2 + (cc / b)**2)
|
|
271
|
+
grad = np.sqrt((rr / a**2)**2 + (cc / b**2)**2)
|
|
272
|
+
|
|
273
|
+
with np.errstate(divide='ignore', invalid='ignore'):
|
|
274
|
+
dist = (1 - rho) * rho / grad
|
|
275
|
+
dist = np.where(grad == 0, 1.0, dist) # center is always interior
|
|
276
|
+
ell = np.clip(dist + 0.5, 0.0, 1.0)
|
|
277
|
+
|
|
278
|
+
if not antialias:
|
|
279
|
+
ell[ell > 0] = 1
|
|
280
|
+
|
|
281
|
+
return ell
|
|
282
|
+
|
|
283
|
+
|
|
222
284
|
def spider(shape, width, angle=0, shift=(0, 0), antialias=True, indexing='ij'):
|
|
223
285
|
"""Draw a spider
|
|
224
286
|
|
|
@@ -261,10 +323,6 @@ def spider(shape, width, angle=0, shift=(0, 0), antialias=True, indexing='ij'):
|
|
|
261
323
|
return 1 - rectangle(shape, len, width, shift, angle, antialias, indexing)
|
|
262
324
|
|
|
263
325
|
|
|
264
|
-
def ellipse():
|
|
265
|
-
pass
|
|
266
|
-
|
|
267
|
-
|
|
268
326
|
def gauss(shape, sigma, shift=(0, 0), indexing='ij'):
|
|
269
327
|
"""Generate a 2D Gaussian function.
|
|
270
328
|
|
|
@@ -14,7 +14,7 @@ def zernike(mask, index, normalize=True, order='noll', rho=None, theta=None):
|
|
|
14
14
|
Binary mask defining the extent to compute the Zernike polynomial
|
|
15
15
|
over.
|
|
16
16
|
index : int
|
|
17
|
-
|
|
17
|
+
Zernike mode index
|
|
18
18
|
normalize : bool, optional
|
|
19
19
|
If True (default), the output is normalized according to [1]. If False,
|
|
20
20
|
the output value ranges [-1, 1] over the mask.
|
|
@@ -112,8 +112,8 @@ def zernike_compose(mask, coeffs, normalize=True, order='noll', rho=None, theta=
|
|
|
112
112
|
mask : array_like
|
|
113
113
|
Binary mask defining the extent to compute the Zernike polynomial over.
|
|
114
114
|
coeffs : array_like
|
|
115
|
-
List of coefficients corresponding to Zernike indices
|
|
116
|
-
|
|
115
|
+
List of coefficients corresponding to Zernike indices used to create
|
|
116
|
+
the OPD.
|
|
117
117
|
normalize : bool, optional
|
|
118
118
|
If True (default), the output is normalized according to [1]. If False,
|
|
119
119
|
the output value ranges [-1, 1] over the mask.
|
|
@@ -177,7 +177,7 @@ def zernike_basis(mask, modes, vectorize=False, normalize=True, order='noll',
|
|
|
177
177
|
mask : array_like
|
|
178
178
|
Binary mask defining the extent to compute the Zernike polynomial over.
|
|
179
179
|
modes : array_like
|
|
180
|
-
List of modes
|
|
180
|
+
List of modes to return.
|
|
181
181
|
vectorize : bool, optional
|
|
182
182
|
If True, the output is returned as a
|
|
183
183
|
``(length(modes), modes.shape[0]*modes.shape[1])`` array If False
|
|
@@ -228,11 +228,11 @@ def zernike_fit(opd, mask, modes, normalize=True, order='noll', rho=None,
|
|
|
228
228
|
Parameters
|
|
229
229
|
----------
|
|
230
230
|
opd : array_like
|
|
231
|
-
OPD to fit.
|
|
231
|
+
OPD to fit. NaN values in ``opd`` are excluded from the fit.
|
|
232
232
|
mask : array_like
|
|
233
233
|
Binary mask defining the extent to compute the Zernike basis over.
|
|
234
234
|
modes : array_like
|
|
235
|
-
List of modes
|
|
235
|
+
List of modes to fit.
|
|
236
236
|
normalize : bool, optional
|
|
237
237
|
If True (default), the output is normalized according to [1]. If False,
|
|
238
238
|
the output value ranges [-1, 1] over the mask.
|
|
@@ -271,14 +271,16 @@ def zernike_fit(opd, mask, modes, normalize=True, order='noll', rho=None,
|
|
|
271
271
|
[1] Noll, RJ. Zernike polynomials and atmospheric turbulence. J Opt Soc Am 66, 207-211 (1976).
|
|
272
272
|
|
|
273
273
|
"""
|
|
274
|
-
opd = np.asarray(opd)
|
|
274
|
+
opd = np.asarray(opd).ravel()
|
|
275
275
|
mask = np.asarray(mask)
|
|
276
|
-
|
|
277
276
|
basis = zernike_basis(mask, modes, True, normalize, order, rho, theta)
|
|
277
|
+
|
|
278
|
+
# valid data includes all non-NaN values inside the mask
|
|
279
|
+
valid = np.isfinite(opd) & mask.ravel().astype(bool)
|
|
280
|
+
|
|
281
|
+
coeff, *_ = np.linalg.lstsq(basis[:, valid].T, opd[valid], rcond=None)
|
|
278
282
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
return np.einsum('ij,i->j', basis, opd.ravel())
|
|
283
|
+
return coeff
|
|
282
284
|
|
|
283
285
|
|
|
284
286
|
def zernike_remove(opd, mask, modes, order='noll', rho=None, theta=None):
|
|
@@ -291,7 +293,7 @@ def zernike_remove(opd, mask, modes, order='noll', rho=None, theta=None):
|
|
|
291
293
|
mask : array_like
|
|
292
294
|
Binary mask defining the extent to compute the Zernike basis over.
|
|
293
295
|
modes : array_like
|
|
294
|
-
List of modes
|
|
296
|
+
List of modes to remove.
|
|
295
297
|
order : {'noll', 'fringe', 'ansi'}, optional
|
|
296
298
|
Zernike ordering scheme. Default is 'noll'.
|
|
297
299
|
rho : array_like, optional
|
|
@@ -109,4 +109,45 @@ def test_register(xp):
|
|
|
109
109
|
|
|
110
110
|
s = prtools.register(x2, x1, oversample=2)
|
|
111
111
|
|
|
112
|
-
assert
|
|
112
|
+
assert xp.allclose(xp.asarray(s), xp.asarray((-1, -1)))
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@pytest.mark.parametrize('xp', BACKENDS)
|
|
116
|
+
@pytest.mark.parametrize('shape, oversample, offset, result', [
|
|
117
|
+
((2, 2), 1, 0, [[0, 1], [2, 3]]),
|
|
118
|
+
((2, 2), 2, 0, [3]),
|
|
119
|
+
((2, 2), 2, (-1, 0), [1]),
|
|
120
|
+
((2, 2), 2, (0, -1), [2]),
|
|
121
|
+
((2, 2), 2, (-1, -1), [0]),
|
|
122
|
+
((3, 3), 1, 0, [[0, 1, 2], [3, 4, 5], [6, 7, 8]]),
|
|
123
|
+
((3, 3), 3, 0, [4]),
|
|
124
|
+
((3, 3), 3, (-1, 0), [1]),
|
|
125
|
+
((3, 3), 3, (1, 0), [7]),
|
|
126
|
+
((3, 3), 3, (0, -1), [3]),
|
|
127
|
+
((3, 3), 3, (0, 1), [5]),
|
|
128
|
+
((3, 3), 3, (-1, -1), [0]),
|
|
129
|
+
((3, 3), 3, (-1, 1), [2]),
|
|
130
|
+
((3, 3), 3, (1, -1), [6]),
|
|
131
|
+
((3, 3), 3, (1, 1), [8]),
|
|
132
|
+
((4, 4), 1, 0, [[0, 1, 2, 3], [4, 5, 6, 7],
|
|
133
|
+
[8, 9, 10, 11], [12, 13, 14, 15]]),
|
|
134
|
+
((4, 4), 2, 0, [[5, 7], [13, 15]]),
|
|
135
|
+
])
|
|
136
|
+
def test_sample(xp, shape, oversample, offset, result):
|
|
137
|
+
x = np.arange(np.prod(shape)).reshape(shape)
|
|
138
|
+
assert np.all(prtools.sample(x, oversample, offset) == result)
|
|
139
|
+
|
|
140
|
+
@pytest.mark.parametrize('xp', BACKENDS)
|
|
141
|
+
@pytest.mark.parametrize('shape, oversample, offset', [
|
|
142
|
+
((2, 2), 1, 1),
|
|
143
|
+
((2, 2), 1, -2),
|
|
144
|
+
((2, 2), 2, 1),
|
|
145
|
+
((3, 3), 3, -2),
|
|
146
|
+
((3, 3), 3, 2),
|
|
147
|
+
((4, 4), 2, 1),
|
|
148
|
+
((4, 4), 2, -2)
|
|
149
|
+
])
|
|
150
|
+
def test_sample_value_error(xp, shape, oversample, offset):
|
|
151
|
+
x = np.arange(np.prod(shape)).reshape(shape)
|
|
152
|
+
with pytest.raises(ValueError):
|
|
153
|
+
prtools.sample(x, oversample, offset)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|