YAML Metadata Warning:empty or missing yaml metadata in repo card

Check out the documentation for more information.

TD3B: Transition-Directed Discrete Diffusion for Allosteric Binder Generation

arXiv

Screenshot 2026-05-10 at 6.52.29β€―PM

TD3B is a sequence-based generative framework that designs peptide binders with specified agonist or antagonist behavior. It combines a Direction Oracle, a soft binding-affinity gate, and amortized fine-tuning of a pre-trained discrete diffusion model (MDLM).

Development release. This repository (ChatterjeeLab/TD3B-dev) carries the full code. The heavy artifacts β€” trained checkpoints, training/test data, and generated binders β€” are distributed as a single archive on Google Drive (see Data and Checkpoints). For the clean code-only release see ChatterjeeLab/TD3B.

Installation

conda env create -f env.yml
conda activate td3b
pip install -e .

Demo

An interactive inference demo is provided in notebooks/TD3B_Inference_Demo.ipynb (configured for a Colab T4 GPU).

Open In Colab

To run it: download the notebook from this repository, open Google Colab, upload it via File β†’ Upload notebook, and select a GPU runtime (Runtime β†’ Change runtime type β†’ T4 GPU).

Data and Checkpoints

The checkpoints, datasets, and generated binders are bundled in a single archive on Google Drive:

Download: td3b_dev_artifacts.zip (~3.4 GB)

Unzip it at the repository root to restore the full layout:

unzip td3b_dev_artifacts.zip -d .
TD3B-dev/
β”œβ”€β”€ checkpoints/
β”‚   β”œβ”€β”€ pretrained.ckpt              # Pre-trained MDLM weights (1.4 GB)
β”‚   β”œβ”€β”€ td3b.ckpt                    # Fine-tuned TD3B model (231 MB)
β”‚   └── direction_oracle.pt          # Direction Oracle weights (2.85 GB)
β”œβ”€β”€ scoring/functions/classifiers/
β”‚   β”œβ”€β”€ binding-affinity.pt          # (from archive)
β”‚   β”œβ”€β”€ permeability-xgboost.json    # (from archive)
β”‚   β”œβ”€β”€ hemolysis-xgboost.json       # (in repo)
β”‚   β”œβ”€β”€ nonfouling-xgboost.json      # (in repo)
β”‚   └── solubility-xgboost.json      # (in repo)
β”œβ”€β”€ data/
β”‚   β”œβ”€β”€ train.csv                    # Training set (target-binder pairs)
β”‚   β”œβ”€β”€ test.csv                     # Test set
β”‚   └── td3b_data_new.csv            # Full labeled target/ligand pairs (agonist/antagonist)
└── generated_binders/
    β”œβ”€β”€ agonist/                     # 1106 generated agonist binders (.pdb + .trb), 249 targets
    └── antagonist.tar.gz            # Generated antagonist binders

Code Structure

TD3B/
β”œβ”€β”€ inference.py                 # Generate binders (main inference entry point)
β”œβ”€β”€ finetune_on_target.py        # Finetune on your own target(s) + generate (Function A)
β”œβ”€β”€ generate_valid.py            # Validity-boosting sampling CLI (Function B)
β”œβ”€β”€ sampling_strategies.py       # Validity-boosting sampler library (Function B)
β”œβ”€β”€ finetune_multi_target.py     # Multi-target TD3B training
β”œβ”€β”€ launch_multi_target.sh       # Training launcher script
β”œβ”€β”€ models/
β”‚   β”œβ”€β”€ diffusion.py             # MDLM backbone (TR2-D2)
β”‚   β”œβ”€β”€ roformer.py              # RoFormer wrapper
β”‚   └── noise_schedule.py        # Noise schedules
β”œβ”€β”€ training/
β”‚   β”œβ”€β”€ finetune_utils.py        # Training utilities
β”‚   └── distributed_utils.py     # Distributed training helpers
β”œβ”€β”€ mcts/
β”‚   └── peptide_mcts.py          # MCTS tree search
β”œβ”€β”€ td3b/
β”‚   β”œβ”€β”€ direction_oracle.py      # Direction Oracle (f_Ο†)
β”‚   β”œβ”€β”€ td3b_scoring.py          # Gated reward R = g_ψ Β· Οƒ(d*Β·(f_Ο†βˆ’0.5)/Ο„)
β”‚   β”œβ”€β”€ td3b_losses.py           # L_WDCE + λ·L_ctr + Ξ²Β·L_KL
β”‚   β”œβ”€β”€ td3b_mcts.py             # TD3B-extended MCTS
β”‚   β”œβ”€β”€ td3b_finetune.py         # Training loop
β”‚   └── data_utils.py            # Data loading utilities
β”œβ”€β”€ scoring/                     # Affinity predictor (g_ψ) and property classifiers
β”œβ”€β”€ baselines/                   # CG, SMC, TDS, PepTune, Unguided baselines
β”œβ”€β”€ tokenizer/                   # SMILES tokenizer (vocab + splits)
β”œβ”€β”€ configs/                     # Model and training configs
└── utils/                       # Misc utilities

Inference

Generate agonist/antagonist binders for target proteins:

python inference.py \
    --ckpt_path checkpoints/td3b.ckpt \
    --val_csv data/test.csv \
    --save_path results/ \
    --seed 42 \
    --num_pool 32 \
    --val_samples_per_target 8 \
    --resample_alpha 0.1

This generates 32 candidates per (target, direction), scores them with the Direction Oracle and affinity predictor, applies Algorithm 2 weighted resampling, and saves only valid peptide samples.

Generation length matters. The length (in SMILES tokens) comes from --seq_length if given, else it is derived from each row's reference binder (correctly tokenized). This released checkpoint reliably produces valid SMILES only at short lengths (≲100 tokens); long binders (e.g. 25–30-residue peptides β‰ˆ 150–210 tokens) yield few or no valid samples regardless of step count. For non-empty output on such targets, pass a shorter --seq_length and a validity-boosting sampler (see below), e.g.:

python inference.py --ckpt_path checkpoints/td3b.ckpt --val_csv data/test.csv \
    --seq_length 50 --sampler best_of_n --best_of_n 6 --num_pool 32 --val_samples_per_target 4

On a real run this yields valid, oracle-scored binders in both directions (antagonist direction accuracy β‰ˆ 1.0).

Output: results/td3b_results_seed42.csv with columns: target, target_uid, sequence, direction_name, target_direction, is_valid, affinity, gated_reward, direction_oracle, direction_accuracy.

Finetune on your own target(s)

finetune_on_target.py takes protein target(s) you supply, finetunes the pretrained TD3B policy on only those target(s), then generates directional (agonist/antagonist) binders for them. It reuses the existing machinery: the finetune half subprocess-invokes finetune_multi_target.py (with K set to the number of targets), and the generation half runs in-process with the same reward, oracle, and Algorithm 2 resampling as inference.py.

python finetune_on_target.py \
    --target_seq MKTAYIAKQRQISFVKSHFSRQLEERLGLIEVQ \
    --target_seq GSHMKELVLALYDYQEKSPREVTMKKGDILTLL \
    --direction both \
    --validity_reward on \
    --num_epochs 20 \
    --num_pool 32 \
    --gen_samples_per_target 8 \
    --device cuda:0 --seed 42

Provide targets by repeating --target_seq and/or via --targets_csv (a CSV with a Target_Sequence column; optional Ligand_Sequence/label rows seed the per-direction length prior):

python finetune_on_target.py --targets_csv my_targets.csv --direction agonist --binder_length 20

Key flags:

  • --target_seq SEQ (repeatable) and/or --targets_csv β€” provide at least one target.
  • --direction {agonist,antagonist,both} (default both) β€” which binders to generate. Finetuning always searches both directions.
  • --validity_reward {on,off} (default on) β€” validity gate applied to both halves. on keeps the is_peptide filter (invalid MCTS children are zero-rewarded during finetuning; only valid peptides are eligible for resampling during generation). off drops the gate on both halves (invalid samples are retained, each row keeps is_valid); the reward itself is always affinity Γ— direction β€” validity is only ever a filter, never part of the reward formula. --finetune_validity_hook {on,off} overrides just the finetune-side gate.
  • --binder_length (default 20) β€” placeholder binder length (residues) used to seed the generation-length prior for any target with no known binder.
  • --skip_finetune (generate directly from --td3b_checkpoint) / --skip_generate (finetune only).
  • Training knobs: --num_epochs (20), --num_iter (10) / --num_children (16) (MCTS), --learning_rate (3e-4), --seq_length (200). Generation knobs: --num_pool (32), --gen_samples_per_target (8), --resample_alpha (0.1), --total_num_steps (128).
  • Paths default to the repo root; override --pretrained_checkpoint, --direction_oracle_ckpt, --output_dir, --device (auto/cpu/cuda:N), --seed as needed.

Output: results/finetune_on_target/binders_<direction>_validity-<on|off>_seed<seed>.csv with columns target, sequence, direction_name, target_direction, is_valid, affinity, gated_reward, direction_oracle, direction_accuracy, gen_length. The finetuned checkpoint is written under results/<run_name>_<timestamp>/ (used automatically for the generation half).

Higher-validity sampling for long binders

As the target length grows, the fraction of decoded SMILES that pass the RDKit peptide check drops. generate_valid.py (backed by sampling_strategies.py) applies sampling-time strategies β€” no retraining β€” to raise the valid-peptide yield, especially at large length, and reports the valid fraction. The strategies only change token selection (temperature / top-k / top-p) and add a remask self-correction loop and best-of-N rejection on top of the model's existing diffusion primitives.

python generate_valid.py \
    --ckpt_path checkpoints/td3b.ckpt \
    --length 400 --num_samples 64 \
    --strategy nucleus_remask \
    --device cuda:0 --seed 42 \
    --save_path results/valid_len400.csv

Key flags:

  • --strategy (default nucleus_remask) β€” one of baseline, more_steps, top_p (a.k.a. nucleus), top_k, low_temp, remask, best_of_n, nucleus_remask. On random-init benchmarks the self-correcting strategies (remask, best_of_n, nucleus_remask) give the largest relative validity gains at length β‰₯ 200; nucleus_remask is the recommended default.
  • --length (default 200, in tokens) and --num_samples (default 64).
  • --ckpt_path β€” TD3B checkpoint. If omitted or missing, a random-init model is used (exercises the sampling machinery only; yields are meaningless β€” pass a real checkpoint for real numbers).
  • Per-strategy overrides (else the preset default is used): --top_p, --top_k, --temperature, --steps_per_token, --remask_rounds, --remask_frac, --remask_steps, --best_of_n, --num_steps.

Output: prints the valid yield (valid / num_samples) with per-round counts, and writes the valid sequences to --save_path (default results/valid_<strategy>_len<L>.csv) with columns idx, sequence, n_chars.

Training

Multi-target TD3B

  1. Edit launch_multi_target.sh only if needed β€” BASE_PATH auto-detects the repo root (this script's own directory), and the checkpoint, data, and oracle paths derive from it:
BASE_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"  # auto-detects the repo root
PRETRAINED_CHECKPOINT="${BASE_PATH}/checkpoints/pretrained.ckpt"
TRAIN_CSV="${BASE_PATH}/data/train.csv"
ORACLE_CKPT="${BASE_PATH}/checkpoints/direction_oracle.pt"

finetune_multi_target.py --base_path also defaults to the repo root, so you only need to override these when your checkpoints or data live elsewhere.

  1. Launch training:
bash launch_multi_target.sh

Key hyperparameters (in launch_multi_target.sh):

  • CONTRASTIVE_WEIGHT=0.1 β€” Ξ» for L_ctr
  • KL_BETA=0.1 β€” Ξ² for L_KL
  • SIGMOID_TEMPERATURE=0.1 β€” Ο„ for gated reward
  • NUM_ITER=20 β€” MCTS iterations per round
  • NUM_CHILDREN=16 β€” Children per MCTS expansion

Baselines

Run baseline methods (CG, SMC, TDS, PepTune, Unguided):

cd baselines/
bash run.sh --baseline cg --device cuda:0
bash run.sh --baseline smc --device cuda:0
bash run.sh --baseline tds --device cuda:0

Citation

@inproceedings{
cao2026tdb,
title={{TD}3B: Transition-Directed Discrete Diffusion for Allosteric Binder Generation},
author={Hanqun Cao and Aastha Pal and Sophia Tang and Yinuo Zhang and Jingjie Zhang and Pheng-Ann Heng and Pranam Chatterjee},
booktitle={Learning Meaningful Representations of Life (LMRL) Workshop at ICLR 2026},
year={2026},
url={https://openreview.net/forum?id=uMUicLylVp}
}
Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support

Paper for ChatterjeeLab/TD3B