pub enum Letter {
Single(String),
Multiple(Vec<String>),
}Expand description
Represents a single letter/symbol in the alphabet.
Variants§
Implementations§
Source§impl Letter
impl Letter
Sourcepub fn new<S: ToString>(symbol: S) -> Self
pub fn new<S: ToString>(symbol: S) -> Self
Create a new letter from a single symbol
Examples found in repository?
examples/vending_machine.rs (line 91)
75 fn resolve_query(&mut self, query: &mut OutputQuery) -> Result<(), String> {
76 self.stats.increment_nb_query();
77 self.stats.add_nb_letter(query.input_word.len());
78 self.stats.increment_nb_submitted_query();
79 self.stats.add_nb_submitted_letter(query.input_word.len());
80
81 // Reset to initial state for each query
82 self.state = VendingState::Idle;
83
84 let mut outputs = Vec::new();
85 for input_letter in query.input_word.letters() {
86 let command_string = input_letter.symbols();
87 let command = command_string.as_str();
88
89 let (next, resp) = self.process_input(command, self.state);
90 self.state = next;
91 outputs.push(Letter::new(resp));
92 }
93
94 query.set_result(Word::from_letters(outputs));
95 Ok(())
96 }More examples
examples/custom_kb.rs (line 112)
94 fn resolve_query(&mut self, query: &mut OutputQuery) -> Result<(), String> {
95 self.stats.increment_nb_query();
96 self.stats.add_nb_letter(query.input_word.len());
97 self.stats.increment_nb_submitted_query();
98 self.stats.add_nb_submitted_letter(query.input_word.len());
99 // Reset to initial state for each query
100 self.state = ATMState::Idle;
101
102 // let mut state = self.state.lock().unwrap();
103 let mut outputs = Vec::new();
104
105 for input_letter in query.input_word.letters() {
106 // Extract command from letter name (format: "Letter('x')")
107 let symbol = input_letter.symbols();
108 let command = symbol.as_str();
109
110 let (next_state, response) = self.process_input(command, self.state);
111 self.state = next_state;
112 outputs.push(Letter::new(response));
113 }
114
115 query.set_result(Word::from_letters(outputs));
116 Ok(())
117 }examples/benchmark.rs (line 107)
89 fn resolve_query(&mut self, query: &mut OutputQuery) -> Result<(), String> {
90 self.stats.increment_nb_query();
91 self.stats.add_nb_letter(query.input_word.len());
92 self.stats.increment_nb_submitted_query();
93 self.stats.add_nb_submitted_letter(query.input_word.len());
94
95 self.current_state = "s0".to_string();
96 let mut output_letters = Vec::new();
97
98 for input_letter in query.input_word.letters() {
99 let input = input_letter.symbols();
100 let key = (self.current_state.clone(), input);
101 let (output, next_state) = self
102 .transitions
103 .get(&key)
104 .cloned()
105 .ok_or_else(|| format!("No transition for ({}, {})", key.0, key.1))?;
106
107 output_letters.push(Letter::new(output));
108 self.current_state = next_state;
109 }
110
111 query.set_result(Word::from_letters(output_letters));
112 Ok(())
113 }examples/memory_kb.rs (line 112)
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}examples/all_eqtests_custom_kb.rs (line 78)
68fn run_strategy(name: &str, builder: &EqBuilder) -> StrategyReport {
69 let vocabulary = vec![
70 "INSERT_CARD".to_string(),
71 "ENTER_PIN".to_string(),
72 "REQUEST_WITHDRAW".to_string(),
73 "EJECT_CARD".to_string(),
74 "TIMEOUT".to_string(),
75 ];
76 let input_letters = vocabulary
77 .iter()
78 .map(|s| Letter::new(s))
79 .collect::<Vec<_>>();
80 let max_states = 8;
81
82 let kb = Arc::new(Mutex::new(ATMKnowledgeBase::new()));
83 let knowledge_base: SharedKb = kb.clone();
84 let eqtest = builder(knowledge_base.clone(), input_letters, max_states);
85 let mut learner = LSTAR::new(vocabulary, knowledge_base, max_states, None, Some(eqtest));
86
87 println!("\n--- Running {name} ---");
88 let started = Instant::now();
89 let learn_result = learner.learn();
90 let elapsed_ms = started.elapsed().as_millis();
91
92 let stats = {
93 let kb_guard = kb.lock().unwrap();
94 StatsSnapshot::from_stats(&kb_guard.stats)
95 };
96
97 match learn_result {
98 Ok(automata) => {
99 let state_count = automata.get_states().len();
100 let transition_count = automata.transitions.len();
101 println!("{name}: success (states={state_count}, transitions={transition_count})");
102 StrategyReport {
103 name: name.to_string(),
104 elapsed_ms,
105 state_count: Some(state_count),
106 transition_count: Some(transition_count),
107 error: None,
108 stats,
109 }
110 }
111 Err(err) => {
112 println!("{name}: failed ({err})");
113 StrategyReport {
114 name: name.to_string(),
115 elapsed_ms,
116 state_count: None,
117 transition_count: None,
118 error: Some(err),
119 stats,
120 }
121 }
122 }
123}
124
125fn print_summary(reports: &[StrategyReport]) {
126 println!("\n=== Final Statistics Summary ===");
127 for report in reports {
128 println!("\n[{}]", report.name);
129 println!(" runtime_ms: {}", report.elapsed_ms);
130 match (&report.state_count, &report.transition_count) {
131 (Some(states), Some(transitions)) => {
132 println!(" states: {}", states);
133 println!(" transitions: {}", transitions);
134 }
135 _ => println!(" model: n/a"),
136 }
137 match &report.error {
138 Some(err) => println!(" status: failed ({err})"),
139 None => println!(" status: success"),
140 }
141 println!(" kb_nb_query: {}", report.stats.nb_query);
142 println!(
143 " kb_nb_submitted_query: {}",
144 report.stats.nb_submitted_query
145 );
146 println!(" kb_nb_letter: {}", report.stats.nb_letter);
147 println!(
148 " kb_nb_submitted_letter: {}",
149 report.stats.nb_submitted_letter
150 );
151 }
152}
153
154/// Minimal ATM simulator used as a custom knowledge base.
155struct ATMKnowledgeBase {
156 state: ATMState,
157 stats: KnowledgeBaseStats,
158}
159
160#[derive(Clone, Copy)]
161enum ATMState {
162 Idle,
163 CardInserted,
164 Authenticated,
165 Ready,
166 Dispensing,
167}
168
169impl ATMKnowledgeBase {
170 fn new() -> Self {
171 Self {
172 state: ATMState::Idle,
173 stats: KnowledgeBaseStats::new(),
174 }
175 }
176
177 fn process_input(&self, command: &str, current_state: ATMState) -> (ATMState, &'static str) {
178 match current_state {
179 ATMState::Idle => match command {
180 "INSERT_CARD" => (ATMState::CardInserted, "CARD_ACCEPTED"),
181 _ => (ATMState::Idle, "INVALID_OP"),
182 },
183 ATMState::CardInserted => match command {
184 "ENTER_PIN" => (ATMState::Authenticated, "PIN_VERIFIED"),
185 "EJECT_CARD" => (ATMState::Idle, "CARD_EJECTED"),
186 _ => (ATMState::CardInserted, "RETRY"),
187 },
188 ATMState::Authenticated => match command {
189 "REQUEST_WITHDRAW" => (ATMState::Ready, "ENTER_AMOUNT"),
190 "EJECT_CARD" => (ATMState::Idle, "CARD_EJECTED"),
191 "TIMEOUT" => (ATMState::Idle, "SESSION_TIMEOUT"),
192 _ => (ATMState::Authenticated, "INVALID_COMMAND"),
193 },
194 ATMState::Ready => match command {
195 "REQUEST_WITHDRAW" => (ATMState::Dispensing, "DISPENSING"),
196 "EJECT_CARD" => (ATMState::Idle, "CARD_EJECTED"),
197 _ => (ATMState::Ready, "WAIT"),
198 },
199 ATMState::Dispensing => match command {
200 "EJECT_CARD" => (ATMState::Idle, "CARD_EJECTED"),
201 _ => (ATMState::Dispensing, "DISPENSING"),
202 },
203 }
204 }
205}
206
207impl KnowledgeBaseTrait for ATMKnowledgeBase {
208 fn resolve_query(&mut self, query: &mut OutputQuery) -> Result<(), String> {
209 self.stats.increment_nb_query();
210 self.stats.add_nb_letter(query.input_word.len());
211 self.stats.increment_nb_submitted_query();
212 self.stats.add_nb_submitted_letter(query.input_word.len());
213
214 self.state = ATMState::Idle;
215 let mut outputs = Vec::new();
216
217 for input_letter in query.input_word.letters() {
218 let command = input_letter.symbols();
219 let (next_state, response) = self.process_input(command.as_str(), self.state);
220 self.state = next_state;
221 outputs.push(Letter::new(response));
222 }
223
224 query.set_result(Word::from_letters(outputs));
225 Ok(())
226 }examples/random_walk_eq.rs (line 42)
9fn main() -> Result<(), Box<dyn std::error::Error>> {
10 println!("=== Random Walk Equivalence Test Example ===\n");
11
12 // Create a simple system
13 let mut kb = DemoKnowledgeBase::new();
14
15 // Define a simple state machine:
16 // S0 -a/0-> S1 (initial state)
17 // S0 -b/1-> S0
18 // S1 -a/0-> S2
19 // S1 -b/1-> S0
20 // S2 -a/0-> S2
21 // S2 -b/1-> S1
22
23 // Make outputs state-dependent so learner must distinguish states
24 kb.add_transition("s0", "a", "0", "s1");
25 kb.add_transition("s0", "b", "1", "s0");
26 kb.add_transition("s1", "a", "1", "s2");
27 kb.add_transition("s1", "b", "0", "s0");
28 kb.add_transition("s2", "a", "1", "s2");
29 kb.add_transition("s2", "b", "0", "s1");
30
31 let knowledge_base = Arc::new(Mutex::new(kb));
32
33 // Create vocabulary
34 let vocabulary = vec!["a".to_string(), "b".to_string()];
35
36 // Create learner
37 let mut lstar = LSTAR::new(vocabulary.clone(), knowledge_base.clone(), 5, None, None);
38
39 // Use random walk equivalence test instead of W-method
40 let input_letters = vocabulary
41 .iter()
42 .map(|s| Letter::new(s))
43 .collect::<Vec<_>>();
44 let random_walk = RandomWalkMethod::new(
45 knowledge_base.clone(),
46 input_letters,
47 10000, // max_steps: 10000 steps
48 0.75, // restart_probability: 75%
49 );
50
51 lstar = lstar.with_equivalence_test(Arc::new(random_walk));
52
53 // Run learning
54 match lstar.learn() {
55 Ok(automata) => {
56 println!("\n=== Learned Automaton (Random Walk Test) ===\n");
57 println!("{}", automata.build_dot_code());
58 println!("\nLearning completed successfully!");
59
60 // Print statistics
61 println!("\nStatistics:");
62 println!(" Number of states: {}", automata.get_states().len());
63 println!(" Number of transitions: {}", automata.transitions.len());
64 }
65 Err(e) => eprintln!("Error during learning: {}", e),
66 }
67
68 let kb_guard = knowledge_base.lock().unwrap();
69 println!("\nKnowledge Base Statistics:\n{}", kb_guard.stats);
70
71 Ok(())
72}
73
74struct DemoKnowledgeBase {
75 transitions: HashMap<(String, String), (String, String)>,
76 current_state: String,
77 stats: KnowledgeBaseStats,
78}
79
80impl DemoKnowledgeBase {
81 fn new() -> Self {
82 Self {
83 transitions: std::collections::HashMap::new(),
84 current_state: "s0".to_string(),
85 stats: KnowledgeBaseStats::new(),
86 }
87 }
88
89 fn add_transition(&mut self, from_state: &str, input: &str, output: &str, to_state: &str) {
90 self.transitions.insert(
91 (from_state.to_string(), input.to_string()),
92 (output.to_string(), to_state.to_string()),
93 );
94 }
95}
96
97impl KnowledgeBaseTrait for DemoKnowledgeBase {
98 fn resolve_query(&mut self, query: &mut OutputQuery) -> Result<(), String> {
99 self.stats.increment_nb_query();
100 self.stats.add_nb_letter(query.input_word.len());
101 self.stats.increment_nb_submitted_query();
102 self.stats.add_nb_submitted_letter(query.input_word.len());
103
104 self.current_state = "s0".to_string();
105 let mut output_letters = Vec::new();
106
107 for input_letter in query.input_word.letters() {
108 let input = input_letter.symbols();
109 let key = (self.current_state.clone(), input);
110 let (output, next_state) = self
111 .transitions
112 .get(&key)
113 .cloned()
114 .ok_or_else(|| format!("No transition for ({}, {})", key.0, key.1))?;
115
116 output_letters.push(Letter::new(output));
117 self.current_state = next_state;
118 }
119
120 query.set_result(Word::from_letters(output_letters));
121 Ok(())
122 }Additional examples can be found in:
Sourcepub fn from_symbols(symbols: Vec<String>) -> Self
pub fn from_symbols(symbols: Vec<String>) -> Self
Create a letter from multiple symbols
Sourcepub fn symbols(&self) -> String
pub fn symbols(&self) -> String
Get the symbols represented by this letter
Examples found in repository?
examples/all_eqtests_custom_kb.rs (line 218)
208 fn resolve_query(&mut self, query: &mut OutputQuery) -> Result<(), String> {
209 self.stats.increment_nb_query();
210 self.stats.add_nb_letter(query.input_word.len());
211 self.stats.increment_nb_submitted_query();
212 self.stats.add_nb_submitted_letter(query.input_word.len());
213
214 self.state = ATMState::Idle;
215 let mut outputs = Vec::new();
216
217 for input_letter in query.input_word.letters() {
218 let command = input_letter.symbols();
219 let (next_state, response) = self.process_input(command.as_str(), self.state);
220 self.state = next_state;
221 outputs.push(Letter::new(response));
222 }
223
224 query.set_result(Word::from_letters(outputs));
225 Ok(())
226 }More examples
examples/vending_machine.rs (line 86)
75 fn resolve_query(&mut self, query: &mut OutputQuery) -> Result<(), String> {
76 self.stats.increment_nb_query();
77 self.stats.add_nb_letter(query.input_word.len());
78 self.stats.increment_nb_submitted_query();
79 self.stats.add_nb_submitted_letter(query.input_word.len());
80
81 // Reset to initial state for each query
82 self.state = VendingState::Idle;
83
84 let mut outputs = Vec::new();
85 for input_letter in query.input_word.letters() {
86 let command_string = input_letter.symbols();
87 let command = command_string.as_str();
88
89 let (next, resp) = self.process_input(command, self.state);
90 self.state = next;
91 outputs.push(Letter::new(resp));
92 }
93
94 query.set_result(Word::from_letters(outputs));
95 Ok(())
96 }examples/custom_kb.rs (line 107)
94 fn resolve_query(&mut self, query: &mut OutputQuery) -> Result<(), String> {
95 self.stats.increment_nb_query();
96 self.stats.add_nb_letter(query.input_word.len());
97 self.stats.increment_nb_submitted_query();
98 self.stats.add_nb_submitted_letter(query.input_word.len());
99 // Reset to initial state for each query
100 self.state = ATMState::Idle;
101
102 // let mut state = self.state.lock().unwrap();
103 let mut outputs = Vec::new();
104
105 for input_letter in query.input_word.letters() {
106 // Extract command from letter name (format: "Letter('x')")
107 let symbol = input_letter.symbols();
108 let command = symbol.as_str();
109
110 let (next_state, response) = self.process_input(command, self.state);
111 self.state = next_state;
112 outputs.push(Letter::new(response));
113 }
114
115 query.set_result(Word::from_letters(outputs));
116 Ok(())
117 }examples/benchmark.rs (line 99)
89 fn resolve_query(&mut self, query: &mut OutputQuery) -> Result<(), String> {
90 self.stats.increment_nb_query();
91 self.stats.add_nb_letter(query.input_word.len());
92 self.stats.increment_nb_submitted_query();
93 self.stats.add_nb_submitted_letter(query.input_word.len());
94
95 self.current_state = "s0".to_string();
96 let mut output_letters = Vec::new();
97
98 for input_letter in query.input_word.letters() {
99 let input = input_letter.symbols();
100 let key = (self.current_state.clone(), input);
101 let (output, next_state) = self
102 .transitions
103 .get(&key)
104 .cloned()
105 .ok_or_else(|| format!("No transition for ({}, {})", key.0, key.1))?;
106
107 output_letters.push(Letter::new(output));
108 self.current_state = next_state;
109 }
110
111 query.set_result(Word::from_letters(output_letters));
112 Ok(())
113 }examples/random_walk_eq.rs (line 108)
98 fn resolve_query(&mut self, query: &mut OutputQuery) -> Result<(), String> {
99 self.stats.increment_nb_query();
100 self.stats.add_nb_letter(query.input_word.len());
101 self.stats.increment_nb_submitted_query();
102 self.stats.add_nb_submitted_letter(query.input_word.len());
103
104 self.current_state = "s0".to_string();
105 let mut output_letters = Vec::new();
106
107 for input_letter in query.input_word.letters() {
108 let input = input_letter.symbols();
109 let key = (self.current_state.clone(), input);
110 let (output, next_state) = self
111 .transitions
112 .get(&key)
113 .cloned()
114 .ok_or_else(|| format!("No transition for ({}, {})", key.0, key.1))?;
115
116 output_letters.push(Letter::new(output));
117 self.current_state = next_state;
118 }
119
120 query.set_result(Word::from_letters(output_letters));
121 Ok(())
122 }Trait Implementations§
impl Eq for Letter
Auto Trait Implementations§
impl Freeze for Letter
impl RefUnwindSafe for Letter
impl Send for Letter
impl Sync for Letter
impl Unpin for Letter
impl UnsafeUnpin for Letter
impl UnwindSafe for Letter
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
Compare self to
key and return true if they are equal.Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
Converts
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
Converts
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more