Skip to main content

Letter

Enum Letter 

Source
pub enum Letter {
    Single(String),
    Multiple(Vec<String>),
}
Expand description

Represents a single letter/symbol in the alphabet.

Variants§

§

Single(String)

§

Multiple(Vec<String>)

Implementations§

Source§

impl Letter

Source

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
Hide additional 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    }
Source

pub fn from_symbols(symbols: Vec<String>) -> Self

Create a letter from multiple symbols

Source

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
Hide additional 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    }
Source

pub fn name(&self) -> String

Get a string representation of this letter

Source

pub fn is_empty(&self) -> bool

Check if this is an empty letter

Source

pub fn deserialize( str_letters: &str, possible_letters: &[Letter], ) -> Result<Letter, String>

Deserialize a comma-separated string of letter names into a Letter

Trait Implementations§

Source§

impl Clone for Letter

Source§

fn clone(&self) -> Letter

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Letter

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for Letter

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Hash for Letter

Source§

fn hash<H: Hasher>(&self, state: &mut H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where H: Hasher, Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl PartialEq for Letter

Source§

fn eq(&self, other: &Self) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl Eq for Letter

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

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
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.