Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
be63cdb
Bring rfd3 FabricTrainer under the mypy type-check gate
lyskov Jun 30, 2026
725ed38
Bring rfd3 legacy_input_parsing under the mypy type-check gate
lyskov Jun 30, 2026
04a542a
Clear the final rfd3 modules from the mypy ratchet (engine, vizualize)
lyskov Jun 30, 2026
deaaf6e
Bring models/mpnn into the mypy gate behind an ignore_errors ratchet
lyskov Jun 30, 2026
eb0c9db
Wire mpnn's CPU-portable tests into the pytest gate
lyskov Jun 30, 2026
3fec4ab
Clear the small-tier mpnn modules from the mypy ratchet
lyskov Jun 30, 2026
1e01beb
Clear the inference/pipeline-glue mpnn modules from the mypy ratchet
lyskov Jul 1, 2026
89e221f
Clear the mpnn feature-collator and train modules from the mypy ratchet
lyskov Jul 1, 2026
de49441
Clear mpnn.utils.weights from the mypy ratchet
lyskov Jul 1, 2026
bd7ed56
Type-check mpnn.utils.inference; fully clear the mpnn mypy ratchet
lyskov Jul 1, 2026
36622e9
Bring models/rfd3na into the mypy gate behind an ignore_errors ratchet
lyskov Jul 1, 2026
6acfff7
Clear the 1-error-tier rfd3na modules from the mypy ratchet
lyskov Jul 1, 2026
c4e839f
Clear the 2-3-error-tier rfd3na modules from the mypy ratchet
lyskov Jul 1, 2026
fd0ac2d
Clear the io / trainer-utils / dump / run_inference rfd3na modules fr…
lyskov Jul 1, 2026
283d7a9
Clear the na_geom / pipelines / design_transforms / RFD3 rfd3na modules
lyskov Jul 1, 2026
3c2e674
Clear the engine / inference_sampler / trainer rfd3na modules from th…
lyskov Jul 1, 2026
17664bb
Clear the utils.inference / hbonds / na_geom_utils rfd3na modules
lyskov Jul 1, 2026
fee0c9f
Clear rfd3na.constants from the mypy ratchet
lyskov Jul 1, 2026
7809032
Clear rfd3na legacy_input_parsing; port _check_has_backbone helper fr…
lyskov Jul 1, 2026
690ca20
Clear rfd3na input_parsing from the mypy ratchet
lyskov Jul 1, 2026
ef97d85
Type-check rfd3na fabric_trainer; fully clear the rfd3na mypy ratchet
lyskov Jul 1, 2026
0fcc8e4
Wire rfd3na tests into the pytest gate; add CPU unit tests
lyskov Jul 1, 2026
88b5eed
Add rfd3na CPU tests for cfg_utils and symmetry checks
lyskov Jul 1, 2026
b618ae4
Add rfd3na CPU tests for the NA dot-bracket parser and dict flattener
lyskov Jul 1, 2026
d76c4f9
Add rfd3na CPU tests for the symmetry atom_array helpers
lyskov Jul 1, 2026
7247d43
Add rfd3na CPU tests for symmetry check and Kabsch alignment
lyskov Jul 2, 2026
6a1c4d5
Add rfd3na CPU tests for conditioning_utils pure helpers
lyskov Jul 2, 2026
2fac0ed
Reclaim mpnn test_inference_utils.py into the pytest gate
lyskov Jul 2, 2026
3db0234
Bring mpnn small-tier modules under strict mypy
lyskov Jul 2, 2026
da789b4
Bring mpnn 3-error-tier modules under strict mypy
lyskov Jul 2, 2026
12dfd18
Bring mpnn pipelines and message_passing under strict mypy
lyskov Jul 2, 2026
3426954
Bring mpnn metrics modules under strict mypy
lyskov Jul 2, 2026
fab12a2
Bring mpnn model.mpnn under strict mypy
lyskov Jul 2, 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
10 changes: 6 additions & 4 deletions models/mpnn/src/mpnn/collate/feature_collator.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class FeatureCollator:
scalar values (requiring consistency checks across the batch).
"""

def __init__(self, default_padding: Dict[str, Any] = None):
def __init__(self, default_padding: Dict[str, Any] | None = None):
"""
Initialize the FeatureCollator.
Expand Down Expand Up @@ -170,7 +170,7 @@ def __call__(self, pipeline_outputs: List[Dict[str, Any]]) -> Dict[str, Any]:

return network_inputs

def _deep_equal(self, a, b):
def _deep_equal(self, a: Any, b: Any) -> bool:
if isinstance(a, torch.Tensor) and isinstance(b, torch.Tensor):
return torch.equal(a, b)
if isinstance(a, dict) and isinstance(b, dict):
Expand Down Expand Up @@ -200,7 +200,9 @@ class TokenBudgetAwareFeatureCollator(FeatureCollator):
"""

def __init__(
self, max_tokens_with_padding: int, default_padding: Dict[str, Any] = None
self,
max_tokens_with_padding: int,
default_padding: Dict[str, Any] | None = None,
):
super().__init__(default_padding)
self.max_tokens_with_padding = max_tokens_with_padding
Expand Down Expand Up @@ -237,7 +239,7 @@ def __call__(self, pipeline_outputs: List[Dict[str, Any]]) -> Dict[str, Any]:
examples_with_L.sort(key=lambda x: x[0])

# Apply token budget constraint by removing largest examples first.
filtered_examples = []
filtered_examples: List[tuple[int, Dict[str, Any]]] = []
max_length = 0
for L, example in examples_with_L:
new_batch_size = len(filtered_examples) + 1
Expand Down
22 changes: 17 additions & 5 deletions models/mpnn/src/mpnn/inference_engines/mpnn.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,11 @@ def _validate_model_config(self) -> None:
raise TypeError("checkpoint_path must be a string path.")

# Check that the checkpoint path exists.
ckpt_path = Path(_absolute_path_or_none(self.checkpoint_path))
abs_checkpoint_path = _absolute_path_or_none(self.checkpoint_path)
# checkpoint_path is a non-empty str here (set in __init__), so the absolute
# form is never None.
assert abs_checkpoint_path is not None
ckpt_path = Path(abs_checkpoint_path)
if not ckpt_path.is_file():
raise FileNotFoundError(
f"checkpoint_path does not exist: {self.checkpoint_path}"
Expand Down Expand Up @@ -147,8 +151,11 @@ def _validate_all(self) -> None:

def _post_process_engine_config(self) -> None:
"""Normalize paths into absolute paths."""
# Make checkpoint path absolute.
self.checkpoint_path = _absolute_path_or_none(self.checkpoint_path)
# Make checkpoint path absolute. checkpoint_path is a non-empty str (set in
# __init__), so the absolute form is never None and the attribute stays `str`.
abs_checkpoint_path = _absolute_path_or_none(self.checkpoint_path)
assert abs_checkpoint_path is not None
self.checkpoint_path = abs_checkpoint_path

# Make output directory absolute.
if self.out_directory is not None:
Expand Down Expand Up @@ -250,8 +257,13 @@ def run(
"'atom_arrays' and 'input_dicts' must have the same length."
)

# Determine the number of inputs.
num_inputs = len(input_dicts) if input_dicts is not None else len(atom_arrays)
# Determine the number of inputs. The guard above ensures at least one of
# input_dicts / atom_arrays is non-None; prefer input_dicts when present.
if input_dicts is not None:
num_inputs = len(input_dicts)
else:
assert atom_arrays is not None
num_inputs = len(atom_arrays)
results: list[MPNNInferenceOutput] = []
for input_idx in range(num_inputs):
# Construct the per-input MPNNInferenceInput.
Expand Down
15 changes: 13 additions & 2 deletions models/mpnn/src/mpnn/loss/nll_loss.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
from typing import Any

import torch
import torch.nn as nn


class LabelSmoothedNLLLoss(nn.Module):
def __init__(self, label_smoothing_eps=0.1, normalization_constant=6000.0):
def __init__(
self,
label_smoothing_eps: float = 0.1,
normalization_constant: float = 6000.0,
) -> None:
"""
Label smoothed negative log likelihood loss for Protein/Ligand MPNN.

Expand All @@ -20,7 +26,12 @@ def __init__(self, label_smoothing_eps=0.1, normalization_constant=6000.0):
self.label_smoothing_eps = label_smoothing_eps
self.normalization_constant = normalization_constant

def forward(self, network_input, network_output, loss_input):
def forward(
self,
network_input: dict[str, Any],
network_output: dict[str, Any],
loss_input: dict[str, Any],
) -> tuple[torch.Tensor, dict[str, torch.Tensor]]:
"""
Given the network_input (same as input_features to the model), network
output, and loss input, compute the loss.
Expand Down
91 changes: 71 additions & 20 deletions models/mpnn/src/mpnn/metrics/nll.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import Any

import torch
from atomworks.ml.transforms.base import ConvertToTorch
from mpnn.collate.feature_collator import FeatureCollator
Expand All @@ -22,10 +24,10 @@ class NLL(Metric):

def __init__(
self,
return_per_example_metrics=False,
return_per_residue_metrics=False,
**kwargs,
):
return_per_example_metrics: bool = False,
return_per_residue_metrics: bool = False,
**kwargs: Any,
) -> None:
"""
Initialize the NLL metric.

Expand All @@ -42,7 +44,7 @@ def __init__(
self.return_per_residue_metrics = return_per_residue_metrics

@property
def kwargs_to_compute_args(self):
def kwargs_to_compute_args(self) -> dict[str, Any]:
"""
Map input keys to the compute method arguments.

Expand All @@ -56,7 +58,9 @@ def kwargs_to_compute_args(self):
"mask_for_loss": ("network_output", "input_features", "mask_for_loss"),
}

def get_per_residue_mask(self, mask_for_loss, **kwargs):
def get_per_residue_mask(
self, mask_for_loss: torch.Tensor, **kwargs: Any
) -> torch.Tensor:
"""
Get the per-residue mask for computing NLL.

Expand All @@ -73,7 +77,9 @@ def get_per_residue_mask(self, mask_for_loss, **kwargs):
per_residue_mask = mask_for_loss
return per_residue_mask

def compute_nll_metrics(self, S, log_probs, per_residue_mask):
def compute_nll_metrics(
self, S: torch.Tensor, log_probs: torch.Tensor, per_residue_mask: torch.Tensor
) -> dict[str, torch.Tensor]:
"""
Compute NLL and perplexity metrics using the provided per-residue mask.
Args:
Expand Down Expand Up @@ -143,7 +149,15 @@ def compute_nll_metrics(self, S, log_probs, per_residue_mask):
}
return nll_dict

def compute(self, log_probs, S, mask_for_loss, **kwargs):
# MetricManager introspects the base ``Metric.compute(**kwargs)`` and dispatches by the
# names in ``kwargs_to_compute_args``, so declaring explicit params here is by design.
def compute( # type: ignore[override]
self,
log_probs: torch.Tensor,
S: torch.Tensor,
mask_for_loss: torch.Tensor,
**kwargs: Any,
) -> dict[str, Any]:
"""
Compute the negative log likelihood (NLL) and perplexity, meaned
across all residues that are included in the loss calculation.
Expand Down Expand Up @@ -182,7 +196,7 @@ def compute(self, log_probs, S, mask_for_loss, **kwargs):
nll_metrics = self.compute_nll_metrics(S, log_probs, per_residue_mask)

# Prepare the metric dictionary.
metric_dict = {
metric_dict: dict[str, Any] = {
"mean_nll": nll_metrics["mean_nll"].detach().item(),
"mean_perplexity": nll_metrics["mean_perplexity"].detach().item(),
}
Expand Down Expand Up @@ -220,8 +234,8 @@ def __init__(
interface_distance_threshold: float = 5.0,
return_per_example_metrics: bool = False,
return_per_residue_metrics: bool = False,
**kwargs,
):
**kwargs: Any,
) -> None:
"""
Initialize the InterfaceNLL metric.

Expand All @@ -244,7 +258,7 @@ def __init__(
self.interface_distance_threshold = interface_distance_threshold

@property
def kwargs_to_compute_args(self):
def kwargs_to_compute_args(self) -> dict[str, Any]:
"""
Map input keys to the compute method arguments.

Expand All @@ -257,7 +271,9 @@ def kwargs_to_compute_args(self):
args_mapping["atom_array"] = ("network_input", "atom_array")
return args_mapping

def get_per_residue_mask(self, mask_for_loss, **kwargs):
def get_per_residue_mask(
self, mask_for_loss: torch.Tensor, **kwargs: Any
) -> torch.Tensor:
"""
Get the per-residue mask for computing interface NLL.

Expand Down Expand Up @@ -334,7 +350,16 @@ def get_per_residue_mask(self, mask_for_loss, **kwargs):

return combined_mask

def compute(self, log_probs, S, mask_for_loss, atom_array, **kwargs):
# MetricManager introspects the base ``Metric.compute(**kwargs)`` and dispatches by the
# names in ``kwargs_to_compute_args``, so declaring explicit params here is by design.
def compute( # type: ignore[override]
self,
log_probs: torch.Tensor,
S: torch.Tensor,
mask_for_loss: torch.Tensor,
atom_array: Any,
**kwargs: Any,
) -> dict[str, Any]:
"""
Compute the interface negative log likelihood (NLL) and perplexity,
averaged across interface residues only.
Expand Down Expand Up @@ -379,7 +404,7 @@ class SampledNLL(NLL):
"""

@property
def kwargs_to_compute_args(self):
def kwargs_to_compute_args(self) -> dict[str, Any]:
# Build the mapping explicitly: score the *sampled* sequence
# (`S_sampled`) using the raw `logits`.
return {
Expand All @@ -388,7 +413,13 @@ def kwargs_to_compute_args(self):
"mask_for_loss": ("network_output", "input_features", "mask_for_loss"),
}

def compute(self, logits, S, mask_for_loss, **kwargs):
def compute( # type: ignore[override]
self,
logits: torch.Tensor,
S: torch.Tensor,
mask_for_loss: torch.Tensor,
**kwargs: Any,
) -> dict[str, Any]:
"""Convert raw logits to log-probabilities and delegate to ``NLL``.

Args:
Expand Down Expand Up @@ -417,7 +448,7 @@ class SampledLigandInterfaceNLL(InterfaceNLL):
"""

@property
def kwargs_to_compute_args(self):
def kwargs_to_compute_args(self) -> dict[str, Any]:
# Build the mapping explicitly: score the *sampled* sequence
# (`S_sampled`) using the raw `logits`; `atom_array` derives the
# polymer-ligand interface mask.
Expand All @@ -428,7 +459,14 @@ def kwargs_to_compute_args(self):
"atom_array": ("network_input", "atom_array"),
}

def compute(self, logits, S, mask_for_loss, atom_array, **kwargs):
def compute( # type: ignore[override]
self,
logits: torch.Tensor,
S: torch.Tensor,
mask_for_loss: torch.Tensor,
atom_array: Any,
**kwargs: Any,
) -> dict[str, Any]:
"""Convert raw logits to log-probabilities and delegate to ``InterfaceNLL``.

Args:
Expand Down Expand Up @@ -463,7 +501,13 @@ class MPNNConfidence(SampledNLL):
in ``(0, 1]``; higher means the model is more confident in the sequence.
"""

def compute(self, logits, S, mask_for_loss, **kwargs):
def compute( # type: ignore[override]
self,
logits: torch.Tensor,
S: torch.Tensor,
mask_for_loss: torch.Tensor,
**kwargs: Any,
) -> dict[str, Any]:
"""Compute the sampled-sequence NLL and derived confidence.

Args:
Expand Down Expand Up @@ -501,7 +545,14 @@ class MPNNLigandInterfaceConfidence(SampledLigandInterfaceNLL):
prefixed keys).
"""

def compute(self, logits, S, mask_for_loss, atom_array, **kwargs):
def compute( # type: ignore[override]
self,
logits: torch.Tensor,
S: torch.Tensor,
mask_for_loss: torch.Tensor,
atom_array: Any,
**kwargs: Any,
) -> dict[str, Any]:
"""Compute the interface NLL and derived interface confidence.

Args:
Expand Down
Loading
Loading