Metadata-Version: 2.4
Name: clamnp
Version: 0.1.0
Summary: CLaM-NP: natural-product-likeness scoring with SMILES language models
Project-URL: Repository, https://github.com/ohuelab/clamnp
Author-email: kohbanye <kohbanye@gmail.com>
License: MIT
License-File: LICENSE
Keywords: SMILES,cheminformatics,drug discovery,language model,natural products
Requires-Python: >=3.12
Requires-Dist: huggingface-hub>=0.20
Requires-Dist: numpy>=1.26
Requires-Dist: rdkit>=2025.3.5
Requires-Dist: torch>=2.7.1
Requires-Dist: tqdm>=4.66
Requires-Dist: transformers>=4.55.0
Requires-Dist: typer>=0.12
Provides-Extra: train
Requires-Dist: lightning>=2.5.2; extra == 'train'
Requires-Dist: molvs>=0.1.1; extra == 'train'
Requires-Dist: pandas>=2.3.3; extra == 'train'
Requires-Dist: requests>=2.32.4; extra == 'train'
Requires-Dist: scikit-learn>=1.7.2; extra == 'train'
Requires-Dist: wandb>=0.21.1; extra == 'train'
Provides-Extra: viz
Requires-Dist: adjusttext>=1.3.0; extra == 'viz'
Requires-Dist: ipykernel>=6.30.1; extra == 'viz'
Requires-Dist: matplotlib>=3.10.7; extra == 'viz'
Requires-Dist: openpyxl>=3.1.5; extra == 'viz'
Requires-Dist: pandas>=2.3.3; extra == 'viz'
Requires-Dist: seaborn>=0.13.2; extra == 'viz'
Requires-Dist: tensorboard>=2.20.0; extra == 'viz'
Requires-Dist: umap-learn>=0.5.9.post2; extra == 'viz'
Description-Content-Type: text/markdown

# CLaM-NP Score

**C**hemical **La**nguage **M**odels for **N**atural **P**roduct-Likeness Score.

CLaM-NP scores how "natural-product-like" a molecule is by comparing the
likelihood of its SMILES string under decoder-only language models trained on
different chemical spaces. A molecule that a natural-product model finds likely
but a synthetic model finds unlikely gets a high score.

```python
from clamnp import CLaMNPScorer

scorer = CLaMNPScorer.from_pretrained()      # downloads models from the HF Hub
scorer.score("CC(=O)Oc1ccccc1C(=O)O")            # aspirin  -> low (synthetic-like)
scorer.score("CN1CCC[C@H]1c1cccnc1")             # nicotine -> high (natural-like)
```

## How it works

Each model is a small GPT-2 causal LM trained on SMILES. The perplexity
of a SMILES under a model measures how well that molecule fits the model's
training distribution. The **CLaM-NP Score** is a log-likelihood ratio:

- **`stabilized`** (default, "CLaM-NP Score"):

  ```
  score = log P(x | natural) − log( α·P(x | synthetic) + (1−α)·P(x | general) )
  ```

  where `α` is a per-molecule gating weight and `general` is a model trained on
  a broad bioactive set that stabilizes the denominator.

- **`unstabilized`** ("CLaM-NP Score (unstabilized)"):

  ```
  score = log P(x | natural) − log P(x | synthetic)
  ```

Higher = more natural-product-like. See `clamnp/scorer.py` for the exact math.

## Installation

Inference only (lightweight):

```bash
pip install clamnp                 # from PyPI (once published)
pip install git+https://github.com/ohuelab/clamnp
```

With [uv](https://docs.astral.sh/uv/), no install needed:

```bash
uvx --from clamnp clamnp "CC(=O)Oc1ccccc1C(=O)O"
```

Extras:

| Extra   | For                                  | Install                     |
|---------|--------------------------------------|-----------------------------|
| `train` | Training models, dataset preparation | `pip install clamnp[train]` |
| `viz`   | Notebooks & paper figures            | `pip install clamnp[viz]`   |

For local development on this repo, `uv sync` installs everything (the `dev`
dependency group bundles all extras).

## Usage

### Command line

```bash
# Single molecule (models auto-downloaded and cached on first run)
clamnp "CC(=O)Oc1ccccc1C(=O)O"

# Several molecules
clamnp "CN1C=NC2=C1C(=O)N(C(=O)N2C)C" "O=C(O)c1ccccc1O"

# A file (one SMILES per line) -> CSV, with normalized [0,1] scores
clamnp --input molecules.smi --output scores.csv --normalized

# Unstabilized (2-model) score, on CPU
clamnp --mode unstabilized --device cpu "CC(=O)Oc1ccccc1C(=O)O"
```

### Python

```python
from clamnp import CLaMNPScorer

scorer = CLaMNPScorer.from_pretrained(scoring_mode="stabilized")

scorer.score("CN1CCC[C@H]1c1cccnc1")             # raw score
scorer.score_normalized("CN1CCC[C@H]1c1cccnc1")  # sigmoid-normalized to [0, 1]
scorer.batch_score(["CCO", "c1ccccc1"])          # list of scores
scorer.score_with_details("CCO")                 # full breakdown (log P, perplexity, α, ...)
```

## Models

Models are hosted on the Hugging Face Hub at
[`kohbanye/clamnp`](https://huggingface.co/kohbanye/clamnp), with one subfolder
per model (`natural`, `synthetic`, `general`). The shared SMILES tokenizer is
[`kohbanye/SmilesTokenizer_PubChem_1M`](https://huggingface.co/kohbanye/SmilesTokenizer_PubChem_1M).

## License

MIT — see [LICENSE](LICENSE).
