Metadata-Version: 2.5
Name: sabr-kit
Version: 0.5.3
Summary: Structure-based Antibody Renumbering
Project-URL: Homepage, https://github.com/delalamo/sabr
Project-URL: Issues, https://github.com/delalamo/sabr/issues
Author: Diego del Alamo
License: MIT License
        
        Copyright (c) 2025 Diego del Alamo
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
License-File: LICENSE
Requires-Python: >=3.11
Requires-Dist: biopython<2,>=1.87
Requires-Dist: click<9,>=8.4.2
Requires-Dist: numpy<3,>=2.4.6
Requires-Dist: scipy<2,>=1.17.1
Provides-Extra: test
Requires-Dist: coverage<8,>=7.15.0; extra == 'test'
Requires-Dist: pre-commit<5,>=4.6.0; extra == 'test'
Requires-Dist: pytest-cov<8,>=7.1.0; extra == 'test'
Requires-Dist: pytest<10,>=9.1.1; extra == 'test'
Description-Content-Type: text/markdown

# Structure-based Antibody Renumbering

[![PyPI](https://img.shields.io/pypi/v/sabr-kit)](https://pypi.org/project/sabr-kit/)
[![Python ≥3.11](https://img.shields.io/badge/python-%E2%89%A53.11-blue?logo=python&logoColor=white)](https://pypi.org/project/sabr-kit/)
[![Python checks](https://github.com/delalamo/SAbR/actions/workflows/test.yml/badge.svg?branch=main)](https://github.com/delalamo/SAbR/actions/workflows/test.yml)
[![Documentation](https://readthedocs.org/projects/sabr/badge/?version=latest)](https://sabr.readthedocs.io/)
[![License: MIT](https://img.shields.io/badge/license-MIT-blue)](LICENSE)
[![Docker build](https://github.com/delalamo/SAbR/actions/workflows/docker-build.yml/badge.svg?event=push)](https://github.com/delalamo/SAbR/actions/workflows/docker-build.yml?query=event%3Apush)
[![PyPI downloads](https://img.shields.io/pypi/dm/sabr-kit)](https://pypi.org/project/sabr-kit/)

SAbR (Structure-based Antibody Renumbering) assigns antibody residue numbers
from backbone coordinates. It combines a fine-tuned version of the ProteinMPNN
encoder from [SoftAlign](https://github.com/jtrinquier/SoftAlign) with the
original affine Smith–Waterman alignment and ANARCI numbering rules.

The complete usage guide is available in the
[SAbR documentation](https://sabr.readthedocs.io/).

## Installation

SAbR requires Python 3.11 or newer. Both `sabr` and `softalign` modes run
on CPU using NumPy and SciPy

```bash
pip install sabr-kit
```

### Docker

With Docker installed and running, pull the image and run SAbR from the
directory containing `antibody.pdb`:

```bash
docker pull ghcr.io/delalamo/sabr:latest
docker run --rm -v "$PWD:/workspace" ghcr.io/delalamo/sabr:latest \
  -i antibody.pdb -c H -o numbered.pdb
```

The mounted directory makes the input available inside the container and
saves `numbered.pdb` back to the same directory on your computer. The image
runs `sabr` directly; append any of the command-line options below, or use
`docker run --rm ghcr.io/delalamo/sabr:latest --help` for help. To pin a
release, replace `latest` with its version tag (without the leading `v`).

## Command line

```bash
sabr -i antibody.pdb -c H -o numbered.pdb
```

The complete interface is:

```text
sabr -i INPUT -c CHAIN -o OUTPUT
     [-n imgt|chothia|kabat|martin|aho|wolfguy]
     [-t auto|TYPES]
     [--noise-level 0.0|0.2|0.5|1.0|2.0]
     [-m sabr|softalign]
     [--residue-range START END]
     [--scfv]
     [--no-mmcif]
     [--overwrite] [-v]
```

Defaults are IMGT numbering, automatic H/K/L selection, noise level `0.0`,
`sabr` mode, and the entire selected chain. `--chain-type` accepts a
comma-separated set of candidates. Each candidate is a sequence of `H`, `K`,
and `L` domains: `H,K` tries heavy and kappa single domains, `HK,HL` tries
heavy-kappa and heavy-lambda two-domain chains, and `HHK,HHL` tries the
corresponding three-domain chains. Existing outputs are never replaced unless
`--overwrite` is given. Normal output contains only warnings and errors; `-v`
reports reference scores and pipeline decisions.

Use `--mode softalign` to select the original SoftAlign encoder weights,
reference embeddings, and affine gap penalties together. SoftAlign references
do not vary with `--noise-level`, so that option is ignored in this mode.

Use `--scfv` to search only the scFv candidate set. It is equivalent to
`--chain-type HK,HL,KH,LH` and still requires the default automatic chain type
when used as a flag. Each domain is numbered in the selected scheme, then
successive domains receive offsets of `0`, `1000`, `2000`, and so on. For
IMGT, this gives domain ranges `1–128`, `1001–1128`, `2001–2128`, and so on.
Linker residues continue sequentially from the preceding domain's last number.
Multi-domain references use the selected parameter mode, so either form can be
combined with `--mode softalign`.

Input and output may be PDB (`.pdb`) or mmCIF (`.cif` or `.mmcif`). When a
requested PDB output needs multi-character insertion codes, SAbR warns and
automatically writes it to the corresponding `.cif` path instead. Pass
`--no-mmcif` to forbid this conversion and fail. Other values that exceed PDB
field limits still require an explicitly named mmCIF output. Writes are atomic,
so a failed run does not leave a partial output.

CLI conversion guarantees preservation of atomic structure content, not
arbitrary non-atomic mmCIF categories. It warns for every mmCIF input.

## Python API

```python
from Bio.PDB import PDBParser
from sabr import renumber_structure

structure = PDBParser(PERMISSIVE=False, QUIET=True).get_structure(
    "antibody", "antibody.pdb"
)
numbered = renumber_structure(structure, chain="H")
```

Strict PDB parsing matches the CLI and rejects malformed duplicate atom
records before renumbering.

`renumber_structure` accepts a Biopython `Structure`, never mutates its input,
and returns a new Biopython `Structure`. Non-target chains, hetero residues,
waters, and residues outside an inclusive `residue_range` are preserved. SAbR
rejects multi-model structures rather than silently modifying only one model.
If a partial range would create duplicate residue IDs with unchanged residues,
the operation fails with an explanation.

For unusually long loops that need extended insertion codes, use mmCIF output.

IMGT, Kabat, Chothia, Martin, and AHo numbering support up to 5,000 insertion
codes per anchor (`A`–`Z`, `AA`–`ZZ`, then three-letter codes). Wolfguy retains
its fixed loop positions: 49 residues per loop, except heavy-chain CDR3
(51). Unsupported lengths raise an error with the scheme, loop, observed
length, and supported limit. These are SAbR implementation limits; mmCIF
does not remove them or the 1,024-residue structure-selection limit. See
[long-CDR numbering support](docs/index.md#long-cdr-numbering-support) for
the complete scheme-specific limits.

## Scientific behavior

- The default `sabr` mode preserves the trained SAbR encoder weights,
  references, and gap penalties unchanged.
- The optional `softalign` mode uses `softalign_encoder.npz`,
  `softalign_embeddings.npz`, and the exact penalties in
  `softalign_gap.npz` as one parameter set.
- Alignment uses the original affine Smith–Waterman recurrence, with soft
  assignments computed by an explicit NumPy reverse pass.
- Deterministic CDR gap distribution and DE-loop correction are always
  applied. Between IMGT anchors 79 and 85, DE-loop residues fill 80 first,
  then 84 back through 81; additional residues are inserted after 82.
- Automatic chain selection aligns against H, K, and L references and uses
  the highest score, with deterministic H/K/L tie order.
- `chain_type` candidate order is deterministic and resolves score ties.
- scFv mode searches only the `HK,HL,KH,LH` candidate list in that order.
- Multi-domain candidates do not apply gap-open or gap-extension costs to
  query linker residues aligned at any boundary between domain references.
- Multi-domain candidates receive normal affine gap-open and gap-extension
  costs for unaligned query and reference termini when their selection scores
  are compared; the underlying alignments and raw alignment scores are
  unchanged.

A structural gap is detected when the C–N distance between consecutive
residues exceeds 2.66 Å. SAbR handles gaps automatically by waiving
reference-deletion opening costs at each detected boundary; extension
costs remain unchanged. For CDR and DE-loop numbering, SAbR keeps the
structural anchors and counts unresolved residues declared in PDB REMARK 465
or mmCIF `_pdbx_unobs_or_zero_occ_residues` metadata between them. These
residues occupy sequence positions through scheme conversion, including
insertion codes, but no atoms are created. Author-number jumps alone are
never treated as missing sequence. Regions with missing anchors or breaks
not explained by metadata retain the existing alignment fallback; numbering
in those regions can still be inaccurate.

The CLI reads this metadata automatically with Biopython's `MMCIF2Dict`.
For in-memory mmCIF inputs, use the standard `MMCIFParser` and supply the
metadata dictionary explicitly (using the default author chain/residue IDs):

```python
from Bio.PDB import MMCIFParser
from Bio.PDB.MMCIF2Dict import MMCIF2Dict
from sabr import renumber_structure

structure = MMCIFParser(QUIET=True).get_structure("input", "input.cif")
numbered = renumber_structure(
    structure, chain="H", mmcif_dict=MMCIF2Dict("input.cif")
)
```

PDB inputs from `Bio.PDB.PDBParser` already retain REMARK 465 in their
headers. Explicit `mmcif_dict` data takes precedence over header records;
it must describe the input structure's author IDs. Neither input is modified.
The returned structure drops missing-residue header records for the renumbered
chain because their original author IDs are no longer valid.

T-cell receptors are not an officially supported SAbR target. For
experimental use, pass the actual TCR chain type (`A`, `B`, `G`, or `D`) with
`--chain-type` or `chain_type`. SAbR aligns only against the K reference,
which includes IMGT position 10 as TCRs do, while retaining the TCR type for
ANARCI conversion. This workflow is limited to IMGT and AHo numbering; the
other bundled schemes are antibody-specific.

## Development

```bash
pip install -c constraints.txt -e '.[test]'
pytest
pre-commit run --all-files
```

`constraints.txt` records the exact canonical development and CI environment.
Package metadata remains ranged for normal installation.

The committed tests are self-contained and never download data. They verify
the fixed asset hashes, encoder and alignment baselines, all numbering schemes,
H/K/L selection, regional corrections, structure-object behavior, and CLI
failure handling.

Independent antibody structure tests use PDB fixtures and SAbDab IMGT labels
in `tests/data/antibody`. They check residue numbering, domain boundaries,
coordinate and input-numbering invariance, and real scFv behavior.

## License and attribution

SAbR is distributed under the repository license. The vendored ANARCI
numbering code retains its original license in `src/sabr/_anarci/LICENSE`.

If you use SAbR, please cite:
```bibtex
@article{delAlamo2026,
  title = {Structure-based Antibody Renumbering},
  url = {http://dx.doi.org/10.64898/2026.09.16.751788},
  DOI = {10.64898/2026.09.16.751788},
  publisher = {openRxiv},
  author = {del Alamo,  Diego},
  year = {2026},
  month = Sept 
}
```
