Skip to main content

qualia_core_db/foundation/
topology_draft.rs

1//! Topological speculative decoding — concept hash → token id (B3.1c).
2
3use crate::compute_universe::{TopologyDraftBatch, MAX_DRAFT_LEN};
4use crate::gguf_sharder::GgufTokenizer;
5
6/// Cold-path vocabulary bridge: 10D concept hashes → GGUF token ids.
7pub struct TopologyDraftMapper<'a> {
8    tokenizer: &'a GgufTokenizer,
9}
10
11impl<'a> TopologyDraftMapper<'a> {
12    pub fn new(tokenizer: &'a GgufTokenizer) -> Self {
13        Self { tokenizer }
14    }
15
16    /// Map a concept fingerprint to a draft token id (stable across runs for a given vocab).
17    pub fn concept_to_token_id(&self, concept_hash: u64) -> u32 {
18        let probe = format!("q42:{:016x}", concept_hash);
19        let ids = self.tokenizer.encode(&probe);
20        if let Some(&id) = ids.first() {
21            return id;
22        }
23        (concept_hash as u32) % self.tokenizer.vocab_len().max(1)
24    }
25
26    /// Fill a draft batch from concept hashes (γ ≤ `MAX_DRAFT_LEN`).
27    pub fn fill_draft_batch(&self, concept_hashes: &[u64], gamma: usize) -> TopologyDraftBatch {
28        let gamma = gamma.clamp(1, MAX_DRAFT_LEN).min(concept_hashes.len());
29        let mut batch = TopologyDraftBatch::empty();
30        for i in 0..gamma {
31            batch.concept_hashes[i] = concept_hashes[i];
32            batch.draft_ids[i] = self.concept_to_token_id(concept_hashes[i]);
33        }
34        batch.draft_len = gamma as u8;
35        batch
36    }
37}
38
39#[cfg(test)]
40mod tests {
41    use super::*;
42
43    #[test]
44    fn mapper_is_stable_for_same_hash() {
45        let tok = GgufTokenizer::default();
46        let mapper = TopologyDraftMapper::new(&tok);
47        let a = mapper.concept_to_token_id(0xDEAD_BEEF);
48        let b = mapper.concept_to_token_id(0xDEAD_BEEF);
49        assert_eq!(a, b);
50    }
51
52    #[test]
53    fn fill_draft_batch_respects_gamma() {
54        let tok = GgufTokenizer::default();
55        let mapper = TopologyDraftMapper::new(&tok);
56        let batch = mapper.fill_draft_batch(&[1, 2, 3, 4], 3);
57        assert_eq!(batch.draft_len, 3);
58        assert_ne!(batch.draft_ids[0], 0);
59    }
60}