Symmetrization: recover symmetries for same-site orbital swap case - #374
Open
tylersax wants to merge 7 commits into
Open
Symmetrization: recover symmetries for same-site orbital swap case#374tylersax wants to merge 7 commits into
tylersax wants to merge 7 commits into
Conversation
Replace the per-geometry declared point-group files (2d_square/hexagonal/oblique/rectangular) with a single holohedries_2d.hpp defining the two 2D holohedries D4 and D6 and the candidate pool they generate. By the crystallographic restriction every 2D point group is a subgroup of one of these, so the lower-symmetry groups (rectangular D2, oblique C2, ...) are derived by the geometry filter rather than declared per model. - Correct D6 at the source: add the identity and use mirror denominator 12 so all six mirror lines survive (the legacy list omitted the identity and used denominator 6, collapsing to only three distinct mirrors). - Delete the now-unused subgroup structs and repoint all D4 consumers to holohedries_2d.hpp. - Migrate the triangular lattice test from C6 to D6 (its actual holohedry) and drop C6 - Add a completeness oracle (holohedries_2d_test) asserting that filtering the pool by each 2D lattice reproduces its full holohedry (8/12/4/2).
…rgence deriveAndComparePointGroup installs the 2D holohedry pool as the live group of a cluster family, gates the geometrically realized ops on H0 invariance, then restores (and verifies) the declared symmetry state. update_domains runs the check after each family's declared initializer and prints a rank-0 report only when the derived and declared groups differ; production symmetrization is unchanged. 3D models and legacy lattices without initializeH0 skip derivation.
Add point_group_symmetry_element::describe(): a short human-readable label decoded from the op matrix -- Schoenflies name (Cn^k, sigma) plus geometry (rotation angle, mirror-line angle). The derive-and-compare report now lists the labeled ops in both divergence directions (under-declared and unverified) instead of bare counts, so a report line like "sigma (mirror @ 45 deg)" pinpoints which symmetry a model's declaration is missing or overclaiming. Some minor comment cleanup as well
Hats off to Claude for catching this. Since set_symmetry_matrices records (-1, -1) when no band image is found (i.e. no symmetry gets accepted), there is a possibility that H0 gets indexed with -1 leading to an out-of-bounds read. (Identified statically, confirmed with AddressSanitizer). The fix is one more rejection branch on solveSignsForOp. Kagome is the model that reliably produces this state, so it gets added as a fixture.
Background: I was running FeAs against the derived-symmetry report we just landed in CompFUSE#369, and it came back with 4 ops vs the expected 8. The solver was rejecting C4, C4^3 and the two diagonal mirrors. Problem: FeAs's two orbitals (d_xz, d_yz) both sit at a_vec = 0. set_symmetry_matrices builds the band image by transforming r + a_b and looking for an orbital at the image position carrying the same flavor. Two orbitals on one site make the position test uninformative -- it matches every band equally. The "flavor" breaks the tie, and that pins each band to itself. The band image comes out the identity for every op, unconditionally. solveSignsForOp took the image on faith and solved only the signs. Signs can flip an entry, but they cannot move it to another band. So the solver falsely rejected real symmetries. The (small) fix: treat the geometric image as a candidate rather than an oracle. solveSignsForOp splits into tryPermutation (same function we had before, returning false where it used to throw) and deriveOrbitalOpForOp, which checks the geometric candidate first and otherwise walks the remaining permutations. Throw only when none of them works. Result 1: FeAs goes from 4 derived ops to 8, and is added as a characterization fixture to pin the number. Result 2: A Kagome win falls out of the same change. Kagome labels its three symmetry-equivalent sublattices with distinct flavors, so its matching finds no admissible image at all and records the -1 that c401448 added a guard for. Deriving P from H0 does more than dodge the out-of-bounds read -- it recovers the ops geometry could not place, and Kagome goes from 2 derived ops to 12.
tylersax
marked this pull request as ready for review
July 21, 2026 15:37
Contributor
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Important
Stacked on #369. This PR will shrink to 1 commit / 171 lines once #369 merges. The only commit to review here is
663769f8Background: I was running FeAs against the derived-symmetry report we just landed in #369, and it came back with 4 ops vs the expected 8. The solver was rejecting C4, C4^3 and the two diagonal mirrors.
Problem: FeAs's two orbitals (d_xz, d_yz) both sit at a_vec = 0. set_symmetry_matrices builds the band image by transforming r + a_b and looking for an orbital at the image position carrying the same flavor.
Two orbitals on one site make the position test uninformative -- it matches every band equally. The "flavor" breaks the tie, and that pins each band to itself. The band image comes out the identity for every op, unconditionally. solveSignsForOp took the image on faith and solved only the signs. Signs can flip an entry, but they cannot move it to another band. So the solver falsely rejected real symmetries.
The (small) fix: treat the geometric image as a candidate rather than an oracle. solveSignsForOp splits into tryPermutation (same function we had before, returning false where it used to throw) and deriveOrbitalOpForOp, which checks the geometric candidate first and otherwise walks the remaining permutations. Throw only when none of them works.
Result 1: FeAs goes from 4 derived ops to 8, and is added as a characterization fixture to pin the number.
Result 2: A Kagome win falls out of the same change. Kagome labels its three symmetry-equivalent sublattices with distinct flavors, so its matching finds no admissible image at all and records the -1 that #369 added a guard for. Deriving P from H0 does more than dodge the out-of-bounds read -- it recovers the ops geometry could not place, and Kagome goes from 2 derived ops to 12.