Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gh-487: reorder alms in _generate_grf to glass ordering #533

Open
wants to merge 8 commits into
base: main
Choose a base branch
from

Conversation

Saransh-cpp
Copy link
Member

@Saransh-cpp Saransh-cpp commented Feb 21, 2025

Description

The PR reorder alms in _generate_grf to glass ordering before operating on them and then reorders it back to healpix before passing into the yield function.

The code works but it is slowing down examples instead of making them faster. I think I am doing something wrong here. @ntessore could you look at this and point me in the right direction?

Fixes: #487

Changelog entry

Checks

  • Is your code passing linting?
  • Is your code passing tests?
  • Have you added additional tests (if required)?
  • Have you modified/extended the documentation (if required)?
  • Have you added a one-liner changelog entry above (if required)?

Copy link
Member Author

@Saransh-cpp Saransh-cpp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See #530 for the mypy failure.

glass/fields.py Outdated
@@ -388,6 +388,8 @@ def _generate_grf(
# sample real and imaginary parts, then view as complex number
rng.standard_normal(n * (n + 1), np.float64, z.view(np.float64))

z = np.asanyarray(healpix_to_glass_spectra(z))
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the functions return a numpy array instead?

@Saransh-cpp Saransh-cpp marked this pull request as ready for review February 21, 2025 17:14
@Saransh-cpp Saransh-cpp self-assigned this Feb 21, 2025
@Saransh-cpp Saransh-cpp added the api An (incompatible) API change label Feb 21, 2025
@paddyroddy paddyroddy added blocked The issue or pull request is blocked by something and removed blocked The issue or pull request is blocked by something labels Feb 21, 2025
@ntessore
Copy link
Collaborator

Thanks for taking this on @Saransh-cpp!

I think we crossed wires with two similar but separate things going on at the same time:

  • The ordering of the spectra. Here we are using GLASS ordering and we want to continue GLASS ordering. The two new functions are only useful for legacy mode interactions between GLASS and other HEALPix-based codes.
  • The ordering of the alms. Here we are using HEALPix ordering at the moment, and the idea is to switch this around.

This switch is fully internal and there are no observable changes to anything from the outside.

The way our alms = $a_{lm}$ are sorted now is keeping $m$ fixes while $l$ increases, so first $m = 0$, $l = 0, 1, 2, \ldots$, then $m = 1$, $l = 1, 2, \ldots$, then $m = 2$, $l = 2, \ldots$, etc. This is encoded in how multalm() works.

After the change, our internal generation would keep $l$ fixed while $m$ increases, so first $l = 0$, $m = 0$, then $l = 1$, $m = 0, 1$, then $l = 2$, $m = 0, 1, 2$, etc. Note how this corresponds to switching from iterating over an upper triangular matrix to a lower triangular matrix.

The goal of this internal reordering is that we will be able to split everything up in blocks of $l$ values with different correlation lengths.

After this internal reordering, we still need to send our alms to healpy for the spherical harmonic transform. So there needs to be an internal reordering.

@paddyroddy paddyroddy removed their request for review February 24, 2025 12:59
@paddyroddy
Copy link
Member

Can you ask me again for a review once the science is worked out? I'm not fully sure what is going on here

@Saransh-cpp Saransh-cpp marked this pull request as draft February 26, 2025 10:50
@ntessore
Copy link
Collaborator

Oh, I just realised this is marked as a fix for #487. That issue is about an unrelated change. The reordering of the alms in the GRF sampling that we discussed is a purely internal change for the time being. Maybe #487 will never be done.

@Saransh-cpp Saransh-cpp marked this pull request as ready for review March 6, 2025 14:56
Copy link
Member Author

@Saransh-cpp Saransh-cpp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ntessore could you please take a look at this again and let me know if this is the right direction?

multalm is definitely slower now because of the dynamic indexing. It would really benefit from using ragged arrays for accessing alms in a NumPy-like way, but we can discuss that in the JAX project. Just using plain NumPy arrays would eat up extra memory because of the matrix structure.

glass/fields.py Outdated
Comment on lines 222 to 231
n = len(bl)
for d in range(n):
indices = []
for m in range(d, n):
# dynamically calculate indices
index = m * (m + 1) // 2 + d
indices.append(index)

if indices:
out[indices] *= bl[: len(indices)]
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function now assumes that the incoming alms are in the order -

[00, 10, 11, 20, 21, 22, 30, 31, 32, 33, ...]

Therefore, it now multiplies alm with bl in the following way -

bl := [1, 10, 100, 1000]

[0, 10, 20, 30] * bl → [0, 100, 2000, 30000]
[11, 21, 31] * bl[:3] → [11, 210, 3100]
[22, 32] * bl[:2] → [22, 320]
[33] * bl[:1] → [33]

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that's right: bl[0] is to be multiplied with alm where l=0, so just alm[0:1]. bl[1] is to be multiplied with alm where l=1, so alm[1:3], and so on.

Long version of the loop:

stop = 0
for ell in range(n):
    start, stop = stop, stop + ell + 1
    alm[start : stop] *= bl[ell]

Short version of the loop:

for ell in range(n):
    alm[ell * (ell + 1) // 2 : (ell + 1) * (ell + 2) // 2] *= bl[ell]

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I got the multiplication wrong! That's why I was confused about why we are switching to this order. Thanks for the correction!

glass/fields.py Outdated
@@ -402,6 +410,8 @@ def _generate_grf(
if j is not None:
y[:, j] = z

alm = np.asanyarray(glass.glass_to_healpix_spectra(alm)) # type: ignore[arg-type]
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC, the alms should be converted back to healpix ordering before being passed into hp functions. I have used the glass.glass_to_healpix_spectra because it does the ordering in the same way.

Copy link
Collaborator

@ntessore ntessore Mar 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it does the reordering in the same way, that's "by accident", and we should not bind ourselves to an unrelated function that happens to do the same thing.

For reordering the alm:

Have: $[a_{00}, a_{10}, a_{11}, a_{20}, a_{21}, a_{22}, \ldots]$
Want: $[a_{00}, a_{10}, a_{20}, \ldots, a_{11}, a_{21}, \ldots, a_{22}, \ldots]$

In the first array, the element with indices $(\ell, m)$ appears in position $\ell (\ell + 1) / 2 + m$. This is what you have in the function above.

def glass_to_healpix_alm(alm):
    n = inv_triangle_number(alm.size)  # we need that function back :)
    ell = np.arange(n)
    out = []
    for m in ell:
        out.append(alm[ell[m:] * (ell[m:] + 1) // 2 + m])
    return np.concatenate(out)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Should I just revert #527 so that we don't use functions named for spectra inside functions meant for alm?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should add the private function back in, then make two helper functions with clear purpose (and error msg) instead of using it directly?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good! I will wrap that up in another PR, which can be merged before this one to make everything cleaner.

@Saransh-cpp
Copy link
Member Author

Okay, so the calculations are not correct, but I am not sure where it is going wrong. I will debug tomorrow -

cosmic_shear.ipynb

image

lensing.ipynb

image

@ntessore
Copy link
Collaborator

ntessore commented Mar 6, 2025

We should really set a timeout on the examples test 😬

@Saransh-cpp Saransh-cpp marked this pull request as ready for review March 11, 2025 17:24
Co-authored-by: Nicolas Tessore <[email protected]>
@Saransh-cpp Saransh-cpp requested a review from ntessore March 12, 2025 15:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api An (incompatible) API change
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Reorder alms to glass ordering
3 participants