Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
892ffa6
fix: adds PDBFixer memory isolation and auto-repair logic
cabb99 Apr 6, 2026
ac30451
Adds method to load multiple structures in parallel
cabb99 Apr 6, 2026
273ea97
Adds sparse distance calculation
cabb99 Apr 7, 2026
43524be
renames full to dense
cabb99 Apr 7, 2026
66e84f2
Adds sparse potts model
cabb99 Apr 7, 2026
fdf8593
Adds chain breaks to support correct sequence separation masking.
cabb99 Apr 7, 2026
3fabff7
Adds computing energy from sparse matrices
cabb99 Apr 7, 2026
aa33f03
Adds sparse frustration methods
cabb99 Apr 7, 2026
efcb2c6
Adds electrostatics corrections
cabb99 Apr 7, 2026
2748c7a
Adds sparse methods to AWSEM
cabb99 Apr 8, 2026
8ace0b9
Adds sparse methods to DCA
cabb99 Apr 8, 2026
ca16691
Fixes pdbfixer mutiprocess tests
cabb99 Apr 8, 2026
7d92439
Adds some sparse methods to optimization
cabb99 Apr 8, 2026
f539244
Reduces dca test size
cabb99 Apr 8, 2026
46dbee7
Fixes numba in sparse native energy
cabb99 Apr 9, 2026
6332458
Adds sparse distance matrix pipeline
cabb99 Apr 13, 2026
756f1fd
Adds class for sparse matrix
cabb99 Apr 13, 2026
477641c
Fixes tests
cabb99 Apr 13, 2026
9c825e9
Fixes sparse matrix import test
cabb99 Apr 13, 2026
b926f9e
Separate class for sparse matrices
cabb99 Apr 14, 2026
895f7f4
Fixes calculations with sparse distance matrix
cabb99 Apr 14, 2026
5f421b6
Adds sparse distance matrix support for dca
cabb99 Apr 14, 2026
5f571ec
Adds action to update README in forks
cabb99 Apr 14, 2026
05aa804
Update docstring for profiling utilities
cabb99 Apr 14, 2026
3a16460
Merge branch 'memory_optimizationv2' of https://github.com/cabb99/DCA…
cabb99 Apr 14, 2026
b3857eb
Updates documentation
cabb99 Apr 14, 2026
fe1d950
Lazy import in optimization to save 20s of numba compilation
cabb99 Apr 15, 2026
663e13c
Fixes visualization script for multiple chains
cabb99 Apr 15, 2026
801b3fa
Fixes configurational frustration call for AWSEM
cabb99 Apr 15, 2026
9abf5a4
Removes array duplication
cabb99 Apr 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 82 additions & 0 deletions .github/workflows/update-readme.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
name: Update README badges

on:
push:
branches: [main]
workflow_dispatch:

permissions:
contents: write

jobs:
update-badges:
if: github.actor != 'github-actions[bot]'
runs-on: ubuntu-latest

steps:
- name: Check out repository
uses: actions/checkout@v4

- name: Rewrite badge block in README
env:
OWNER: ${{ github.repository_owner }}
REPO: ${{ github.event.repository.name }}
run: |
python3 <<'PY'
import os
from pathlib import Path

readme = Path("README.md")
text = readme.read_text()

start = "<!-- badges:start -->"
end = "<!-- badges:end -->"

if start not in text or end not in text:
raise SystemExit("README.md is missing badge markers")

owner = os.environ["OWNER"]
repo = os.environ["REPO"]

badge_block = (
f"<!-- badges:start -->\n"
f"[![GitHub Actions Build Status](https://github.com/{owner}/{repo}/workflows/CI/badge.svg)]"
f"(https://github.com/{owner}/{repo}/actions?query=workflow%3ACI)\n"
f"[![codecov](https://codecov.io/gh/{owner}/{repo}/graph/badge.svg?token=JKDOXOYPRS)]"
f"(https://codecov.io/gh/{owner}/{repo})\n"
f"[![Documentation Status](https://readthedocs.org/projects/frustratometer/badge/?version=latest)]"
f"(https://frustratometer.readthedocs.io/en/latest/?badge=latest)\n"
f"<!-- badges:end -->"
)

before, rest = text.split(start, 1)
_, after = rest.split(end, 1)
new_text = before + badge_block + after

# Rewrite clone URL
clone_start = "<!-- clone:start -->"
clone_end = "<!-- clone:end -->"
if clone_start in new_text and clone_end in new_text:
clone_block = (
f"<!-- clone:start -->\n"
f" git clone https://github.com/{owner}/{repo}\n"
f"<!-- clone:end -->"
)
cb, crest = new_text.split(clone_start, 1)
_, cafter = crest.split(clone_end, 1)
new_text = cb + clone_block + cafter

if new_text != text:
readme.write_text(new_text)
print("README updated.")
else:
print("README already up to date.")
PY

- name: Commit changes if needed
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add README.md
git diff --cached --quiet || git commit -m "Update README badges for ${{ github.repository }}"
git diff --cached --quiet || git push
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
Frustratometer
==============================
[//]: # (Badges)
<!-- badges:start -->
[![GitHub Actions Build Status](https://github.com/cabb99/Frustratometer/workflows/CI/badge.svg)](https://github.com/cabb99/Frustratometer/actions?query=workflow%3ACI)
[![codecov](https://codecov.io/gh/HanaJaafari/Frustratometer/graph/badge.svg?token=JKDOXOYPRS)](https://codecov.io/gh/HanaJaafari/Frustratometer)
[![codecov](https://codecov.io/gh/cabb99/Frustratometer/graph/badge.svg?token=JKDOXOYPRS)](https://codecov.io/gh/cabb99/Frustratometer)
[![Documentation Status](https://readthedocs.org/projects/frustratometer/badge/?version=latest)](https://frustratometer.readthedocs.io/en/latest/?badge=latest)
<!-- badges:end -->



Expand Down Expand Up @@ -39,7 +40,9 @@ Note: `openmm` and `pdbfixer` are not available on PyPI and must be installed se

### From source

git clone https://github.com/HanaJaafari/Frustratometer
<!-- clone:start -->
git clone https://github.com/cabb99/Frustratometer
<!-- clone:end -->
cd Frustratometer
conda install -c conda-forge --file requirements.txt
pip install -e .
Expand Down
86 changes: 86 additions & 0 deletions devtools/profiling.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
"""
Profiling utilities for tracking memory usage and execution time.

Usage (from the project folder)
from devtools.profiling import track_time, track_memory, profile

# Context managers
with track_time("Building AWSEM model"):
model = frustratometer.AWSEM(structure)

with track_memory("AWSEM sparse"):
model = frustratometer.AWSEM(structure, sparse=True)

# Decorator
@profile
def my_function():
...
"""

import time
import tracemalloc
import functools
from contextlib import contextmanager


@contextmanager
def track_time(label=""):
"""Context manager that prints elapsed wall-clock time."""
start = time.perf_counter()
yield
elapsed = time.perf_counter() - start
tag = f" [{label}]" if label else ""
print(f"Time{tag}: {elapsed:.4f} s")


@contextmanager
def track_memory(label=""):
"""Context manager that prints peak memory allocated inside the block.

Uses ``tracemalloc`` to measure only *new* Python allocations within the
block, so the numbers reflect the incremental cost of the code inside.
"""
tracemalloc.start()
snapshot_before = tracemalloc.take_snapshot()
yield
snapshot_after = tracemalloc.take_snapshot()
tracemalloc.stop()
stats = snapshot_after.compare_to(snapshot_before, "lineno")
total = sum(s.size_diff for s in stats if s.size_diff > 0)
tag = f" [{label}]" if label else ""
print(f"Memory{tag}: {_fmt_bytes(total)}")


@contextmanager
def track_peak_memory(label=""):
"""Context manager that prints the peak memory usage (high-water mark)."""
tracemalloc.start()
yield
_, peak = tracemalloc.get_traced_memory()
tracemalloc.stop()
tag = f" [{label}]" if label else ""
print(f"Peak memory{tag}: {_fmt_bytes(peak)}")


def profile(func):
"""Decorator that prints time and peak memory for a function call."""
@functools.wraps(func)
def wrapper(*args, **kwargs):
tracemalloc.start()
start = time.perf_counter()
result = func(*args, **kwargs)
elapsed = time.perf_counter() - start
_, peak = tracemalloc.get_traced_memory()
tracemalloc.stop()
print(f"{func.__name__}: {elapsed:.4f} s, peak {_fmt_bytes(peak)}")
return result
return wrapper


def _fmt_bytes(n):
"""Format byte count in human-readable units."""
for unit in ("B", "KB", "MB", "GB"):
if abs(n) < 1024:
return f"{n:.2f} {unit}"
n /= 1024
return f"{n:.2f} TB"
12 changes: 11 additions & 1 deletion docs/api_classes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ The frustratometer package provides a handful of classes used to encapsulate the
:inherited-members:
:private-members:

.. autoclass:: Frustratometer
.. autoclass:: frustratometer.classes.Frustratometer.Frustratometer
:members:
:undoc-members:
:member-order: bysource
Expand All @@ -49,4 +49,14 @@ The frustratometer package provides a handful of classes used to encapsulate the
:inherited-members:
:private-members:

.. autoclass:: SparseMatrix
:members:
:undoc-members:
:member-order: bysource
:show-inheritance:

.. autoclass:: Gamma
:members:
:undoc-members:
:member-order: bysource
:show-inheritance:
14 changes: 13 additions & 1 deletion docs/examples_awsem.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,30 @@ The Frustratometer package includes a prody-based Structure class to load the st
.. code-block:: python

import frustratometer
from pathlib import Path

# Define the path to your PDB file
pdb_path = Path('data/my_protein.pdb')
# Load the structure
# Load the structure (dense distance matrix)
structure = frustratometer.Structure.full_pdb(pdb_path)
structure.sequence #The sequence of the structure

For large proteins you can use a sparse distance matrix to avoid allocating the
full :math:`(L, L)` array:

.. code-block:: python

structure_sparse = frustratometer.Structure.full_pdb(pdb_path, sparse=True)

Creating an AWSEM Model Instance
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

After loading the structure, create an AWSEM model instance with the desired parameters. Here we provide some typical configurations that can be found elsewhere.

The AWSEM constructor defaults to ``sparse=True``, building a sparse Potts model
that stores couplings only at contact positions. Pass ``sparse=False`` to build
the full dense representation.

.. code-block:: python

## Single residue frustration with electrostatics
Expand Down
8 changes: 8 additions & 0 deletions docs/examples_dca.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ The Frustratometer package includes a prody-based Structure class to load the st
.. code-block:: python

import frustratometer
from pathlib import Path

# Define the path to your PDB file
pdb_path = Path('data/my_protein.pdb')
Expand All @@ -23,7 +24,13 @@ After loading the structure, create a DCA model instance with the desired parame

When using Potts Model files generated by the mfDCA algorithm, make sure to set the "reformat_potts_model" to True.

All DCA factory methods default to ``sparse=True``, which stores couplings only
at contact positions to avoid allocating the full :math:`(N, N, 21, 21)` tensor.
Pass ``sparse=False`` if you need the dense representation (e.g. for contact
prediction via ``scores()``).

.. code-block:: python

potts_model_file=Path('data/my_potts_model.mat')

## Using existing potts model file
Expand All @@ -36,6 +43,7 @@ Check the Accuracy of the Potts Model
In order to check the accuracy of the contacts predicted by the Potts model and true contacts from the protein's contact map, the receiver operator curve (ROC) can be generated. If the Potts model has high accuracy, the Area Under the Curve (AUC) of the ROC will be 1.

.. code-block:: python

## Generate the ROC curve
model_dca.plot_roc()

Expand Down
2 changes: 1 addition & 1 deletion docs/frustratometer.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ In this module, we implement a version of the frustratometer based on Direct Cou
Copyright
---------

Copyright (c) 2022-2024, Carlos Bueno, Hana Jaafari
Copyright (c) 2022-2026, Carlos Bueno, Hana Jaafari

Acknowledgements
----------------
Expand Down
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Welcome to Frustratometer's documentation!
installation
examples_awsem
examples_dca
sparse
api_classes
api_modules

Expand Down
Loading