Skip to main content

memory_kb/
memory_kb.rs

1//! Demonstrates learning a randomly generated automaton using a custom
2//! in-memory knowledge base layered on top of the default cache.
3
4use rust_lstar::knowledge_base::{KnowledgeBase, KnowledgeBaseStats, KnowledgeBaseTrait};
5use rust_lstar::query::OutputQuery;
6use rust_lstar::*;
7use std::sync::{Arc, Mutex};
8use std::time::{SystemTime, UNIX_EPOCH};
9
10fn main() -> Result<(), Box<dyn std::error::Error>> {
11    println!("=== Memory Knowledge Base Learning Example ===\n");
12
13    let input_symbols = vec!["a", "b", "c"];
14    let output_symbols = vec!["0", "1", "2", "3"];
15    let target_state_count = 5;
16    let max_states = 8;
17
18    let target = build_random_machine(target_state_count, &input_symbols, &output_symbols);
19    println!(
20        "Random target generated (states={}, transitions={})",
21        target.get_states().len(),
22        target.transitions.len()
23    );
24
25    let kb = Arc::new(Mutex::new(RandomMachineKnowledgeBase::new(target.clone())));
26    let vocabulary = input_symbols
27        .iter()
28        .map(|symbol| symbol.to_string())
29        .collect::<Vec<_>>();
30
31    let mut learner = LSTAR::new(vocabulary, kb.clone(), max_states, None, None);
32    let learned = learner.learn()?;
33
34    println!("\n=== Learned Automaton ===");
35    println!("{}", learned.build_dot_code());
36
37    let kb_guard = kb.lock().unwrap();
38    println!("\nKnowledge Base Statistics:\n{}", kb_guard.stats());
39
40    Ok(())
41}
42
43/// Knowledge base that answers queries by executing a hidden target automaton.
44struct RandomMachineKnowledgeBase {
45    base: KnowledgeBase,
46    target: Automata,
47}
48
49impl RandomMachineKnowledgeBase {
50    fn new(target: Automata) -> Self {
51        Self {
52            base: KnowledgeBase::new(),
53            target,
54        }
55    }
56
57    fn stats(&self) -> &KnowledgeBaseStats {
58        self.base.stats()
59    }
60
61    fn submit_word_to_target(&mut self, word: &Word) -> Result<Word, String> {
62        if word.is_empty() {
63            return Ok(Word::new());
64        }
65
66        let initial_state = self.target.initial_state.clone();
67        let (output_word, _) = self
68            .target
69            .play_word(word, Some(&initial_state))?;
70        Ok(output_word)
71    }
72}
73
74impl KnowledgeBaseTrait for RandomMachineKnowledgeBase {
75    fn resolve_query(&mut self, query: &mut OutputQuery) -> Result<(), String> {
76        match self.base.resolve_query(query) {
77            Ok(_) => Ok(()),
78            Err(_) => {
79                let output = self.submit_word_to_target(&query.input_word)?;
80                self.base.add_word(&query.input_word, &output)?;
81                query.set_result(output);
82                Ok(())
83            }
84        }
85    }
86
87    fn add_word(&mut self, input_word: &Word, output_word: &Word) -> Result<(), String> {
88        self.base.add_word(input_word, output_word)
89    }
90}
91
92fn build_random_machine(
93    state_count: usize,
94    input_symbols: &[&str],
95    output_symbols: &[&str],
96) -> Automata {
97    let mut rng = SimpleRng::new();
98    let mut automata = Automata::new(State::new("S0".to_string()), "RandomTarget".to_string());
99    let mut transitions = Vec::new();
100    let mut transition_id = 0usize;
101
102    for state_idx in 0..state_count {
103        let source_state = format!("S{}", state_idx);
104        for input_symbol in input_symbols {
105            let next_state_idx = rng.gen_range(0, state_count);
106            let output_idx = rng.gen_range(0, output_symbols.len());
107
108            transitions.push(Transition::new_with_source(
109                format!("t{}", transition_id),
110                source_state.clone(),
111                State::new(format!("S{}", next_state_idx)),
112                Letter::new(*input_symbol),
113                Letter::new(output_symbols[output_idx]),
114            ));
115            transition_id += 1;
116        }
117    }
118
119    automata.transitions = transitions;
120    automata
121}
122
123/// Tiny deterministic RNG for generating repeatable random automata topology.
124struct SimpleRng {
125    state: u64,
126}
127
128impl SimpleRng {
129    fn new() -> Self {
130        let seed = SystemTime::now()
131            .duration_since(UNIX_EPOCH)
132            .unwrap_or_default()
133            .as_nanos() as u64;
134        Self { state: seed }
135    }
136
137    fn next_u64(&mut self) -> u64 {
138        self.state = self.state.wrapping_mul(1664525).wrapping_add(1013904223);
139        self.state
140    }
141
142    fn gen_range(&mut self, min: usize, max: usize) -> usize {
143        if max <= min {
144            return min;
145        }
146        min + (self.next_u64() as usize) % (max - min)
147    }
148}