Skip to content

FBBasis2D and FLEBasis2D correspondence #738

@chris-langfield

Description

@chris-langfield

Preserving work done on this problem for posterity

FLEBasis2D will be a new basis class introduced in #693, ported from https://github.com/nmarshallf/fle_2d.

Differences between this basis and the existing FBBasis2D mean that either

  • There are fundamental differences in both selection of basis functions and calculation of coefficients or,
  • The differences in the implementation between the two codes are such that comparison is very difficult

Initial basis count heuristic

FBBasis2D sets the count in advance using the following formula:

self.count = self.k_max[0] + sum(2 * self.k_max[1:])

k_max[i] is the number of Bessel zeros (q's in the paper) included for Bessel order i. So this is equivalent to the DC component plus all k,q combinations times 2 (positive and negative).

FLEBasis2D uses

self.count = int(self.nres**2 * np.pi / 4)

which is based on the geometry of the problem (pi * R**2: the area of the disc inscribed in the image box)

Basis functions used

FLEBasis2D can be forced to use the FBBasis2D heuristic to determine whether the same subset of basis functions are chosen.

The two classes store the 3 parameters determining the basis function (ell, k, and sign) differently in FBBasis2D._indices vs FLEBasis2D.ells/.ks but compare in the following way:

L = 32
fle = FLEBasis2D(L, match_fb=True, dtype=np.float64)
fb = FBBasis2D(L, dtype=np.float64)

fb_fns = []
for idx, ell in enumerate(fb._indices["ells"]):
    fb_fns.append((ell, fb._indices["ks"][idx], fb._indices["sgns"][idx]))

def sign(x):
    if ell==0:
        return 1
    else:
        return np.sign(x)

fle_fns = []
for idx, ell in enumerate(fle.ells):
    fle_fns.append((abs(ell), fle.ks[idx]-1, sign(ell)))

The code above reveals two things to note:

  • FBBasis2D records the sign of the DC component as 1, while FLE has 0
  • All ks for FLE are one ahead of those for FB

When these are corrected for, we seem to have the same set of functions:

>>> set(fle_fns) == set(fb_fns)
True

The following code sorts both sets of tuples and returns the indices after being sorted (like np.argsort but modified to work on ordering tuples)
Luckily, fb_indices is simply [0,1,2...count] in order, because the way FB indices are stored already exactly corresponds to the way Python sorts tuples by default. So all we need to worry about is fle_indices THIS IS NOT TRUE, YOU DO HAVE TO WORRY fb_indices. CODE SNIPPETS UPDATED

fb_indices = sorted(range(len(fb_fns)), key=lambda k: fb_fns[k])
fle_indices = sorted(range(len(fle_fns)), key=lambda k: fle_fns[k])

Visually compare matched basis functions using one-hot tests with evaluate

fb_images_sorted = fb.evaluate(np.eye(fb.count)[fb_indices])
fle_images_sorted = fle.evaluate(np.eye(fb.count)[fle_indices])

Functions are visually similar when k=0 but diverge past that point. Note also that the deltas for those similar images are on a very very small scale (1e-8).

Image(fb_images_sorted[10:20]).show()
Image(fle_images_sorted[10:20]).show()
Image(fb_images_sorted[10:20] - fle_images_sorted[10:20]).show()

FB

fb

FLE

FLE

Deltas

DELTA

This seems likely to be related to the off-by-one discrepancy with ks. To be continued

Metadata

Metadata

Labels

cleanuphelp wantedExtra attention is neededinvalidThis doesn't seem righttheoryRelating to theoretical background

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions