Skip to main content

lindera_dictionary/dictionary/
context_id_map.rs

1//! Connection-cost context-ID permutation shipped with a dictionary.
2//!
3//! When `connection_id_mapping` is enabled, the build relabels left/right context IDs by
4//! access frequency so that hot connection-matrix cells cluster in cache. The permutation
5//! is applied to the connection matrix, the system dictionary and the unknown dictionary
6//! at build time, and is persisted here so that anything compiled later against the same
7//! dictionary — most importantly a detailed user dictionary — can be relabeled into the
8//! same ID space.
9
10use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
11use serde::{Deserialize, Serialize};
12
13/// A pair of context-ID permutations, `perm[old_id] = new_id`.
14///
15/// `left` permutes left-context IDs (a word's `left_id`, the connection matrix's backward
16/// / row axis, length `backward_size`) and `right` permutes right-context IDs (a word's
17/// `right_id`, the forward / column axis, length `forward_size`). Both are bijections
18/// over `0..len`, with ID 0 pinned to 0 because it is reserved for BOS/EOS.
19#[derive(Debug, Clone, Serialize, Deserialize, Archive, RkyvSerialize, RkyvDeserialize)]
20pub struct ContextIdMap {
21    /// Permutation of left-context IDs (`WordEntry.left_id`, matrix backward axis).
22    pub left: Vec<u16>,
23    /// Permutation of right-context IDs (`WordEntry.right_id`, matrix forward axis).
24    pub right: Vec<u16>,
25}
26
27impl ContextIdMap {
28    /// Map a left-context ID into the remapped space.
29    ///
30    /// # Arguments
31    ///
32    /// * `id` - Left-context ID in the original space.
33    ///
34    /// # Returns
35    ///
36    /// The remapped ID, or `id` unchanged when it is outside the permutation (a
37    /// malformed ID; the matrix build rejects those separately).
38    #[inline]
39    pub fn map_left(&self, id: u16) -> u16 {
40        self.left.get(id as usize).copied().unwrap_or(id)
41    }
42
43    /// Map a right-context ID into the remapped space.
44    ///
45    /// # Arguments
46    ///
47    /// * `id` - Right-context ID in the original space.
48    ///
49    /// # Returns
50    ///
51    /// The remapped ID, or `id` unchanged when it is outside the permutation.
52    #[inline]
53    pub fn map_right(&self, id: u16) -> u16 {
54        self.right.get(id as usize).copied().unwrap_or(id)
55    }
56}
57
58#[cfg(test)]
59mod tests {
60    use super::*;
61
62    /// Mapping uses the right axis for each ID kind and passes through out-of-range IDs.
63    #[test]
64    fn test_map_left_and_right() {
65        let map = ContextIdMap {
66            left: vec![0, 2, 1],
67            right: vec![0, 5, 6, 7],
68        };
69        assert_eq!(map.map_left(0), 0); // BOS/EOS pinned
70        assert_eq!(map.map_left(1), 2);
71        assert_eq!(map.map_left(2), 1);
72        assert_eq!(map.map_right(3), 7);
73        // Out of range on each axis is returned untouched.
74        assert_eq!(map.map_left(9), 9);
75        assert_eq!(map.map_right(9), 9);
76    }
77}