Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
101 changes: 101 additions & 0 deletions autopeptideml/reps/fps.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,55 @@
raise ImportError("You need to install rdkit to use this method.",
" Try: `pip install rdkit`")

# Mapping from lowercase string keys to skfp fingerprint classes.
# Populated lazily on first use so that importing this module does not require
# scikit-fingerprints to be installed.
_SKFP_CLASS_MAP: Optional[Dict[str, Any]] = None


def _get_skfp_class_map() -> Dict[str, Any]:
global _SKFP_CLASS_MAP
if _SKFP_CLASS_MAP is not None:
return _SKFP_CLASS_MAP
try:
import skfp.fingerprints as _sfp
except ImportError:
raise ImportError(
"You need to install scikit-fingerprints to use skfp-backed fingerprints. "
"Try: `pip install scikit-fingerprints`"
)
_SKFP_CLASS_MAP = {
'atompair': _sfp.AtomPairFingerprint,
'autocorr': _sfp.AutocorrFingerprint,
'avalon': _sfp.AvalonFingerprint,
'bcut2d': _sfp.BCUT2DFingerprint,
'ecfp': _sfp.ECFPFingerprint,
'erg': _sfp.ERGFingerprint,
'estate': _sfp.EStateFingerprint,
'functionalgroups': _sfp.FunctionalGroupsFingerprint,
'ghosecrippen': _sfp.GhoseCrippenFingerprint,
'klekotaroth': _sfp.KlekotaRothFingerprint,
'laggner': _sfp.LaggnerFingerprint,
'layered': _sfp.LayeredFingerprint,
'lingo': _sfp.LingoFingerprint,
'maccs': _sfp.MACCSFingerprint,
'map': _sfp.MAPFingerprint,
'mhfp': _sfp.MHFPFingerprint,
'mordred': _sfp.MordredFingerprint,
'mqns': _sfp.MQNsFingerprint,
'pattern': _sfp.PatternFingerprint,
'pharmacophore': _sfp.PharmacophoreFingerprint,
'pubchem': _sfp.PubChemFingerprint,
'rdkit': _sfp.RDKitFingerprint,
'rdkit2d': _sfp.RDKit2DDescriptorsFingerprint,
'secfp': _sfp.SECFPFingerprint,
'topologicaltorsion': _sfp.TopologicalTorsionFingerprint,
'usr': _sfp.USRFingerprint,
'usrcat': _sfp.USRCATFingerprint,
'vsa': _sfp.VSAFingerprint,
}
return _SKFP_CLASS_MAP


class PepFunn_Generator:
radius: int = 2
Expand Down Expand Up @@ -168,3 +217,55 @@ def dim(self) -> int:
:return: The number of bits in the fingerprint (i.e., `nbits`).
"""
return self.nbits


class RepEngineSkfp(RepEngineBase):
"""
Wraps any `scikit-fingerprints` (skfp) fingerprint class as a
`RepEngineBase`-compatible engine.

Attributes:
:type engine: str
:param engine: Fixed to ``'skfp'``.

:type name: str
:param name: ``'skfp-<rep>'``, e.g. ``'skfp-maccs'``.

:type generator: BaseFingerprintTransformer
:param generator: The underlying skfp transformer instance.
"""
engine = 'skfp'

def __init__(self, rep: str, **kwargs):
"""
:type rep: str
:param rep: Lowercase fingerprint key, e.g. ``'maccs'``, ``'ecfp'``,
``'atompair'``. See :func:`_get_skfp_class_map` for all valid keys.

:type **kwargs: dict
:param **kwargs: Forwarded verbatim to the skfp fingerprint constructor
(e.g. ``fp_size``, ``radius``, ``count``).
"""
super().__init__(rep, **kwargs)
self.generator = self._load_generator(rep, **kwargs)
self.name = f'{self.engine}-{rep}'

def _load_generator(self, rep: str, **kwargs):
class_map = _get_skfp_class_map()
key = rep.lower()
if key not in class_map:
raise NotImplementedError(
f"skfp fingerprint '{rep}' is not supported. "
f"Valid keys: {sorted(class_map)}"
)
return class_map[key](**kwargs)

def _preprocess_batch(self, batch: List[str]) -> List[str]:
# skfp accepts SMILES strings directly; no preprocessing needed.
return list(batch)

def _rep_batch(self, batch: List[str]) -> np.ndarray:
return self.generator.transform(batch)

def dim(self) -> int:
return int(self.generator.n_features_out)
1 change: 1 addition & 0 deletions docs/repenginebase.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ Apply any necessary preprocessing to a batch before representation computation (
| Class | Module | Description |
|---|---|---|
| [`RepEngineFP`](repenginefp.md) | `autopeptideml.reps.fps` | Molecular fingerprints via RDKit (ECFP, FCFP, PepFuNN). |
| [`RepEngineSkfp`](repengineskfp.md) | `autopeptideml.reps.fps` | 28 fingerprint families via scikit-fingerprints (MACCS, AtomPair, Avalon, PubChem, Mordred, …). |
| [`RepEngineLM`](repenginelm.md) | `autopeptideml.reps.lms` | Pre-trained language model embeddings (ESM2, ProtT5, MoLFormer, …). |
| [`RepEngineOnehot`](repengineseqbased.md) | `autopeptideml.reps.seq_based` | Fixed-length one-hot encoding for canonical amino acid sequences. |

Expand Down
171 changes: 171 additions & 0 deletions docs/repengineskfp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
# `RepEngineSkfp` — scikit-fingerprints Engine

**Module:** `autopeptideml.reps.fps`
**Inherits from:** [`RepEngineBase`](repenginebase.md)

## Overview

`RepEngineSkfp` wraps any [scikit-fingerprints](https://scikit-fingerprints.github.io/scikit-fingerprints/) (`skfp`) fingerprint class as a drop-in `RepEngineBase`-compatible engine. It gives access to 28 fingerprint families beyond the RDKit-backed ones available in [`RepEngineFP`](repenginefp.md), including MACCS, AtomPair, TopologicalTorsion, Avalon, PubChem, Mordred, and more.

**Requires:** `pip install scikit-fingerprints`
(RDKit is also required as a transitive dependency.)

---

## Attributes

| Attribute | Type | Description |
|---|---|---|
| `engine` | `str` | Fixed to `'skfp'`. |
| `name` | `str` | Auto-generated as `'skfp-<rep>'`, e.g. `'skfp-maccs'`. |
| `generator` | `BaseFingerprintTransformer` | The underlying skfp transformer instance. |

---

## Constructor

```python
RepEngineSkfp(rep: str, **kwargs)
```

| Parameter | Type | Description |
|---|---|---|
| `rep` | `str` | Lowercase fingerprint key (see [Supported fingerprints](#supported-fingerprints)). |
| `**kwargs` | `Any` | Forwarded verbatim to the skfp fingerprint constructor (e.g. `fp_size`, `radius`, `count`). |

---

## Methods

### `compute_reps` *(inherited)*

```python
compute_reps(
mols: List[str],
verbose: bool = False,
batch_size: int = 12
) -> np.ndarray
```

Compute fingerprints for a list of SMILES strings. Returns an array of shape `(n_mols, dim)`.

---

### `dim`

```python
dim() -> int
```

Returns `generator.n_features_out` — the feature dimensionality reported by the underlying skfp transformer.

---

### `_preprocess_batch`

```python
_preprocess_batch(batch: List[str]) -> List[str]
```

Returns the batch unchanged. `skfp` transformers accept SMILES strings directly and handle `Mol` conversion internally.

---

### `_rep_batch`

```python
_rep_batch(batch: List[str]) -> np.ndarray
```

Delegates to `generator.transform(batch)`. Returns a dense `np.ndarray` of shape `(len(batch), dim)`.

---

### `_load_generator`

```python
_load_generator(rep: str, **kwargs) -> BaseFingerprintTransformer
```

Looks up `rep` in the internal class map and instantiates the matching skfp class with `**kwargs`. Raises `NotImplementedError` for unknown keys.

---

## Supported fingerprints

| Key | skfp class | Fixed `dim` | Notes |
|---|---|---|---|
| `atompair` | `AtomPairFingerprint` | `fp_size` | Hashed atom-pair counts |
| `autocorr` | `AutocorrFingerprint` | 192 | 2D autocorrelation descriptors |
| `avalon` | `AvalonFingerprint` | `fp_size` | Avalon substructure fingerprint |
| `bcut2d` | `BCUT2DFingerprint` | 64 | BCUT2D descriptors |
| `ecfp` | `ECFPFingerprint` | `fp_size` | Extended connectivity (Morgan); pass `use_pharmacophoric_invariants=True` for FCFP |
| `erg` | `ERGFingerprint` | 315 | Extended reduced graph |
| `estate` | `EStateFingerprint` | 79 | Electrotopological state |
| `functionalgroups` | `FunctionalGroupsFingerprint` | 85 | Functional group presence |
| `ghosecrippen` | `GhoseCrippenFingerprint` | 110 | Ghose-Crippen atom types |
| `klekotaroth` | `KlekotaRothFingerprint` | `fp_size` | Klekota-Roth substructure |
| `laggner` | `LaggnerFingerprint` | 307 | Laggner substructure |
| `layered` | `LayeredFingerprint` | `fp_size` | RDKit layered fingerprint |
| `lingo` | `LingoFingerprint` | `fp_size` | SMILES n-gram similarity |
| `maccs` | `MACCSFingerprint` | 166 | MACCS structural keys |
| `map` | `MAPFingerprint` | `fp_size` | MinHashed atom-pair |
| `mhfp` | `MHFPFingerprint` | `fp_size` | MinHashed fingerprint |
| `mordred` | `MordredFingerprint` | 1613 | Mordred 2D descriptors |
| `mqns` | `MQNsFingerprint` | 42 | Molecular quantum numbers |
| `pattern` | `PatternFingerprint` | `fp_size` | RDKit pattern fingerprint |
| `pharmacophore` | `PharmacophoreFingerprint` | `fp_size` | 2D pharmacophore |
| `pubchem` | `PubChemFingerprint` | 881 | PubChem substructure keys |
| `rdkit` | `RDKitFingerprint` | `fp_size` | RDKit path fingerprint |
| `rdkit2d` | `RDKit2DDescriptorsFingerprint` | 200 | RDKit 2D descriptors |
| `secfp` | `SECFPFingerprint` | `fp_size` | SMILES extended connectivity |
| `topologicaltorsion` | `TopologicalTorsionFingerprint` | `fp_size` | Topological torsion |
| `usr` | `USRFingerprint` | 12 | Ultrafast shape recognition (3D) |
| `usrcat` | `USRCATFingerprint` | 60 | USR + CREDO atom types (3D) |
| `vsa` | `VSAFingerprint` | 71 | Van der Waals surface area bins |

> **3D fingerprints** (`usr`, `usrcat`) require molecules with pre-computed conformations. Pass RDKit `Mol` objects with the `conf_id` property set rather than bare SMILES strings.

---

## Examples

### MACCS keys (fixed 166-bit)

```python
from autopeptideml.reps.fps import RepEngineSkfp

engine = RepEngineSkfp('maccs')
smiles = [
'C[C@H](N)C(=O)N[C@@H](CCCNC(=N)N)C(=O)NCC(=O)O', # Ala-Arg-Gly
'N[C@@H](Cc1ccccc1)C(=O)N[C@@H](CS)C(=O)O', # Phe-Cys
]
X = engine.compute_reps(smiles)
print(X.shape) # (2, 166)
print(engine.dim()) # 166
```

### ECFP via skfp (variable bit size, count variant)

```python
engine = RepEngineSkfp('ecfp', fp_size=2048, radius=3, count=True)
X = engine.compute_reps(smiles)
print(X.shape) # (2, 2048)
print(engine.dim()) # 2048
```

### AtomPair fingerprint

```python
engine = RepEngineSkfp('atompair', fp_size=512)
X = engine.compute_reps(smiles)
print(X.shape) # (2, 512)
```

---

## Notes

- `RepEngineSkfp` and [`RepEngineFP`](repenginefp.md) both live in `autopeptideml.reps.fps` and share the same `RepEngineBase` interface.
- For ECFP / FCFP via RDKit (the existing path in `build_models`), continue to use `RepEngineFP`. `RepEngineSkfp('ecfp', ...)` is an independent implementation backed by scikit-fingerprints.
- The class map is populated lazily: importing `autopeptideml.reps.fps` does **not** require `scikit-fingerprints` to be installed until `RepEngineSkfp` is actually instantiated.
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ nav:
- Representations:
- RepEngineBase: repenginebase.md
- RepEngineFP: repenginefp.md
- RepEngineSkfp: repengineskfp.md
- RepEngineLM: repenginelm.md
- RepEngineOnehot: repengineseqbased.md
- Pipeline: pipeline.md
Expand Down
Loading