Skip to content

PRS directional response#2251

Closed
jamesmkrieger wants to merge 4 commits into
prody:mainfrom
jamesmkrieger:jamesk/prs-directional-response
Closed

PRS directional response#2251
jamesmkrieger wants to merge 4 commits into
prody:mainfrom
jamesmkrieger:jamesk/prs-directional-response

Conversation

@jamesmkrieger

Copy link
Copy Markdown
Contributor

Here is a fix that I have been wanting for a long time. PRS now works to give directional responses using SignDy ModeEnsemble, giving n_atoms members with n_repeats modes each over 3 x n_atoms

jamesmkrieger and others added 2 commits July 18, 2026 00:02
return_vectors=True keeps the per-force response displacement vectors
cov.f (as a ModeEnsemble, one modeset per perturbed node) instead of
squaring and averaging them into the magnitude PRS matrix, so each
response can be overlapped with a target conformational change (e.g.
calcDeformVector) to identify which perturbations trigger a transition.
Optional force/perturb_node override the random forces or restrict the
scan. The default path is unchanged; requires a 3d model.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Exercises the return_vectors ModeEnsemble on the adenylate kinase
open->closed transition (4ake chain A -> 1ake, bundled datafiles):
ensemble shape, cov.f correctness, squared-magnitude eigenvalues,
perturb_node restriction, the GNM 3d-only guard, and that a directed
force recovers the closure better than any single ANM mode. Legacy
three-tuple return is checked for back-compatibility.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
jamesmkrieger and others added 2 commits July 18, 2026 00:10
return_vectors always applies explicit perturbing forces, so the turbo
setting is ignored when it is enabled.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@karolamik13 karolamik13 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Tests below:
In [1]: from prody import *
...:
...: atoms = parsePDB('1tqn', subset='calpha')
...:
...: anm = ANM('1tqn')
...: anm.buildHessian(atoms)
...: anm.calcModes(n_modes=20)
...:
...: prs = calcPerturbResponse(
...: anm,
...: atoms=atoms,
...: return_vectors=True,
...: repeats=5
...: )
...:
...: type(prs)
...: prs.numModeSets()
...: prs.numModes()
...: prs.numAtoms()
...:
@> PDB file is found in working directory (1tqn.pdb.gz).
@> 468 atoms and 1 coordinate set(s) were parsed in 0.01s.
@> Hessian was built in 0.21s.
@> 20 modes were calculated in 0.14s.
@> Calculating directional perturbation response [ 99%] 1sOut[1]: 468

In [2]: prs.numModeSets() == atoms.numAtoms()
...: prs.numModes() == 5
...: prs.numAtoms() == atoms.numAtoms()
...:
Out[2]: True

In [3]: prs_one = calcPerturbResponse(
...: anm,
...: atoms=atoms,
...: return_vectors=True,
...: perturb_node=10,
...: force=[1, 0, 0])
...:
...: prs_one.numModeSets()
...: prs_one.numModes()
...: prs_one.getLabels()
...:
@> Calculating directional perturbation response [ 0%]Out[3]: ['perturb_38']

In [4]: prs_one.numModeSets() == 1
...: prs_one.numModes() == 1
...:
Out[4]: True

In [5]: calcOverlap(prs_one.getModeSets()[0], anm[0])
Out[5]: array([0.69835304])

And test in ~/Prody:
$ python -m pytest -v $(git show --name-only --format="" 1278eda | grep 'test.*.py$')

========================================== test session starts ==========================================
platform linux -- Python 3.10.15, pytest-8.3.3, pluggy-1.5.0 -- /home/XXX/anaconda3/envs/prody_tests/bin/python
cachedir: .pytest_cache
rootdir: ~/ProDy
configfile: pyproject.toml
plugins: skip-slow-0.0.5, anyio-4.9.0
collected 7 items

prody/tests/dynamics/test_perturb.py::TestCalcPerturbResponse::testDirectionalPRSRecoversClosure PASSED [ 14%]
prody/tests/dynamics/test_perturb.py::TestCalcPerturbResponse::testEigvalsAreSquaredResponseMagnitude PASSED [ 28%]
prody/tests/dynamics/test_perturb.py::TestCalcPerturbResponse::testGNMRaisesForVectors PASSED [ 42%]
prody/tests/dynamics/test_perturb.py::TestCalcPerturbResponse::testLegacyReturnsTriple PASSED [ 57%]
prody/tests/dynamics/test_perturb.py::TestCalcPerturbResponse::testPerturbNodeList PASSED [ 71%]
prody/tests/dynamics/test_perturb.py::TestCalcPerturbResponse::testResponseVectorEqualsCovDotForce PASSED [ 85%]
prody/tests/dynamics/test_perturb.py::TestCalcPerturbResponse::testReturnVectorsEnsembleShape PASSED [100%]

=========================================== warnings summary ============================================
:241
:241: DeprecationWarning: builtin type SwigPyPacked has no module attribute

:241
:241: DeprecationWarning: builtin type SwigPyObject has no module attribute

:241
:241: DeprecationWarning: builtin type swigvarlink has no module attribute

prody/tests/dynamics/test_perturb.py::TestCalcPerturbResponse::testDirectionalPRSRecoversClosure
prody/tests/dynamics/test_perturb.py::TestCalcPerturbResponse::testDirectionalPRSRecoversClosure
~/ProDy/prody/utilities/eigtools.py:44: DeprecationWarning: Keyword argument 'turbo' is deprecated in favour of 'driver=gvd' keyword instead and will be removed in SciPy 1.12.0.
values, vectors = linalg.eigh(M, turbo=turbo, eigvals=eigvals)

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
===================================== 7 passed, 5 warnings in 2.01s =====================================

@jamesmkrieger

Copy link
Copy Markdown
Contributor Author

Does it behave like you’d expect? It seems like pushing residues that are supposed to move is what has the biggest effect, which is against the idea of sensors and effectors. I think I want to try it out more and probably recode it again myself. The ordering in the file is messy anyway with it not reusing bits.

@jamesmkrieger

Copy link
Copy Markdown
Contributor Author

It does behave as expected after trying more things but the code logic isn't great, so I am still making another version

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