Skip to content

fix three bugs in dynamics/analysis.py (dropped hinges, IndexError, wrong collectivity norm) - #2262

Open
steps-re wants to merge 1 commit into
prody:mainfrom
steps-re:fix/dynamics-hinges-collectivity
Open

fix three bugs in dynamics/analysis.py (dropped hinges, IndexError, wrong collectivity norm)#2262
steps-re wants to merge 1 commit into
prody:mainfrom
steps-re:fix/dynamics-hinges-collectivity

Conversation

@steps-re

Copy link
Copy Markdown

disclosing up front: this was put together with help from claude (anthropic's coding agent), then reproduced and verified by me before opening. first pr i'm opening on prody.

three separate bugs in prody/dynamics/analysis.py, all found while looking at the hinge-finding code. each is small and independent so happy to split into separate prs if you'd rather.

1. getGlobalHinges silently drops every chain's hinges but the last

the if trim is not False / else block that accumulates hinges is indented at the same level as for fst, lst in chains:, so it runs once after the chain loop finishes, using only the leftover h/l/fst from the last chain. for any multi-chain structure, every earlier chain's hinges are silently thrown away. fix is to move the accumulation inside the loop.

there's also an off-by-one in the same spot: chains stores inclusive (fst, lst) pairs, but the code slices vecs[fst:lst, i] (exclusive), which drops each chain's last residue from hinge detection. fixed to vecs[fst:lst+1, i].

2. getHinges crashes on a segment with no sign change

merged = [regs[0]] raises IndexError when regs is empty, which happens whenever an eigenvector segment has no sign crossing (realistic for short segments, and more likely once bug #1 is splitting by chain). added a guard to return no hinges instead of crashing.

3. calcCollectivity mis-normalizes when masses are supplied

Bruschweiler 1995 needs the per-atom msd normalized into a probability distribution (p_i = u_i^2 / sum(u_j^2)) before the shannon-entropy step. the code divides by sqrt(sum(u_j^2)) instead. this cancels on the default path because mode vectors are already unit-normalized (sum == sqrt(sum) == 1), so it only shows up when masses is passed, which is the documented-supported path. for v=[0.6,0.8], masses=[1,4] the current result is ~0.9878 vs the correct ~0.9269 (~35% off). fix is to divide by the sum, not its sqrt.

verification

added prody/tests/dynamics/test_analysis_hinge_bugs.py (offline, deterministic, no downloads): 4 tests targeting the bugs + 2 control/regression guards. confirmed all 4 fail on current main (IndexError, dropped-chain hinge mismatch, wrong collectivity value) and pass after the fixes. ran the full prody/tests/dynamics/ suite, no regressions (existing test_hinge_finder.py still passes). the default no-masses collectivity path is unchanged.

mike

…rong collectivity norm

getGlobalHinges was silently discarding hinges from every chain but the
last: the accumulation block was indented one level too high, outside the
per-chain loop, so it only ever ran once using the leftover h/l/fst from
the final chain. Moved it inside the loop. Also fixed an off-by-one in the
same function: chains stores inclusive (fst, lst) pairs but the vecs slice
used the exclusive vecs[fst:lst], dropping the last residue of every
chain from hinge detection. Now vecs[fst:lst+1, i].

getHinges raised IndexError (regs[0] on an empty list) whenever an
eigenvector segment had no sign change at all. Added a guard that returns
an empty hinge list in that case, matching the function's normal return
type.

calcCollectivity normalized u2in by dividing by sqrt(sum) instead of sum
when masses were supplied, which doesn't produce a valid probability
distribution per Bruschweiler 1995 eq. 5 (the exponent no longer sums the
correct entropy term). Only the masses path was affected; the default
unit-mass path is unchanged since sum and sqrt(sum) both equal 1 there.

Added prody/tests/dynamics/test_analysis_hinge_bugs.py with synthetic,
offline, deterministic repros for all three (multi-chain AtomGroup +
monkeypatched _getModeVecs for the hinge bugs, hand-computed Bruschweiler
values for collectivity). All 6 new tests fail on the prior code and pass
after the fix; existing dynamics test modules show no regressions.

Co-Authored-By: Claude <noreply@anthropic.com>
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.

1 participant