Skip to content

Commit f0671b5

Browse files
authored
Merge pull request #576 from janden/new_fb2d_tests
New logical (F)FBBasis2D tests
2 parents 11e9fc5 + eb92614 commit f0671b5

File tree

3 files changed

+267
-541
lines changed

3 files changed

+267
-541
lines changed

tests/_basis_util.py

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
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(
43+
self.L, x0=x0, y0=y0, sigma_x=sigma, sigma_y=sigma, dtype=self.dtype
44+
)
45+
46+
coef = self.basis.expand(im1)
47+
im2 = self.basis.evaluate(coef)
48+
49+
if isinstance(im2, Image):
50+
im2 = im2.asnumpy()
51+
im2 = im2[0]
52+
53+
# For small L there's too much clipping at high freqs to get 1e-3
54+
# accuracy.
55+
if self.L < 32:
56+
atol = 1e-2
57+
else:
58+
atol = 1e-3
59+
60+
self.assertTrue(im1.shape == im2.shape)
61+
self.assertTrue(np.allclose(im1, im2, atol=atol))
62+
63+
def testIsotropic(self):
64+
sigma = self.L / 8
65+
im = gaussian_2d(self.L, sigma_x=sigma, sigma_y=sigma, dtype=self.dtype)
66+
67+
coef = self.basis.expand(im)
68+
69+
ells = self.basis.indices()["ells"]
70+
71+
energy_outside = np.sum(np.abs(coef[ells != 0]) ** 2)
72+
energy_total = np.sum(np.abs(coef) ** 2)
73+
74+
energy_ratio = energy_outside / energy_total
75+
76+
self.assertTrue(energy_ratio < 0.01)
77+
78+
def testModulated(self):
79+
if self.L < 32:
80+
raise SkipTest
81+
82+
ell = 1
83+
84+
sigma = self.L / 8
85+
im = gaussian_2d(self.L, sigma_x=sigma, sigma_y=sigma, dtype=self.dtype)
86+
87+
g2d = grid_2d(self.L)
88+
89+
for trig_fun in (np.sin, np.cos):
90+
im1 = im * trig_fun(ell * g2d["phi"])
91+
92+
coef = self.basis.expand(im1)
93+
94+
ells = self.basis.indices()["ells"]
95+
96+
energy_outside = np.sum(np.abs(coef[ells != ell]) ** 2)
97+
energy_total = np.sum(np.abs(coef) ** 2)
98+
99+
energy_ratio = energy_outside / energy_total
100+
101+
self.assertTrue(energy_ratio < 0.10)
102+
103+
def testEvaluateExpand(self):
104+
coef1 = randn(self.basis.count, seed=self.seed)
105+
coef1 = coef1.astype(self.dtype)
106+
107+
im = self.basis.evaluate(coef1)
108+
if isinstance(im, Image):
109+
im = im.asnumpy()
110+
coef2 = self.basis.expand(im)[0]
111+
112+
self.assertTrue(coef1.shape == coef2.shape)
113+
self.assertTrue(np.allclose(coef1, coef2, atol=utest_tolerance(self.dtype)))
114+
115+
def testAdjoint(self):
116+
u = randn(self.basis.count, seed=self.seed)
117+
u = u.astype(self.dtype)
118+
119+
Au = self.basis.evaluate(u)
120+
if isinstance(Au, Image):
121+
Au = Au.asnumpy()
122+
123+
x = randn(*self.basis.sz, seed=self.seed)
124+
x = x.astype(self.dtype)
125+
126+
ATx = self.basis.evaluate_t(x)
127+
128+
Au_dot_x = np.sum(Au * x)
129+
u_dot_ATx = np.sum(u * ATx)
130+
131+
self.assertTrue(Au_dot_x.shape == u_dot_ATx.shape)
132+
self.assertTrue(np.isclose(Au_dot_x, u_dot_ATx))

0 commit comments

Comments
 (0)