File size: 10,043 Bytes
c1616e9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
---
license: apache-2.0
library_name: transformers
pipeline_tag: fill-mask
base_model: kuleshov-group/PlantCaduceus_l28
tags:
- biology
- genomics
- dna
- oryza-sativa
- rice
- variant-effect-prediction
- masked-language-modeling
- caduceus
- mamba
---

# COD PlantCAD for Rice

COD PlantCAD Rice is a single-nucleotide DNA language model for *Oryza sativa*.
It uses the PlantCaduceus l28 architecture and was selected from the continued
rice training run at step 12,000.

The model is a masked language model with bidirectional Mamba blocks and reverse
complement parameter sharing (RCPS). It accepts 512 bp DNA sequences and can be
used for nucleotide logits, sequence representations, and SNV effect scoring.

## Model details

| Property | Value |
|---|---|
| Architecture | Caduceus masked language model |
| Base architecture | PlantCaduceus l28 |
| Release checkpoint | Rice continued-training step 12,000 |
| Layers | 28 |
| Hidden size | 768 per orientation |
| Raw RCPS hidden size | 1,536 |
| Parameters | 112,107,264 |
| Input length | 512 bp |
| Vocabulary | a/c/g/t plus PAD, MASK, and UNK |
| Reference species | *Oryza sativa* |
| Project reference assembly | R498/IRGSP-1.0 (`osa1_r7.asm.chrs.fa`) |
| Weight file | `pytorch_model.bin` (FP32) |

No distillation-state JSON is included in this release.

## Requirements

The custom Caduceus implementation requires PyTorch, Transformers, Mamba-SSM,
and a compatible CUDA/Triton installation. The model uses fused Mamba/Triton
operations and is intended for GPU inference.

The release was tested in the existing environment:

```text
/root/private_data/miniconda3/envs/plantgenoann
Python 3.8.20
PyTorch 2.2.2+cu121
Transformers 4.38.1
Mamba-SSM 1.2.0.post1
NVIDIA RTX 4090
```

Activate that environment without reinstalling dependencies:

```bash
source /root/private_data/miniconda3/bin/activate plantgenoann
```

This repository is public, so download and inference do not require a Hugging
Face token.

## Download

```bash
hf download xxl0001/COD-PlantCAD-Rice \
  --local-dir ./COD-PlantCAD-Rice
```

The examples below can use either the Hub repository ID or the downloaded path.

## Load with Transformers

Use `AutoModel` for sequence representations:

```python
import torch
from transformers import AutoModel, AutoTokenizer

repo_id = "xxl0001/COD-PlantCAD-Rice"
device = torch.device("cuda")

tokenizer = AutoTokenizer.from_pretrained(repo_id, trust_remote_code=True)
model = AutoModel.from_pretrained(repo_id, trust_remote_code=True)
model.to(device).eval()

sequences = ["ACGT" * 128, "TGCA" * 128]
encoded = tokenizer(
    [sequence.lower() for sequence in sequences],
    add_special_tokens=False,
    padding=False,
    truncation=False,
    return_attention_mask=False,
    return_token_type_ids=False,
    return_tensors="pt",
)
input_ids = encoded["input_ids"].to(device)
assert input_ids.shape == (2, 512)

with torch.inference_mode():
    outputs = model(input_ids=input_ids, return_dict=True)

# RCPS concatenates forward and reverse-complement representations.
raw_hidden_states = outputs.last_hidden_state  # [batch, 512, 1536]

# Align and average the two orientations.
hidden_size = raw_hidden_states.shape[-1] // 2
forward = raw_hidden_states[..., :hidden_size]
reverse_complement = raw_hidden_states[..., hidden_size:].flip(dims=(1,))
hidden_states = (forward + reverse_complement) / 2  # [batch, 512, 768]
```

Use `AutoModelForMaskedLM` when nucleotide logits or variant scores are needed.

## Score an SNV

The project uses the REF-to-ALT log-likelihood-ratio direction:

```text
LLR(REF>ALT) = log P(ALT | sequence context) - log P(REF | sequence context)
```

A more negative score means the ALT allele is less supported than the REF allele
in the same sequence context. The following function supports biallelic A/C/G/T
SNVs and either of the project's two scoring protocols:

- `masked=True`: replace the variant position with `[MASK]`. This is the protocol
  used for the rice labeled-variant PR-AUC evaluation.
- `masked=False`: retain the reference nucleotide. This is the unmasked LLR
  protocol used for the genome-wide score collection.

Do not compare or combine scores from the two protocols as if they were the same
distribution.

```python
import torch
from transformers import AutoModelForMaskedLM, AutoTokenizer

repo_id = "xxl0001/COD-PlantCAD-Rice"
device = torch.device("cuda")

tokenizer = AutoTokenizer.from_pretrained(repo_id, trust_remote_code=True)
model = AutoModelForMaskedLM.from_pretrained(
    repo_id,
    trust_remote_code=True,
).to(device).eval()

base_token_ids = {
    base: tokenizer.convert_tokens_to_ids(base.lower())
    for base in "ACGT"
}


def score_snv(sequence_512, variant_index, ref, alt, masked=True):
    """Return ALT-logit minus REF-logit for one biallelic SNV.

    sequence_512: reference DNA sequence containing exactly 512 bases
    variant_index: zero-based SNV position inside sequence_512
    ref/alt: one of A, C, G, or T
    masked: use the center-masked protocol when True
    """
    sequence = sequence_512.upper()
    ref = ref.upper()
    alt = alt.upper()

    if len(sequence) != 512:
        raise ValueError(f"Expected 512 bp, received {len(sequence)} bp")
    if not 0 <= variant_index < 512:
        raise ValueError("variant_index must be in [0, 511]")
    if ref not in base_token_ids or alt not in base_token_ids:
        raise ValueError("Only biallelic A/C/G/T SNVs are supported")
    if sequence[variant_index] != ref:
        raise ValueError(
            f"Reference mismatch: sequence has {sequence[variant_index]}, ref={ref}"
        )

    input_ids = tokenizer(
        sequence.lower(),
        add_special_tokens=False,
        padding=False,
        truncation=False,
        return_attention_mask=False,
        return_token_type_ids=False,
        return_tensors="pt",
    )["input_ids"].to(device)
    if input_ids.shape != (1, 512):
        raise RuntimeError(f"Unexpected tokenized shape: {tuple(input_ids.shape)}")

    if masked:
        input_ids[0, variant_index] = tokenizer.mask_token_id

    with torch.inference_mode():
        logits = model(input_ids=input_ids, return_dict=True).logits

    site_logits = logits[0, variant_index].float()
    return (
        site_logits[base_token_ids[alt]]
        - site_logits[base_token_ids[ref]]
    ).item()


sequence = "ACGT" * 128  # A at zero-based index 256
masked_llr = score_snv(sequence, 256, ref="A", alt="G", masked=True)
unmasked_llr = score_snv(sequence, 256, ref="A", alt="G", masked=False)
print({"masked_llr": masked_llr, "unmasked_llr": unmasked_llr})
```

For this release checkpoint, the example returns approximately:

```text
masked_llr   = -7.037059
unmasked_llr = -9.671406
```

## Score a rice genomic coordinate

The project reference FASTA is `osa1_r7.asm.chrs.fa`, with chromosome names
`Chr1` through `Chr12`. VCF positions are one-based. This helper extracts a
centered 512 bp reference window with the SNV at zero-based index 256, matching
the masked PR-AUC workflow:

```python
from pyfaidx import Fasta


def get_centered_window(fasta, chrom, pos, window_size=512):
    pos0 = int(pos) - 1
    variant_index = window_size // 2
    start = pos0 - variant_index
    end = start + window_size
    chrom_length = len(fasta[chrom])

    if start < 0 or end > chrom_length:
        raise ValueError("Variant is too close to a chromosome boundary")

    sequence = str(fasta[chrom][start:end]).upper()
    if len(sequence) != window_size:
        raise RuntimeError("Failed to extract a 512 bp window")
    return sequence, variant_index


fasta = Fasta(
    "/root/private_data/xlxiang/MTEDF/huggingface/"
    "mtedf-rice-vep/reference_genome/osa1_r7.asm.chrs.fa",
    as_raw=True,
    sequence_always_upper=True,
)

chrom = "Chr1"
pos = 1_000_001
ref = "A"  # Replace with REF from the VCF.
alt = "G"  # Replace with ALT from the VCF.

sequence_512, variant_index = get_centered_window(fasta, chrom, pos)
llr = score_snv(sequence_512, variant_index, ref, alt, masked=True)
print({"chrom": chrom, "pos": pos, "ref": ref, "alt": alt, "llr": llr})
```

Keep the reference-match check enabled. A mismatch usually means the coordinate
base, chromosome naming, or reference assembly does not match the scoring data.

## Score direction and allele-frequency folding

- The released score direction is `logit(ALT) - logit(REF)`.
- Lower values indicate less model support for ALT relative to REF.
- For minor-allele analyses only, negate the score when ALT allele frequency is
  greater than 0.5, because REF is then the minor allele.
- Do not apply that sign flip when retaining the original REF-to-ALT direction.
- Scores depend on the 512 bp context and masked/unmasked protocol.

## Limitations

- This checkpoint supports 512 bp inputs; the examples do not pad shorter input.
- The documented variant function supports biallelic A/C/G/T SNVs only.
- The model was evaluated against the R498/IRGSP-1.0 project reference; verify
  coordinates and REF alleles before using another rice assembly.
- An LLR is a relative model score, not a calibrated probability of a phenotype,
  pathogenicity, or fitness effect.
- `trust_remote_code=True` executes code from this repository. Pin a reviewed
  revision for production use.

## Citation

COD PlantCAD Rice uses the PlantCaduceus architecture. Please cite the PlantCAD
work:

```bibtex
@article{Zhai2025CrossSpecies,
  author  = {Zhai, Jingjing and Gokaslan, Aaron and Schiff, Yoni and Berthel,
             Alexander and Liu, Z. Y. and Lai, W. L. and Miller, Z. R. and
             Scheben, Armin and Stitzer, Michelle C. and Romay, Maria C. and
             Buckler, Edward S. and Kuleshov, Volodymyr},
  title   = {Cross-species modeling of plant genomes at single nucleotide
             resolution using a pretrained DNA language model},
  journal = {Proceedings of the National Academy of Sciences},
  year    = {2025},
  volume  = {122},
  number  = {24},
  pages   = {e2421738122},
  doi     = {10.1073/pnas.2421738122}
}
```