Skip to content

fix(rfd3): build symmetry frame translations as float - #420

Open
JMacnar wants to merge 1 commit into
productionfrom
fix/symmetry-frame-translation-dtype
Open

JMacnar wants to merge 1 commit into
productionfrom
fix/symmetry-frame-translation-dtype

Conversation

@JMacnar

@JMacnar JMacnar commented Nov 10, 2026 •

Copy link
Copy Markdown
Member

Fixes #418

Problem

Symmetric designs fail in collate_examples with a dtype mismatch on
translation[row, target] = t, before the first diffusion step. Traceback and repro in
#418.

get_cyclic_frames and get_dihedral_frames build a frame's translation as
np.array([0, 0, 0]), which is int64. framecoords_to_RTs divides the rotation by a
norm, so R comes out float, but T = Ori passes the integer dtype through.
AddSymmetryFeats.make_transforms_dict carries it into feats["sym_transform"].
symmetry_maps then writes it by index into translation = torch.zeros(b, l, 3), which
is float, and an indexed write requires the dtypes to match.

batching.py arrived with #401 (2026-10-14), so this path is only reached since then.
test_rfd3_batch_symmetry.py does not cover it, and could not: it builds sym_transform
by hand because it needs a non-zero translation and a different frame per example to tell
a per-example mapping from a shared one. That leaves the dtype the generators produce
untested, which is what the new test pins.

Fix

Give the translation a float dtype where it is created, at the four literals in
frames.py:

np.array([0, 0, 0])   ->   np.zeros(3, dtype=np.float32)

This changes no value. The translation is exactly zero in every generator, and consumers
already add it to float coordinates: atom_array.py:161 does atom_array.coord += T,
with a comment that T should be 0. The rotations were always float, since they come from
np.cos and np.sin; only the translation was integer.

I changed the generators rather than casting in framecoords_to_RTs or at
batching.py:190, because that is where a translation in angstroms is being stored as an
integer, and a cast further down leaves the next reader of it to trip over the same thing.

Verification

  • New test test_frame_translation_keeps_the_rotation_dtype in
    models/rfd3/tests/test_symmetry_frames.py, parametrized over C2, C3 and D2. It goes
    through get_symmetry_frames_from_symmetry_id and decompose_symmetry_frame, the
    path a real design takes. It fails on production for all three groups and passes
    with the fix; the file goes from 23 to 26 passing tests.
  • End to end: rfd3 design with {"length": 200, "symmetry": {"id": "C2"}} on the
    default settings. On production it raises in symmetry_maps; with the fix it
    completes and writes a two-chain C2 assembly (200 residues per chain, 0.03 A RMSD
    between one chain and the other rotated 180 degrees about z).
  • Every symmetry and batching test file in models/rfd3/tests passes: 98 tests, no
    failures. (test_symmetry.py errors during collection on missing json test data, both
    with and without this change.)
  • In use since 9 November on a C2, C3, C4 and C6 design campaign, about 4000 structures
    over thirteen jobs. Geometry checked on one C6 assembly of six 500-residue chains: each
    chain superimposes on the first at one of the six multiples of 60 degrees, every angle
    used once, worst CA RMSD 0.086 A, assembly centred on the origin to within 0.02 A.

Disclosure

This change was prepared with AI assistance. The test and the end-to-end run above were
both executed.

The frame generators wrote the translation as np.array([0, 0, 0]), which numpy
makes int64, while the rotation comes out float from np.cos and np.sin. Four
literals in frames.py now build it as np.zeros(3, dtype=np.float32). The
translation is zero in all four, so the coordinates are unchanged.

symmetry_maps writes the translation into a float tensor by index, which
requires matching dtypes, so symmetric designs failed in collate_examples
before the first diffusion step. Everywhere else the translation is only added
to coordinates, where an integer promotes, so nothing caught it earlier.

Adds a regression test over C2, C3 and D2 that builds its frames with the real
generators, so the dtype is covered where it is set.

Fixes #418
@JMacnar
JMacnar marked this pull request as ready for review November 10, 2026 14:53
@JMacnar JMacnar self-assigned this Nov 10, 2026

This branch has not been deployed

No deployments
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.

[BUG] Symmetric designs fail in the batched path: the frame translation is int64

1 participant