Skip to main content

ferritin_plms/esm3/tokenization/
sasa.rs

1//! ESM3 SASA (solvent-accessible surface area) tokenization.
2//!
3//! Continuous SASA values (Ų) are discretized into 16 bins using
4//! the 15 boundary values from `esm/utils/constants/esm3.py`.
5//!
6//! Token layout:
7//!   0 = PAD, 1 = MASK, 2 = UNK
8//!   3..18 = bins 0..15   (total 19 tokens)
9
10use crate::esm3::utils::constants::SASA_DISCRETIZATION_BOUNDARIES;
11
12/// Discretize a slice of per-residue SASA values into token IDs.
13///
14/// Each value is placed in the leftmost bin where `value < boundary`.
15/// Values above all boundaries fall into the highest bin.
16pub fn tokenize_sasa(sasa_values: &[f32]) -> Vec<u32> {
17    sasa_values
18        .iter()
19        .map(|&v| {
20            let bin = SASA_DISCRETIZATION_BOUNDARIES
21                .iter()
22                .position(|&b| v < b)
23                .unwrap_or(SASA_DISCRETIZATION_BOUNDARIES.len());
24            bin as u32 + 3 // offset by 3 for PAD, MASK, UNK
25        })
26        .collect()
27}
28
29#[cfg(test)]
30mod tests {
31    use super::*;
32
33    #[test]
34    fn test_tokenize_sasa_lowest_bin() {
35        // 0.0 < 0.8 → bin 0 → token 3
36        assert_eq!(tokenize_sasa(&[0.0]), vec![3]);
37    }
38
39    #[test]
40    fn test_tokenize_sasa_highest_bin() {
41        // 200.0 > all boundaries → bin 15 → token 18
42        assert_eq!(tokenize_sasa(&[200.0]), vec![18]);
43    }
44
45    #[test]
46    fn test_tokenize_sasa_midpoint() {
47        // 5.0 >= 0.8, 5.0 >= 4.0, 5.0 < 9.6 → bin 2 → token 5
48        assert_eq!(tokenize_sasa(&[5.0]), vec![5]);
49    }
50
51    #[test]
52    fn test_tokenize_sasa_batch() {
53        let ids = tokenize_sasa(&[0.0, 200.0]);
54        assert_eq!(ids, vec![3, 18]);
55    }
56}