Skip to content

Commit abda13a

Browse files
committed
Move tests not specific to FB into a mixin
1 parent 32ae0ed commit abda13a

File tree

2 files changed

+133
-123
lines changed

2 files changed

+133
-123
lines changed

tests/_basis_util.py

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
from unittest.case import SkipTest
2+
3+
import numpy as np
4+
5+
from aspire.image import Image
6+
from aspire.utils import gaussian_2d, utest_tolerance
7+
from aspire.utils.coor_trans import grid_2d
8+
from aspire.utils.random import randn
9+
10+
11+
class Steerable2DMixin:
12+
def testIndices(self):
13+
ell_max = self.basis.ell_max
14+
k_max = self.basis.k_max
15+
16+
indices = self.basis.indices()
17+
18+
i = 0
19+
20+
for ell in range(ell_max + 1):
21+
if ell == 0:
22+
sgns = [1]
23+
else:
24+
sgns = [1, -1]
25+
26+
for sgn in sgns:
27+
for k in range(k_max[ell]):
28+
self.assertTrue(indices["ells"][i] == ell)
29+
self.assertTrue(indices["sgns"][i] == sgn)
30+
self.assertTrue(indices["ks"][i] == k)
31+
32+
i += 1
33+
34+
def testGaussianExpand(self):
35+
# Offset slightly
36+
x0 = 0.50
37+
y0 = 0.75
38+
39+
# Want sigma to be as large as possible without the Gaussian
40+
# spilling too much outside the central disk.
41+
sigma = self.L / 8
42+
im1 = gaussian_2d(self.L, x0=x0, y0=y0, sigma_x=sigma, sigma_y=sigma)
43+
im1 = im1.astype(self.dtype)
44+
45+
coef = self.basis.expand(im1)
46+
im2 = self.basis.evaluate(coef)
47+
48+
if isinstance(im2, Image):
49+
im2 = im2.asnumpy()
50+
51+
# For small L there's too much clipping at high freqs to get 1e-3
52+
# accuracy.
53+
if self.L < 32:
54+
atol = 1e-2
55+
else:
56+
atol = 1e-3
57+
58+
self.assertTrue(np.allclose(im1, im2, atol=atol))
59+
60+
def testIsotropic(self):
61+
sigma = self.L / 8
62+
im = gaussian_2d(self.L, sigma_x=sigma, sigma_y=sigma)
63+
im = im.astype(self.dtype)
64+
65+
coef = self.basis.expand(im)
66+
67+
ells = self.basis.indices()["ells"]
68+
69+
energy_outside = np.sum(np.abs(coef[ells != 0]) ** 2)
70+
energy_total = np.sum(np.abs(coef) ** 2)
71+
72+
energy_ratio = energy_outside / energy_total
73+
74+
self.assertTrue(energy_ratio < 0.01)
75+
76+
def testModulated(self):
77+
if self.L < 32:
78+
raise SkipTest
79+
80+
ell = 1
81+
82+
sigma = self.L / 8
83+
im = gaussian_2d(self.L, sigma_x=sigma, sigma_y=sigma)
84+
im = im.astype(self.dtype)
85+
86+
g2d = grid_2d(self.L)
87+
88+
for trig_fun in (np.sin, np.cos):
89+
im1 = im * trig_fun(ell * g2d["phi"])
90+
91+
coef = self.basis.expand(im1)
92+
93+
ells = self.basis.indices()["ells"]
94+
95+
energy_outside = np.sum(np.abs(coef[ells != ell]) ** 2)
96+
energy_total = np.sum(np.abs(coef) ** 2)
97+
98+
energy_ratio = energy_outside / energy_total
99+
100+
self.assertTrue(energy_ratio < 0.10)
101+
102+
def testEvaluateExpand(self):
103+
coef1 = randn(self.basis.count, seed=self.seed)
104+
coef1 = coef1.astype(self.dtype)
105+
106+
im = self.basis.evaluate(coef1)
107+
if isinstance(im, Image):
108+
im = im.asnumpy()
109+
coef2 = self.basis.expand(im)[:, 0]
110+
111+
self.assertTrue(np.allclose(coef1, coef2, atol=utest_tolerance(self.dtype)))
112+
113+
def testAdjoint(self):
114+
u = randn(self.basis.count, seed=self.seed)
115+
u = u.astype(self.dtype)
116+
117+
Au = self.basis.evaluate(u)
118+
if isinstance(Au, Image):
119+
Au = Au.asnumpy()
120+
121+
x = randn(*self.basis.sz, seed=self.seed)
122+
x = x.astype(self.dtype)
123+
124+
ATx = self.basis.evaluate_t(x)
125+
126+
Au_dot_x = np.sum(Au * x)
127+
u_dot_ATx = np.sum(u * ATx)
128+
129+
self.assertTrue(np.isclose(Au_dot_x, u_dot_ATx))

tests/test_FBbasis2D.py

Lines changed: 4 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
import os.path
22
from unittest import TestCase
3-
from unittest.case import SkipTest
43

54
import numpy as np
65
from pytest import raises
76
from scipy.special import jv
87

98
from aspire.basis import FBBasis2D
109
from aspire.image import Image
11-
from aspire.utils import complex_type, gaussian_2d, real_type, utest_tolerance
10+
from aspire.utils import complex_type, real_type, utest_tolerance
1211
from aspire.utils.coor_trans import grid_2d
13-
from aspire.utils.random import randn
12+
13+
from ._basis_util import Steerable2DMixin
1414

1515
DATA_DIR = os.path.join(os.path.dirname(__file__), "saved_test_data")
1616

1717

18-
class FBBasis2DTestCase(TestCase):
18+
class FBBasis2DTestCase(TestCase, Steerable2DMixin):
1919
def setUp(self):
2020
self.dtype = np.float32
2121
self.L = 8
@@ -329,28 +329,6 @@ def testFBBasis2DExpand(self):
329329
)
330330
)
331331

332-
def testIndices(self):
333-
ell_max = self.basis.ell_max
334-
k_max = self.basis.k_max
335-
336-
indices = self.basis.indices()
337-
338-
i = 0
339-
340-
for ell in range(ell_max + 1):
341-
if ell == 0:
342-
sgns = [1]
343-
else:
344-
sgns = [1, -1]
345-
346-
for sgn in sgns:
347-
for k in range(k_max[ell]):
348-
self.assertTrue(indices["ells"][i] == ell)
349-
self.assertTrue(indices["sgns"][i] == sgn)
350-
self.assertTrue(indices["ks"][i] == k)
351-
352-
i += 1
353-
354332
def testElement(self):
355333
ell = 1
356334
sgn = -1
@@ -387,103 +365,6 @@ def testElement(self):
387365
self.assertTrue(np.allclose(im, im_ref, atol=1e-4))
388366
self.assertTrue(np.allclose(coef, coef_ref, atol=1e-4))
389367

390-
def testGaussianExpand(self):
391-
# Offset slightly
392-
x0 = 0.50
393-
y0 = 0.75
394-
395-
# Want sigma to be as large as possible without the Gaussian
396-
# spilling too much outside the central disk.
397-
sigma = self.L / 8
398-
im1 = gaussian_2d(self.L, x0=x0, y0=y0, sigma_x=sigma, sigma_y=sigma)
399-
im1 = im1.astype(self.dtype)
400-
401-
coef = self.basis.expand(im1)
402-
im2 = self.basis.evaluate(coef)
403-
404-
if isinstance(im2, Image):
405-
im2 = im2.asnumpy()
406-
407-
# For small L there's too much clipping at high freqs to get 1e-3
408-
# accuracy.
409-
if self.L < 32:
410-
atol = 1e-2
411-
else:
412-
atol = 1e-3
413-
414-
self.assertTrue(np.allclose(im1, im2, atol=atol))
415-
416-
def testIsotropic(self):
417-
sigma = self.L / 8
418-
im = gaussian_2d(self.L, sigma_x=sigma, sigma_y=sigma)
419-
im = im.astype(self.dtype)
420-
421-
coef = self.basis.expand(im)
422-
423-
ells = self.basis.indices()["ells"]
424-
425-
energy_outside = np.sum(np.abs(coef[ells != 0]) ** 2)
426-
energy_total = np.sum(np.abs(coef) ** 2)
427-
428-
energy_ratio = energy_outside / energy_total
429-
430-
self.assertTrue(energy_ratio < 0.01)
431-
432-
def testModulated(self):
433-
if self.L < 32:
434-
raise SkipTest
435-
436-
ell = 1
437-
438-
sigma = self.L / 8
439-
im = gaussian_2d(self.L, sigma_x=sigma, sigma_y=sigma)
440-
im = im.astype(self.dtype)
441-
442-
g2d = grid_2d(self.L)
443-
444-
for trig_fun in (np.sin, np.cos):
445-
im1 = im * trig_fun(ell * g2d["phi"])
446-
447-
coef = self.basis.expand(im1)
448-
449-
ells = self.basis.indices()["ells"]
450-
451-
energy_outside = np.sum(np.abs(coef[ells != ell]) ** 2)
452-
energy_total = np.sum(np.abs(coef) ** 2)
453-
454-
energy_ratio = energy_outside / energy_total
455-
456-
self.assertTrue(energy_ratio < 0.10)
457-
458-
def testEvaluateExpand(self):
459-
coef1 = randn(self.basis.count, seed=self.seed)
460-
coef1 = coef1.astype(self.dtype)
461-
462-
im = self.basis.evaluate(coef1)
463-
if isinstance(im, Image):
464-
im = im.asnumpy()
465-
coef2 = self.basis.expand(im)[:, 0]
466-
467-
self.assertTrue(np.allclose(coef1, coef2, atol=utest_tolerance(self.dtype)))
468-
469-
def testAdjoint(self):
470-
u = randn(self.basis.count, seed=self.seed)
471-
u = u.astype(self.dtype)
472-
473-
Au = self.basis.evaluate(u)
474-
if isinstance(Au, Image):
475-
Au = Au.asnumpy()
476-
477-
x = randn(*self.basis.sz, seed=self.seed)
478-
x = x.astype(self.dtype)
479-
480-
ATx = self.basis.evaluate_t(x)
481-
482-
Au_dot_x = np.sum(Au * x)
483-
u_dot_ATx = np.sum(u * ATx)
484-
485-
self.assertTrue(np.isclose(Au_dot_x, u_dot_ATx))
486-
487368
def testComplexCoversion(self):
488369
# Load a reasonable input
489370
x = np.load(os.path.join(DATA_DIR, "fbbasis_coefficients_8_8.npy"))

0 commit comments

Comments
 (0)