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}