prothon-ensembles 2.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. prothon_ensembles-2.1.0/.github/workflows/publish.yml +63 -0
  2. prothon_ensembles-2.1.0/.github/workflows/tests.yml +41 -0
  3. prothon_ensembles-2.1.0/.gitignore +22 -0
  4. prothon_ensembles-2.1.0/CHANGELOG.md +173 -0
  5. prothon_ensembles-2.1.0/CITATION.cff +38 -0
  6. prothon_ensembles-2.1.0/CONTRIBUTING.md +52 -0
  7. prothon_ensembles-2.1.0/LICENSE +21 -0
  8. prothon_ensembles-2.1.0/PKG-INFO +207 -0
  9. prothon_ensembles-2.1.0/README.md +168 -0
  10. prothon_ensembles-2.1.0/legacy/Prothon.py +579 -0
  11. prothon_ensembles-2.1.0/legacy/README.md +20 -0
  12. prothon_ensembles-2.1.0/legacy/prothon_example_script.py +17 -0
  13. prothon_ensembles-2.1.0/pyproject.toml +99 -0
  14. prothon_ensembles-2.1.0/pytest.ini +7 -0
  15. prothon_ensembles-2.1.0/setup.cfg +4 -0
  16. prothon_ensembles-2.1.0/src/Prothon.py +35 -0
  17. prothon_ensembles-2.1.0/src/prothon/__init__.py +57 -0
  18. prothon_ensembles-2.1.0/src/prothon/_version.py +24 -0
  19. prothon_ensembles-2.1.0/src/prothon/cli.py +177 -0
  20. prothon_ensembles-2.1.0/src/prothon/core/__init__.py +24 -0
  21. prothon_ensembles-2.1.0/src/prothon/core/dissimilarity.py +700 -0
  22. prothon_ensembles-2.1.0/src/prothon/core/plotting.py +426 -0
  23. prothon_ensembles-2.1.0/src/prothon/core/prothon_core.py +379 -0
  24. prothon_ensembles-2.1.0/src/prothon/core/representation.py +371 -0
  25. prothon_ensembles-2.1.0/src/prothon/utils.py +92 -0
  26. prothon_ensembles-2.1.0/src/prothon_ensembles.egg-info/PKG-INFO +207 -0
  27. prothon_ensembles-2.1.0/src/prothon_ensembles.egg-info/SOURCES.txt +35 -0
  28. prothon_ensembles-2.1.0/src/prothon_ensembles.egg-info/dependency_links.txt +1 -0
  29. prothon_ensembles-2.1.0/src/prothon_ensembles.egg-info/entry_points.txt +2 -0
  30. prothon_ensembles-2.1.0/src/prothon_ensembles.egg-info/requires.txt +10 -0
  31. prothon_ensembles-2.1.0/src/prothon_ensembles.egg-info/scm_file_list.json +30 -0
  32. prothon_ensembles-2.1.0/src/prothon_ensembles.egg-info/scm_version.json +8 -0
  33. prothon_ensembles-2.1.0/src/prothon_ensembles.egg-info/top_level.txt +2 -0
  34. prothon_ensembles-2.1.0/tests/conftest.py +104 -0
  35. prothon_ensembles-2.1.0/tests/test_core.py +267 -0
  36. prothon_ensembles-2.1.0/tests/test_dissimilarity.py +257 -0
  37. prothon_ensembles-2.1.0/tests/test_representation.py +149 -0
@@ -0,0 +1,63 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ jobs:
9
+ build:
10
+ name: Build distributions
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v5
14
+ with:
15
+ fetch-depth: 0 # setuptools-scm reads the tag
16
+
17
+ - name: Set up Python
18
+ uses: actions/setup-python@v6
19
+ with:
20
+ python-version: "3.11"
21
+
22
+ - name: Install build
23
+ run: python -m pip install --upgrade pip build
24
+
25
+ - name: Build
26
+ run: python -m build
27
+
28
+ - name: Verify the built version matches the release tag
29
+ run: |
30
+ TAG="${GITHUB_REF_NAME#v}"
31
+ VER=$(ls dist/*.tar.gz | sed -E 's/.*prothon_ensembles-(.*)\.tar\.gz/\1/')
32
+ echo "built: $VER tag: $TAG"
33
+ if [ "$TAG" != "$VER" ]; then
34
+ echo "::error::built version ($VER) does not match the tag ($TAG). A dirty tree or a missing tag will do this."
35
+ exit 1
36
+ fi
37
+
38
+ - name: Check the metadata PyPI will render
39
+ run: |
40
+ python -m pip install --upgrade twine
41
+ python -m twine check dist/*
42
+
43
+ - name: Upload artifacts
44
+ uses: actions/upload-artifact@v5
45
+ with:
46
+ name: dist
47
+ path: dist/
48
+
49
+ publish:
50
+ name: Publish to PyPI
51
+ needs: build
52
+ runs-on: ubuntu-latest
53
+ environment: pypi
54
+ permissions:
55
+ id-token: write # trusted publishing; no API token is stored
56
+ steps:
57
+ - uses: actions/download-artifact@v5
58
+ with:
59
+ name: dist
60
+ path: dist/
61
+ - uses: pypa/gh-action-pypi-publish@release/v1
62
+ with:
63
+ skip-existing: true
@@ -0,0 +1,41 @@
1
+ name: tests
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ workflow_dispatch:
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ${{ matrix.os }}
12
+ strategy:
13
+ fail-fast: false
14
+ matrix:
15
+ os: [ubuntu-latest, macos-latest, windows-latest]
16
+ python-version: ["3.9", "3.11", "3.13"]
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+ with:
20
+ fetch-depth: 0 # setuptools-scm needs the tags
21
+
22
+ - uses: actions/setup-python@v5
23
+ with:
24
+ python-version: ${{ matrix.python-version }}
25
+
26
+ - name: Install
27
+ run: |
28
+ python -m pip install --upgrade pip
29
+ python -m pip install -e ".[dev]"
30
+
31
+ - name: Lint
32
+ if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.11'
33
+ run: ruff check src tests
34
+
35
+ - name: Test
36
+ run: pytest --cov=prothon --cov-report=term-missing
37
+
38
+ - name: Smoke-test the CLI
39
+ run: |
40
+ prothon --version
41
+ prothon --info
@@ -0,0 +1,22 @@
1
+ # Build artifacts
2
+ build/
3
+ dist/
4
+ *.egg-info/
5
+ src/prothon/_version.py
6
+
7
+ # Python cache
8
+ __pycache__/
9
+ *.py[cod]
10
+
11
+ # Test and coverage
12
+ .pytest_cache/
13
+ .coverage
14
+ htmlcov/
15
+ .ruff_cache/
16
+
17
+ # Prothon output
18
+ *_output/
19
+
20
+ # Environments
21
+ .venv/
22
+ venv/
@@ -0,0 +1,173 @@
1
+ # Changelog
2
+
3
+ All notable changes to Prothon are recorded here. This project follows
4
+ [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
5
+
6
+ ## [2.1.0] — unreleased
7
+
8
+ A correctness and packaging release. The public API is unchanged: code written
9
+ against 2.0 runs without modification. **Numerical results will differ**, and
10
+ the reasons are set out below — the significance test in 2.0 was not sound.
11
+
12
+ ### Fixed — the significance test
13
+
14
+ Version 2.0 built its null distribution by drawing two bootstrap resamples from
15
+ the *same* ensemble and measuring the Jensen–Shannon distance between them.
16
+ Two resamples of *n* frames drawn with replacement from the same *n* frames
17
+ share roughly 63% of their points, so they resemble each other far more closely
18
+ than two independent samples of the same size. The null was therefore too tight
19
+ by about a factor of two, and any honest between-ensemble distance cleared it.
20
+
21
+ Measured on a 400-frame Gaussian ensemble:
22
+
23
+ | quantity | value |
24
+ |---|---|
25
+ | bootstrap null used by 2.0 | 0.046 |
26
+ | two independent samples, same distribution | 0.097 |
27
+ | observed between-ensemble distance | 0.090 |
28
+
29
+ The consequence, over 40 null replicates in which both ensembles were drawn
30
+ from an identical distribution:
31
+
32
+ | | features called different | studies with ≥1 false positive |
33
+ |---|---|---|
34
+ | 2.0 bootstrap null | **100%** | **100%** |
35
+ | 2.1 permutation null | 1.2% | 7.5% |
36
+
37
+ Version 2.0 reported two independent samples of the same distribution as
38
+ differing significantly at every residue.
39
+
40
+ The replacement is a permutation test: the frames of both ensembles are pooled
41
+ and relabelled at random into two groups of the original sizes, which gives the
42
+ exact distribution of the statistic under the hypothesis that the ensembles are
43
+ the same. Per-feature values are standardised and pooled before correction, so
44
+ 100 relabellings give p-value resolution fine enough to survive a
45
+ false-discovery-rate correction over several hundred residues.
46
+
47
+ `legacy=True` (CLI: `--legacy-statistics`) reproduces 2.0's behaviour exactly,
48
+ for regenerating published figures. It is documented as unsound.
49
+
50
+ ### Fixed — everything else
51
+
52
+ - **Per-residue significance.** 2.0 computed one pooled p-value and wrote
53
+ `local_diss[p_value >= 0.05] = 0.0`. With a scalar `p_value`, NumPy reads
54
+ that as a mask over the whole array, so a single test decided the fate of
55
+ every residue at once. Each feature is now tested separately, and the
56
+ resulting p-values are Benjamini–Hochberg corrected — a 300-residue protein
57
+ tested at α = 0.05 yields fifteen false positives by construction.
58
+ - **Circular densities for torsions.** `cata` values wrap at ±π. 2.0 estimated
59
+ them with a Gaussian kernel on a linear grid, which splits a population
60
+ straddling the wraparound across both ends and puts a false trough between
61
+ them. Circular measures now use a von Mises kernel with Taylor's plug-in
62
+ bandwidth on a grid spanning a full turn. Each measure declares whether it is
63
+ circular, so the call site cannot forget.
64
+ - **`scikit-learn` is now declared as a dependency.** 2.0 imported it for PCA,
65
+ MDS and t-SNE without listing it, so a clean `pip install` failed on any run
66
+ that reached dimensionality reduction — which was the CLI default.
67
+ - **Constant features no longer crash the run.** A buried residue with zero
68
+ SASA in every frame gave `gaussian_kde` a singular covariance matrix and took
69
+ down the whole study. Such columns now get a degenerate density.
70
+ - **Float32 overflow in the contact sigmoid.** `mdtraj` returns float32
71
+ distances; `exp()` overflows above ~88 in float32 while the clip allowed 700.
72
+ The result was right by accident, via `1/(1+inf) → 0`, and noisy with
73
+ warnings. Distances are promoted to float64 first.
74
+ - **Replotting no longer overwrites saved figures**, and the documented
75
+ `xlabel`, `ylabel`, `title` and `color` arguments now take effect. In 2.0
76
+ `replot_global_dissimilarity` accepted them, discarded them, and re-saved
77
+ over the original file.
78
+ - **Reproducibility.** Resampling drew from the global NumPy state, so two runs
79
+ of one study gave different p-values with nothing recording why. `Prothon`
80
+ now takes `random_state`.
81
+ - **Empty atom selections are named.** A coarse-grained model with no C-beta
82
+ atoms produced an inscrutable NumPy error several frames down the stack.
83
+
84
+ ### Added
85
+
86
+ - **Noise floor on every result.** The distance between two disjoint halves of
87
+ a single ensemble is the smallest difference the sampling can resolve. It is
88
+ reported alongside every comparison, drawn on every dissimilarity figure, and
89
+ `ComparisonResult.resolved` says plainly whether the measurement clears it.
90
+ - **`manifest.json` per measure**, recording inputs, parameters, seed, Prothon
91
+ version and full results — so a run can be reproduced and not merely admired.
92
+ - **`ComparisonResult`**, a typed result object that still supports dictionary
93
+ access (`result["global_dissimilarity"]`) so 2.0 code keeps working. Carries
94
+ the unmasked per-residue values, per-residue p-values, the significance mask
95
+ and the noise floor.
96
+ - **`Prothon.summary()`** and a readable default CLI output. `--json` restores
97
+ the 2.0 behaviour of dumping everything to stdout.
98
+ - `prothon --info`, listing measures and detected backends.
99
+ - Backward-compatibility shim: `from Prothon import Prothon` still works,
100
+ with a `DeprecationWarning`. Removed in 3.0. Shipped as a single module
101
+ `src/Prothon.py`, not a package — a directory named `Prothon` beside one
102
+ named `prothon` is a single path on macOS and Windows.
103
+ - 81 tests, a GitHub Actions matrix across operating systems and Python
104
+ versions, and `ruff` linting.
105
+
106
+ ### Changed
107
+
108
+ - **Distribution name is `prothon-ensembles`; the import name and the command
109
+ are both `prothon`.** PyPI's `prothon` was registered in 2020 by an unrelated
110
+ protobuf generator and names there are permanent. conda-forge, where the name
111
+ is free, gets `prothon`. Version 2.0 was never published to either index, so
112
+ nothing that already works breaks.
113
+ - Releases publish to PyPI from a tag via trusted publishing (OpenID Connect),
114
+ so no long-lived API token exists to leak.
115
+ - **`src/` layout and `pyproject.toml`**, replacing `setup.py`. Versioning via
116
+ `setuptools-scm`.
117
+ - **Dimensionality reduction is off by default.** It defaulted to
118
+ `pca,mds,tsne`; MDS builds a dense frame-by-frame distance matrix, so on a
119
+ real trajectory the default turned a short comparison into an out-of-memory
120
+ failure. MDS is now refused above 5,000 frames with a message naming the
121
+ memory it would need, and a refusal no longer discards the comparison that
122
+ already succeeded.
123
+ - **Contact numbers are computed once rather than once per atom.** 2.0 rebuilt
124
+ the pair list in Python for every atom and recomputed the same distances.
125
+ Identical output to float32 precision:
126
+
127
+ | residues | 2.0 | 2.1 | speedup |
128
+ |---|---|---|---|
129
+ | 50 | 0.21 s | 0.05 s | 4× |
130
+ | 100 | 1.37 s | 0.48 s | 3× |
131
+ | 200 | 10.66 s | 1.10 s | 10× |
132
+ | 300 | 36.03 s | 2.22 s | 16× |
133
+
134
+ Pairs are processed in blocks, so a long trajectory of a large protein no
135
+ longer needs the whole distance matrix resident.
136
+ - Progress reporting moved from `print` to the `logging` module, so an
137
+ embedding program can silence or redirect it.
138
+ - Ensembles with fewer than 50 frames now emit a warning that the noise floor
139
+ understates the true uncertainty.
140
+
141
+ ### Licence
142
+
143
+ Relicensed from GPL-3.0 to **MIT**, matching the rest of the AAI Research Lab
144
+ tooling and removing an adoption barrier: a number of industrial groups have
145
+ blanket policies against GPL dependencies, and they are a large part of the
146
+ audience for ensemble comparison.
147
+
148
+ The change is not retroactive. Versions up to and including 2.0.0 were
149
+ distributed under GPL-3.0 and copies obtained under it stay governed by it.
150
+ Nothing in the dependency stack required copyleft — MDTraj is LGPL, and NumPy,
151
+ SciPy, Matplotlib and scikit-learn are BSD.
152
+
153
+ ### Known limitations
154
+
155
+ - The permutation null assumes frames are exchangeable. Frames from a single
156
+ continuous MD trajectory are correlated in time, so an ensemble holds fewer
157
+ independent conformations than it has frames and the p-values remain somewhat
158
+ optimistic. A block permutation over the correlation time is planned for 3.0.
159
+ The split-half noise floor is measured rather than assumed and is the more
160
+ trustworthy guide.
161
+ - Ensembles must share a topology. Comparison across differing sequences —
162
+ wild type against mutant, ortholog against ortholog — is planned for 3.0.
163
+
164
+ ## [2.0.0] — 2025-04-23
165
+
166
+ - Restructured the single-module version 1 into a package.
167
+ - Added dimensionality reduction (PCA, MDS, t-SNE), matrix heatmaps, combined
168
+ local dissimilarity plots and a replotting API.
169
+
170
+ ## [1.0.1] — 2023
171
+
172
+ - Original release accompanying Aina, Hsueh & Plotkin, *J. Chem. Inf. Model.*
173
+ **2023**, 63 (11), 3453–3461. Preserved under `legacy/`.
@@ -0,0 +1,38 @@
1
+ cff-version: 1.2.0
2
+ message: "If you use Prothon, please cite the method paper below."
3
+ title: "Prothon: efficient comparison of protein conformational ensembles"
4
+ abstract: >-
5
+ Prothon represents a protein conformational ensemble as a vector of
6
+ probability distributions over local order parameters and quantifies the
7
+ dissimilarity between ensembles by the Jensen-Shannon distance between
8
+ corresponding distributions. The local representation requires no structural
9
+ superposition, so the cost is linear rather than quadratic in ensemble size.
10
+ type: software
11
+ license: MIT
12
+ repository-code: "https://github.com/aai-research-lab/Prothon"
13
+ authors:
14
+ - family-names: Aina
15
+ given-names: Adekunle
16
+ - family-names: Hsueh
17
+ given-names: Shawn C. C.
18
+ - family-names: Plotkin
19
+ given-names: Steven S.
20
+ preferred-citation:
21
+ type: article
22
+ title: >-
23
+ PROTHON: A Local Order Parameter-Based Method for Efficient Comparison of
24
+ Protein Ensembles
25
+ authors:
26
+ - family-names: Aina
27
+ given-names: Adekunle
28
+ - family-names: Hsueh
29
+ given-names: Shawn C. C.
30
+ - family-names: Plotkin
31
+ given-names: Steven S.
32
+ journal: "Journal of Chemical Information and Modeling"
33
+ volume: 63
34
+ issue: 11
35
+ start: 3453
36
+ end: 3461
37
+ year: 2023
38
+ doi: "10.1021/acs.jcim.3c00145"
@@ -0,0 +1,52 @@
1
+ # Contributing to Prothon
2
+
3
+ Contributions are welcome — bug reports, new order parameters, better
4
+ estimators, documentation.
5
+
6
+ ## Getting set up
7
+
8
+ ```bash
9
+ git clone https://github.com/aai-research-lab/Prothon.git
10
+ cd Prothon
11
+ pip install -e ".[dev]"
12
+ pytest
13
+ ```
14
+
15
+ ## What a change needs
16
+
17
+ **A test.** Especially for a bug fix: the test should fail before the change
18
+ and pass after it, and be named so that a future regression says which
19
+ behaviour came back.
20
+
21
+ **A changelog entry**, under `[Unreleased]` in `CHANGELOG.md`. If a change
22
+ alters numerical results, say so plainly and say why the new numbers are the
23
+ right ones.
24
+
25
+ **Comments that explain why, not what.** The code says what it does. What a
26
+ reader cannot recover is why the bandwidth is chosen that way, or why a null
27
+ distribution is built from permutations rather than bootstrap resamples. That
28
+ reasoning belongs in the source.
29
+
30
+ ## Adding an order parameter
31
+
32
+ 1. Write `compute_<name>(traj)` in `core/representation.py`, returning an
33
+ `(n_frames, n_features)` array.
34
+ 2. Register it in `MEASURES`, declaring `circular` and `per_residue`. Getting
35
+ `circular` wrong is silent and wrong, not loud and wrong: a linear kernel on
36
+ circular data produces plausible numbers that understate dissimilarity at
37
+ the wraparound.
38
+ 3. Add it to `_COMPUTE`. A test checks the two stay in step.
39
+ 4. Test the shape, the value range, and one case where you know the answer.
40
+
41
+ ## Statistics
42
+
43
+ Changes to `core/dissimilarity.py` need a calibration check as well as a unit
44
+ test: a null case where both ensembles are drawn from the same distribution,
45
+ asserting that the false-positive rate stays near the nominal level. There is
46
+ one in `tests/test_dissimilarity.py` to copy. This is not optional — the bug
47
+ that motivated the 2.1 release passed every unit test in 2.0 and failed exactly
48
+ this check.
49
+
50
+ ## Style
51
+
52
+ `ruff check src tests`. Line length 100.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023-2026 Adekunle Aina
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,207 @@
1
+ Metadata-Version: 2.4
2
+ Name: prothon-ensembles
3
+ Version: 2.1.0
4
+ Summary: Efficient comparison of protein conformational ensembles using local order parameters
5
+ Author-email: Adekunle Aina <kunleaina@gmail.com>
6
+ Maintainer-email: Adekunle Aina <kunleaina@gmail.com>
7
+ License: MIT
8
+ Project-URL: Homepage, https://github.com/aai-research-lab/Prothon
9
+ Project-URL: Repository, https://github.com/aai-research-lab/Prothon
10
+ Project-URL: Issues, https://github.com/aai-research-lab/Prothon/issues
11
+ Project-URL: Publication, https://doi.org/10.1021/acs.jcim.3c00145
12
+ Keywords: protein-ensembles,conformational-ensembles,molecular-dynamics,intrinsically-disordered-proteins,jensen-shannon,order-parameters,structural-biology,biophysics
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Science/Research
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
24
+ Classifier: Topic :: Scientific/Engineering :: Chemistry
25
+ Classifier: Topic :: Scientific/Engineering :: Physics
26
+ Requires-Python: <3.14,>=3.9
27
+ Description-Content-Type: text/markdown
28
+ License-File: LICENSE
29
+ Requires-Dist: numpy>=1.22
30
+ Requires-Dist: mdtraj>=1.9.7
31
+ Requires-Dist: scipy>=1.9
32
+ Requires-Dist: matplotlib>=3.5
33
+ Requires-Dist: scikit-learn>=1.0
34
+ Provides-Extra: dev
35
+ Requires-Dist: pytest>=7.0; extra == "dev"
36
+ Requires-Dist: pytest-cov>=4.0; extra == "dev"
37
+ Requires-Dist: ruff>=0.4; extra == "dev"
38
+ Dynamic: license-file
39
+
40
+ <div align="center">
41
+
42
+ # Prothon
43
+
44
+ **How different are two protein ensembles — and is the difference real?**
45
+
46
+ [![DOI](https://img.shields.io/badge/DOI-10.1021%2Facs.jcim.3c00145-blue)](https://doi.org/10.1021/acs.jcim.3c00145)
47
+ [![Python](https://img.shields.io/badge/python-3.9%2B-blue)](https://pypi.org/project/prothon/)
48
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
49
+
50
+ </div>
51
+
52
+ ---
53
+
54
+ ```bash
55
+ prothon -traj wild_type.dcd,mutant.dcd -top topology.pdb -m cbcn
56
+ ```
57
+
58
+ ```
59
+ CBCN (reference: ensemble 0)
60
+ ensemble 1: d = 0.2841 (floor 0.0472) — 34/76 residues differ
61
+ ```
62
+
63
+ Prothon represents each conformational ensemble as a vector of probability
64
+ distributions over **local order parameters** — contact numbers, virtual bond
65
+ and torsion angles, solvent accessibility — and measures the Jensen–Shannon
66
+ distance between corresponding distributions. Because the representation is
67
+ local, no structural superposition is needed and the cost is linear in the
68
+ number of frames rather than quadratic, which is what makes ensembles of tens
69
+ of thousands of conformations tractable.
70
+
71
+ **It reports what it cannot resolve.** Two independent halves of a *single*
72
+ ensemble have a non-zero Jensen–Shannon distance, because a finite sample never
73
+ reproduces a continuous distribution exactly. That self-distance is the
74
+ resolution limit of the comparison, and Prothon measures it, prints it beside
75
+ every result, and draws it on every figure. A difference smaller than the floor
76
+ is reported as unresolvable rather than as a small difference.
77
+
78
+ ## Install
79
+
80
+ ```bash
81
+ conda install -c conda-forge prothon # preferred
82
+ pip install prothon-ensembles # the distribution name; see below
83
+ prothon --info
84
+ ```
85
+
86
+ The distribution on PyPI is `prothon-ensembles`, because `prothon` was
87
+ registered in 2020 by an unrelated protobuf generator and PyPI names are
88
+ permanent. The import name and the command are both `prothon`:
89
+
90
+ ```python
91
+ from prothon import Prothon
92
+ ```
93
+
94
+ ## Use it
95
+
96
+ From the command line:
97
+
98
+ ```bash
99
+ prothon -traj a.dcd,b.dcd,c.dcd -top top.pdb -m cbcn,cata -o results --seed 0
100
+ ```
101
+
102
+ | flag | meaning |
103
+ |---|---|
104
+ | `-traj` | Trajectory files, one per ensemble, comma-separated. Never concatenated. |
105
+ | `-top` | Topology (PDB), shared by all of them. |
106
+ | `-m` | Measures: `cbcn`, `cacn`, `caba`, `cata`, `sasa`. |
107
+ | `-r` | Reference ensemble index (default 0). |
108
+ | `-o` | Output root. Each measure writes `<measure>_output/`. |
109
+ | `-d` | Projections: `pca`, `mds`, `tsne`. Off by default. |
110
+ | `--seed` | Set it, and the run is reproducible. |
111
+
112
+ Or from Python:
113
+
114
+ ```python
115
+ from prothon import Prothon
116
+
117
+ study = Prothon(["wild_type.dcd", "mutant.dcd"], "topology.pdb", random_state=0)
118
+ results = study.compare_ensembles(methods="cbcn")
119
+
120
+ comparison = results["cbcn"][0]
121
+ comparison.global_dissimilarity # 0.2841
122
+ comparison.noise_floor # 0.0472 — the resolution limit
123
+ comparison.resolved # True: the difference clears the floor
124
+ comparison.significant # bool array, one per residue
125
+ comparison.local_dissimilarity # per residue, zero where not significant
126
+ comparison.raw_local_dissimilarity # per residue, unmasked
127
+
128
+ print(study.summary())
129
+ ```
130
+
131
+ Each measure writes a directory containing the representation matrices as CSV,
132
+ heatmaps, global and per-residue dissimilarity figures, and a `manifest.json`
133
+ recording the inputs, parameters, seed and version that produced them.
134
+
135
+ ## The measures
136
+
137
+ | name | quantity | circular |
138
+ |---|---|---|
139
+ | `cbcn` | C-beta contact number, smooth cutoff | |
140
+ | `cacn` | C-alpha contact number, smooth cutoff | |
141
+ | `caba` | Virtual Cα–Cα–Cα bond angle | |
142
+ | `cata` | Virtual Cα torsion angle | yes |
143
+ | `sasa` | Per-residue solvent accessible surface area | |
144
+
145
+ Torsions live on a circle, so they are estimated with a von Mises kernel on a
146
+ grid spanning a full turn. Each measure declares this, so the call site cannot
147
+ forget it.
148
+
149
+ ## Upgrading from 2.0
150
+
151
+ The API is unchanged and existing scripts run without modification, but
152
+ **numbers will differ**, because the significance test in 2.0 was not sound: it
153
+ compared each ensemble against a bootstrap of itself, a null about half as wide
154
+ as the true sampling variability. Over 40 replicates in which both ensembles
155
+ were drawn from an *identical* distribution, 2.0 called 100% of residues
156
+ significantly different. The permutation test that replaces it sits at 1.2%.
157
+
158
+ `--legacy-statistics` reproduces the old behaviour for regenerating published
159
+ figures. [CHANGELOG.md](CHANGELOG.md) has the full account.
160
+
161
+ `from Prothon import Prothon` still works and warns; use
162
+ `from prothon import Prothon`.
163
+
164
+ ## Citation
165
+
166
+ > Aina, A.; Hsueh, S. C. C.; Plotkin, S. S. PROTHON: A Local Order
167
+ > Parameter-Based Method for Efficient Comparison of Protein Ensembles.
168
+ > *J. Chem. Inf. Model.* **2023**, *63* (11), 3453–3461.
169
+ > DOI: [10.1021/acs.jcim.3c00145](https://doi.org/10.1021/acs.jcim.3c00145)
170
+
171
+ ```bibtex
172
+ @article{aina2023prothon,
173
+ author = {Aina, Adekunle and Hsueh, Shawn C. C. and Plotkin, Steven S.},
174
+ title = {PROTHON: A Local Order Parameter-Based Method for Efficient
175
+ Comparison of Protein Ensembles},
176
+ journal = {Journal of Chemical Information and Modeling},
177
+ volume = {63},
178
+ number = {11},
179
+ pages = {3453--3461},
180
+ year = {2023},
181
+ doi = {10.1021/acs.jcim.3c00145},
182
+ }
183
+ ```
184
+
185
+ ## Contributing
186
+
187
+ See [CONTRIBUTING.md](CONTRIBUTING.md). The original version 1 code accompanying
188
+ the 2023 paper is preserved unchanged under `legacy/`.
189
+
190
+ ## License
191
+
192
+ MIT. See [LICENSE](LICENSE).
193
+
194
+ Prothon was distributed under GPL-3.0 up to and including version 2.0.0.
195
+ From 2.1.0 the project is MIT-licensed. Copies already obtained under
196
+ GPL-3.0 remain governed by that licence — relicensing is not retroactive
197
+ and takes nothing away from anyone who has a copy.
198
+
199
+ ---
200
+
201
+ <div align="center">
202
+
203
+ Built in the [AAI Research Lab](https://aai-research-lab.github.io) at
204
+ California State University Dominguez Hills, on MDTraj, NumPy, SciPy,
205
+ scikit-learn and Matplotlib.
206
+
207
+ </div>