Skip to content

Non-unique Games w/ Symmetry - #1391

Draft
j-c-c wants to merge 60 commits into
developfrom
nug
Draft

Non-unique Games w/ Symmetry#1391
j-c-c wants to merge 60 commits into
developfrom
nug

Conversation

@j-c-c

@j-c-c j-c-c commented Jun 15, 2026

Copy link
Copy Markdown
Collaborator

Adds the port of Ruiyi's NUG algorithm that handles Cn/Dn symmetric molecules.

As of now, most variables names have been left as in the original code. Many methods have been optimized via broadcasting/vectorization. GPU logic is now handled via the xp namespace. Other code alterations were made to fit within our orientation estimation framework and class structure. Additional changes were made to leverage in-house methods such as PolarFT, Handling non-zero offset images, and symmetry parsing.

@j-c-c j-c-c self-assigned this Jun 15, 2026
@codecov

codecov Bot commented Jun 15, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 99.67213% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.83%. Comparing base (83badf4) to head (5a1e432).

Files with missing lines Patch % Lines
src/aspire/abinitio/commonline_nug.py 99.63% 3 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           develop    #1391      +/-   ##
===========================================
+ Coverage    88.18%   88.83%   +0.65%     
===========================================
  Files          135      136       +1     
  Lines        14927    15824     +897     
===========================================
+ Hits         13164    14058     +894     
- Misses        1763     1766       +3     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@j-c-c
j-c-c requested a review from garrettwrong July 29, 2026 19:59

@garrettwrong garrettwrong left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

A few easy suggestions in-line but I do have larger concerns about the NUG code and am kicking it back so you can address them.

i) We don't appear to include a reproduction of original NUG for C1. If Amit is looking for complete coverage of groups' methods, this is required.

ii) We don't appear to include the symmetric extension authors reproducible example experiment. This is important since the method did not appear to work on the best experimental data we have available, so we will need this reproducible example as the full scientific test. The data will need to be made available for this purpose.

iii) After about line 300 or so into nug, where I oof'ed, the code becomes difficult to read/follow, with little documentation of the (many) variables. I had to start backing off the detailed review at that point, unfortunately. Not trying to reverse engineer that thing. While some docstrings are good, many are hit/miss, and missing coverage of their args/returns. Given (iv), this is sort of important. I didn't want to annotate every miss, too much.

iv) There are places where there are an excess of variables that could/should probably be restructured. This is normally the type of work the RSE's do. If we aren't restructuring the code to be improved (in some cases not merited/effective, I get it), I would expect the documentation to be better...

v) A lot of the xp code looks questionable. I have concerns that np was replaced with xp without considering the operations enough. Creating/performing many small array operations can often perform worse and is a poor use of limited resources. Even if performance is at par, if it fragments/uses GPU memory; this implementation may negatively impact later components that want to use the GPU for gains. I would want to see some evidence as to which of those areas actually effectively use the GPU and places that don't be reverted. Perhaps I'm totally misunderstanding some of the sizes/operations there... Some of the ones that look like might offer more were not converted, so I found it generally confusing.

vi) It is sort of confusing which methods were chosen to be static, which were not, and how they are nested (or not).

vii) a lot of the code has strange indexing. In some cases it would actually appear to be simpler as 0-based, but is written (like a port of) 1-based. I recognize a few of the areas might be cleaner as 1-based, and many neutral but are written 1-based. This is odd; reads like the code started in MATLAB and passed through multiple people who didn't clean up the indexing...

vii) I'm not sure how performance critical we need to be about this code, but the way it is written in some places has a lot of inefficient implementations. Lots of lists, scalars, lists of small arrays etc being constructed and passed around. Because of how the code is written, it isn't immediately clear where the bulk of compute is happening, so I can't offer much more there based on my limited review. (or if it matters...)

viii) The printing formatting, while fine, are not consistent with the rest of ASPIRE. In this file, there are multiple types of formatting. Probably better to convert to f-strings. FWIW, if it was not part of larger code base that uses the other formatting I would have no problem with the C style printf formats. Having multiple styles is not ideal.

I think there were some other things but I'm running out of steam/time. This is plenty. Some of those need to be addressed. Others, well, I'm not sure to what extent you've already improved from Ruiyi's code, or if the extra effort is worth it for this algorithm's results. We can discuss the right level of effort for this algo with Joakim and/or Amit when I get back, but you should have enough to stay busy until then.

Comment thread tests/test_nug.py
return request.param


@pytest.fixture(params=PR, ids=lambda x: f"proximal_refine={x}", scope="module")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

whats the purpose of this fixture?

Initialize the symmetric NUG orientation estimator. All default values match those used
for publication results, with exception of `pr_iters`, which controls how many iterations
of proximal refinement to be used. The default of None runs the algorithm without proximal
refinement. Set `pr_iters=4` to match t3he proximal refinement workflow in the related

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

typo.

Initialize the symmetric NUG orientation estimator. All default values match those used
for publication results, with exception of `pr_iters`, which controls how many iterations
of proximal refinement to be used. The default of None runs the algorithm without proximal
refinement. Set `pr_iters=4` to match t3he proximal refinement workflow in the related

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Why is the PR not on by default? is it covered in the unit tests?


Ii_hat = self.pf_full[i]
Ij_hat = self.pf_full[j]
idxi = np.round((alpha - np.pi / 2) * n_theta / 2 / np.pi) % n_theta

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

maybe for cases like this, make it an integer directly if you don't intend to use idx as floats

"""
dk = 2 * k + 1

exp_alpha_grid = np.zeros((2 * T, dk), dtype=complex_type(np.float64))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

no need for complex_type() of a static types


self.C = C

def complex2real(self, ell):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

does this require anything from self?


# Solve the symmetry-constrained SDP relaxation in the real, packed
# representation basis prepared by ADMM_preprocessing.
(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

oof, what are these

P = []
for k in range(1, Lmax + 1):
dk = 2 * k + 1
Pk = xp.eye(dk, dtype=np.float64)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

not clear to me it is useful to put so many small matrices on GPU... are they involved in something heavy later?

)
self.X_est = X_est

def admm_sym_J(self, C, verbose):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

did you check if having all these small matrices on the GPU was faster?... it may not be worth the overhead...

# Allocate one coefficient matrix per Wigner degree;
# each is block-indexed by image pair.
C = []
for k in range(1, Lmax + 1):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

if it makes more sense for k (L) to be zero based, it looks pretty easy to convert all the uses by changing the dk line.

Similar concerns throughout the file.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants