PRS directional response#2251
Conversation
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>
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>
There was a problem hiding this comment.
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
========================================== 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 =====================================
|
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. |
|
It does behave as expected after trying more things but the code logic isn't great, so I am still making another version |
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