Commit afff8993 authored by Bharat Garhewal's avatar Bharat Garhewal
Browse files

Cleanup

parent 72575064
use crate::automatadefs::mealy::{InputSymbol, Mealy, State};
use crate::automatadefs::traits::{FiniteStateMachine, InputWord};
use crate::automatadefs::traits::FiniteStateMachine;
use crate::util::data_structs::arena_tree::ArenaTree;
use fnv::FnvHashMap;
use std::collections::VecDeque;
......@@ -11,7 +11,7 @@ pub struct SplittingNode {
// Index of the child nodes.
children: Vec<usize>,
// Separating word for the block.
separator: Option<Box<InputWord>>,
separator: Option<Box<[InputSymbol]>>,
// Distance between root node and current node.
depth: usize,
}
......@@ -21,7 +21,7 @@ impl SplittingNode {
fn new(
states: Vec<State>,
children: Vec<usize>,
separator: Option<Box<InputWord>>,
separator: Option<Box<[InputSymbol]>>,
depth: usize,
) -> Self {
Self {
......@@ -33,10 +33,10 @@ impl SplittingNode {
}
#[must_use]
pub fn get_separator(&self) -> Option<Box<InputWord>> {
pub fn get_separator(&self) -> Option<Box<[InputSymbol]>> {
self.separator.clone()
}
fn add_separator(&mut self, sep: Box<InputWord>) {
fn add_separator(&mut self, sep: Box<[InputSymbol]>) {
self.separator = Some(sep);
}
fn add_child(&mut self, child: usize) {
......@@ -218,7 +218,7 @@ pub fn new(hyp: &Mealy) -> ArenaTree<SplittingNode, ()> {
fn refine_block(
curr_block: &[State],
hyp: &Mealy,
sep: Box<InputWord>,
sep: Box<[InputSymbol]>,
tree: &mut ArenaTree<SplittingNode, ()>,
curr_node_idx: usize,
) -> Vec<usize> {
......
......@@ -111,7 +111,10 @@ impl AdsTree {
continue;
}
// Compute the sub-trees for the parititions.
let u_i: usize = o_partitions.values().map(|x| x.len()).sum();
let u_i: usize = o_partitions
.values()
.map(std::collections::BTreeSet::len)
.sum();
for (o, o_part) in &o_partitions {
let u_i_o = o_part.len();
let i_subtree_idx = self.ads_recurse(o_tree, o_part);
......@@ -275,7 +278,7 @@ impl AdaptiveDistinguishingSequence for AdsTree {
let self_input = self.tree.arena[self_idx].val.input;
// Check whether the child nodes are the same.
let self_node = &self.tree.arena[self_idx].val;
for (output, child_idx) in self_node.children.iter() {
for (output, child_idx) in &self_node.children {
writeln!(
&mut ret,
"({:?}, {:?}, {:?}, {} , {:?})",
......
use crate::automatadefs::traits::FiniteStateMachine;
use super::traits::FiniteStateMachine;
use fnv::{FnvHashMap, FnvHashSet};
use rayon::prelude::*;
use serde::{Deserialize, Serialize};
use std::fmt;
use super::traits::{InputWord, OutputWord};
/// Newtype for an input symbol: wraps a usize.
#[derive(
Clone, Default, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Deserialize, Serialize,
......@@ -25,29 +23,31 @@ pub struct OutputSymbol(u16);
pub struct State(u32);
impl fmt::Display for InputSymbol {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
write!(f, "I{}", self.0)
}
}
impl fmt::Display for OutputSymbol {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
write!(f, "O{}", self.0)
}
}
impl fmt::Display for State {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
write!(f, "S{}", self.0)
}
}
impl State {
#[inline]
#[must_use]
pub fn new(a: u32) -> State {
Self(a)
}
#[inline]
#[must_use]
pub fn raw(self) -> u32 {
self.0
}
......@@ -61,6 +61,7 @@ impl From<u32> for State {
impl InputSymbol {
#[inline]
#[must_use]
pub fn new(a: u16) -> InputSymbol {
Self(a)
}
......@@ -88,11 +89,13 @@ impl From<InputSymbol> for usize {
impl OutputSymbol {
#[inline]
#[must_use]
pub fn new(a: u16) -> OutputSymbol {
Self(a)
}
#[inline]
#[must_use]
pub fn raw(self) -> u16 {
self.0
}
......@@ -135,7 +138,7 @@ impl FiniteStateMachine for Mealy {
}
}
fn trace_from(&self, src: State, input_word: &InputWord) -> (State, Box<OutputWord>) {
fn trace_from(&self, src: State, input_word: &[InputSymbol]) -> (State, Box<[OutputSymbol]>) {
let (dest, output) = input_word.iter().fold(
(src, Vec::with_capacity(input_word.len())),
|acc, &input| {
......@@ -148,7 +151,7 @@ impl FiniteStateMachine for Mealy {
(dest, output.into_boxed_slice())
}
fn trace(&self, input_word: &InputWord) -> (State, Box<OutputWord>) {
fn trace(&self, input_word: &[InputSymbol]) -> (State, Box<[OutputSymbol]>) {
self.trace_from(self.initial_state, input_word)
}
......@@ -189,7 +192,7 @@ mod tests {
fn fsm_initial_state_is_set() {
let file_name = Path::new("example.dot");
let (fsm, _, _) = read_mealy_from_file(file_name.to_str().unwrap());
assert_ne!(fsm.initial_state(), State::new(u32::MAX))
assert_ne!(fsm.initial_state(), State::new(u32::MAX));
}
/// Ensure that the set of states is set.
......@@ -197,7 +200,7 @@ mod tests {
fn fsm_states_loaded_correctly() {
let file_name = Path::new("example.dot");
let (fsm, _, _) = read_mealy_from_file(file_name.to_str().unwrap());
assert_eq!(fsm.states().len(), 6)
assert_eq!(fsm.states().len(), 6);
}
/// FSM must be complete.
......@@ -205,6 +208,6 @@ mod tests {
fn fsm_is_complete() {
let file_name = Path::new("example.dot");
let (fsm, _, _) = read_mealy_from_file(file_name.to_str().unwrap());
assert!(fsm.is_complete())
assert!(fsm.is_complete());
}
}
use crate::automatadefs::mealy::{InputSymbol, OutputSymbol, State};
pub type InputWord = [InputSymbol];
pub type OutputWord = [OutputSymbol];
/**
Trait for interacting with an FSM during learning.
......@@ -18,12 +15,12 @@ pub trait FiniteStateMachine {
/// Stateless single step from given state.
fn step_from(&self, src: State, i: InputSymbol) -> (State, OutputSymbol);
/// Stateless multi-step from given state.
fn trace_from(&self, src: State, i_word: &InputWord) -> (State, Box<OutputWord>);
fn trace_from(&self, src: State, i_word: &[InputSymbol]) -> (State, Box<[OutputSymbol]>);
/// Stateless multi-step from initial state.
fn trace(&self, i_word: &InputWord) -> (State, Box<OutputWord>);
fn trace(&self, i_word: &[InputSymbol]) -> (State, Box<[OutputSymbol]>);
/// Return a copy of the input alphabet of the FSM.
fn input_alphabet(&self) -> Vec<InputSymbol>;
/// Return a copy of the output alphabet of the FSm.
/// Return a copy of the output alphabet of the FSM.
fn output_alphabet(&self) -> fnv::FnvHashSet<OutputSymbol>;
}
......@@ -43,8 +40,8 @@ pub trait ObservationTree {
fn insert_observation(
&mut self,
start: Option<State>,
input_seq: &InputWord,
output_seq: &OutputWord,
input_seq: &[InputSymbol],
output_seq: &[OutputSymbol],
) -> State;
// Getter methods now, a lot compared to insertion ;)
......@@ -58,14 +55,14 @@ pub trait ObservationTree {
fn get_observation(
&self,
start: Option<State>,
input_seq: &InputWord,
input_seq: &[InputSymbol],
) -> Option<Vec<OutputSymbol>>;
/// Given a state and an input, return the output and successor state, if defined.
fn get_out_succ(&self, src: State, input: InputSymbol) -> Option<(OutputSymbol, State)>;
/// Given a state and an input, return the output, if defined.
fn get_out(&self, src: State, input: InputSymbol) -> Option<OutputSymbol>;
/// Given a state and an *input sequence*, return the successor state, if defined.
fn get_succ(&self, src: State, input: &InputWord) -> Option<State>;
fn get_succ(&self, src: State, input: &[InputSymbol]) -> Option<State>;
/// Return an array of`(State, InputSymbol)` where &forall;q &in; `basis`, &forall;i &in; I : &delta;(q,i) is undefined.
fn no_succ_defined(&self, basis: &[State], sort: bool) -> Box<[(State, InputSymbol)]>;
......
......@@ -18,17 +18,12 @@ pub fn compute_witness<Tree: ObservationTree>(
first_state: State,
second_state: State,
) -> Option<Vec<InputSymbol>> {
let input_alphabet = (0..obs_tree.input_size())
.into_iter()
.map(|x| x as u16)
.map(InputSymbol::from)
.collect_vec();
let input_alphabet = toolbox::inputs_iterator(obs_tree.input_size()).collect_vec();
// We begin with the pair for which we want to compute the relation,
// the pair is considered to be the "initial state".
let mut work_list = VecDeque::from([(first_state, second_state)]);
while !work_list.is_empty() {
while let Some((fst, snd)) = work_list.pop_front() {
// Immediate return of None is safe, work_list will never be empty here.
let (fst, snd) = work_list.pop_front()?;
for (input, (fst_o_d, snd_o_d)) in input_alphabet.iter().filter_map(|&input| {
Some(input).zip(
obs_tree
......@@ -52,6 +47,8 @@ pub fn compute_witness<Tree: ObservationTree>(
None // no witness!
}
#[inline]
#[must_use]
pub fn tree_and_hyp_states_apart<Tree: ObservationTree>(
obs_tree: &Tree,
tree_state: State,
......@@ -85,17 +82,12 @@ pub fn states_are_apart<Tree: ObservationTree>(
first_state: State,
second_state: State,
) -> bool {
let input_alphabet = (0..obs_tree.input_size())
.into_iter()
.map(|x| x as u16)
.map(InputSymbol::from)
.collect_vec();
let input_alphabet = toolbox::inputs_iterator(obs_tree.input_size()).collect_vec();
// We begin with the pair for which we want to compute the relation,
// the pair is considered to be the "initial state".
let mut work_list = VecDeque::from([(first_state, second_state)]);
while !work_list.is_empty() {
while let Some((fst, snd)) = work_list.pop_front() {
// Immediate return of None is safe, work_list will never be empty here.
let (fst, snd) = work_list.pop_front().expect("Safe");
for (fst_o_d, snd_o_d) in input_alphabet.iter().filter_map(|&input| {
obs_tree
.get_out_succ(fst, input)
......
......@@ -5,7 +5,7 @@ use crate::{
},
automatadefs::{
mealy::{InputSymbol, Mealy, MealyBuilder, OutputSymbol, State},
traits::{FiniteStateMachine, InputWord, ObservationTree, OutputWord},
traits::{FiniteStateMachine, ObservationTree},
},
learner::apartness::{compute_witness, states_are_apart},
oracles::{equivalence::equivalence_trait::CounterExample, membership::Oracle as OutputOracle},
......@@ -56,7 +56,7 @@ where
oq_oracle: oracle,
input_size: input_alphabet.len(),
basis: vec![State::new(0)],
frontier_to_basis_map: Default::default(),
frontier_to_basis_map: HashMap::default(),
use_ly_ads,
consistency_ce_count: 0,
}
......@@ -89,21 +89,21 @@ where
#[allow(clippy::many_single_char_names)]
fn process_bin_search(
&mut self,
ce_input: &InputWord,
ce_output: &OutputWord,
ce_input: &[InputSymbol],
ce_output: &[OutputSymbol],
hypothesis: &Mealy,
) {
let q: State = hypothesis.trace(ce_input).0;
let r: State = RefCell::borrow(&self.oq_oracle)
.borrow_tree()
.get_succ(State::new(0), ce_input)
.unwrap();
.expect("Safe");
self.update_frontier_and_basis();
let temp = RefCell::borrow(&self.oq_oracle);
let o_tree = temp.borrow_tree();
if self.frontier_to_basis_map.contains_key(&r) || self.basis.contains(&r) {
return;
}
let temp = RefCell::borrow(&self.oq_oracle);
let o_tree = temp.borrow_tree();
// Get the prefix of the CE input that is leading to a frontier state.
let idx = {
let mut src = State::new(0);
......@@ -113,7 +113,7 @@ where
.get_succ(src, &[*input])
.expect("Obs-Tree should have CE included!");
if self.frontier_to_basis_map.contains_key(&dest) {
i = idx as u32;
i = idx;
break;
}
src = dest;
......@@ -121,17 +121,17 @@ where
i + 1
};
let eta = compute_witness(o_tree, q, r).unwrap_or_else(|| {
unreachable!("Could not find witness when it should exist, during CE processing!");
});
let eta = compute_witness(o_tree, q, r)
.expect("Could not find witness when it should exist, during CE processing!");
// We used idx inputs to get to the frontier.
let rho = &ce_input[..(idx as usize)];
let rho = &ce_input[..idx];
let x = rho.len();
let y = ce_input.len();
let h = ((x as f32 + y as f32) / 2_f32).floor() as u32;
let h = ((x as f32 + y as f32) / 2_f32).floor() as usize;
let sigma_1 = &ce_input[..(h as usize)];
let sigma_2 = &ce_input[(h as usize)..];
let sigma_1 = &ce_input[..h];
let sigma_2 = &ce_input[h..];
let q_p = hypothesis.trace(sigma_1).0;
let r_p = o_tree.get_succ(State::new(0), sigma_1).unwrap();
let access_q_p = o_tree.get_access_seq(q_p);
......@@ -222,23 +222,16 @@ where
self.check_frontier_consistency();
log::debug!("Basis set: {:?}", self.basis);
log::debug!("Frontier->Basis mapping: {:?}", self.frontier_to_basis_map);
let all_frontier_identified = self
.frontier_to_basis_map
.values()
.all(|cands| cands.len() == 1);
let input_alphabet = (0..self.input_size)
.into_iter()
.map(|x| x as u16)
.map(InputSymbol::from)
.collect::<rustc_hash::FxHashSet<_>>();
let is_singleton = |c: &Vec<State>| c.len() == 1;
let all_frontier_identified = self.frontier_to_basis_map.values().all(is_singleton);
let input_alphabet = toolbox::inputs_iterator(self.input_size).collect_vec();
let temp = RefCell::borrow(&self.oq_oracle);
let o_tree = temp.borrow_tree();
let all_basis_extended = self
.basis
.iter()
.copied()
.cartesian_product(input_alphabet.iter().copied())
.cartesian_product(input_alphabet)
.all(|(q, i)| o_tree.get_out(q, i).is_some());
all_frontier_identified && all_basis_extended
}
......@@ -425,8 +418,7 @@ where
} else {
let mut inputs = o_tree.get_access_seq(q);
inputs.push(i);
// let inputs = concat_slices(&[&access_seq, &[i]]);
let outputs = o_tree.get_observation(None, &inputs).unwrap();
let outputs = o_tree.get_observation(None, &inputs).expect("Safe");
return Some((inputs, outputs));
}
}
......
use crate::{
automatadefs::{
mealy::{InputSymbol, Mealy, OutputSymbol},
traits::{FiniteStateMachine, InputWord, OutputWord},
traits::FiniteStateMachine,
},
learner::{l_sharp::Lsharp, obs_tree::map_tree::ObsTree as ObsMapTree},
oracles::{
......@@ -47,7 +47,7 @@ pub fn learn_fsm(
input_map: FnvHashMap<String, InputSymbol>,
output_map: FnvHashMap<String, OutputSymbol>,
options: &Options,
logs: Option<Vec<(Box<InputWord>, Box<OutputWord>)>>,
logs: Option<Vec<(Box<[InputSymbol]>, Box<[OutputSymbol]>)>>,
) -> LearnResult {
let mealy_machine = Arc::new(sul.clone());
log::info!("Qsize : {}", mealy_machine.as_ref().states().len());
......
use crate::{
automatadefs::{
mealy::{InputSymbol, OutputSymbol, State},
traits::{InputWord, ObservationTree, OutputWord},
traits::ObservationTree,
},
util::data_structs::arena_tree::ArenaTree,
};
......@@ -37,8 +37,8 @@ impl ObservationTree for ObsTree {
fn insert_observation(
&mut self,
start: Option<State>,
input_seq: &InputWord,
output_seq: &OutputWord,
input_seq: &[InputSymbol],
output_seq: &[OutputSymbol],
) -> State {
input_seq
.iter()
......@@ -74,7 +74,7 @@ impl ObservationTree for ObsTree {
fn get_observation(
&self,
start: Option<State>,
input_seq: &InputWord,
input_seq: &[InputSymbol],
) -> Option<Vec<OutputSymbol>> {
input_seq
.iter()
......@@ -100,7 +100,7 @@ impl ObservationTree for ObsTree {
self.get_out_succ(src, input).map(|x| x.0)
}
fn get_succ(&self, src: State, input: &InputWord) -> Option<State> {
fn get_succ(&self, src: State, input: &[InputSymbol]) -> Option<State> {
input.iter().try_fold(src, |s, i| self._get_succ(s, *i))
}
......
use crate::{
automatadefs::{
mealy::{InputSymbol, OutputSymbol, State},
traits::{InputWord, ObservationTree, OutputWord},
traits::ObservationTree,
},
util::data_structs::arena_tree::ArenaTree,
};
......@@ -33,8 +33,8 @@ impl<S: std::hash::BuildHasher + Default + Send + Sync> ObservationTree for ObsT
fn insert_observation(
&mut self,
start: Option<State>,
input_seq: &InputWord,
output_seq: &OutputWord,
input_seq: &[InputSymbol],
output_seq: &[OutputSymbol],
) -> State {
Iterator::fold(
input_seq.iter().zip(output_seq.iter()),
......@@ -69,7 +69,7 @@ impl<S: std::hash::BuildHasher + Default + Send + Sync> ObservationTree for ObsT
fn get_observation(
&self,
start: Option<State>,
input_seq: &InputWord,
input_seq: &[InputSymbol],
) -> Option<Vec<OutputSymbol>> {
let input_len = input_seq.len();
Iterator::try_fold(
......@@ -95,7 +95,7 @@ impl<S: std::hash::BuildHasher + Default + Send + Sync> ObservationTree for ObsT
self.get_out_succ(src, input).map(|x| x.0)
}
fn get_succ(&self, src: State, input: &InputWord) -> Option<State> {
fn get_succ(&self, src: State, input: &[InputSymbol]) -> Option<State> {
input.iter().try_fold(src, |s, i| self._get_succ(s, *i))
}
......
/*!
L# Learning Library
#![warn(clippy::pedantic)]
This library implements the L# learning algorithm
as mentioned in `TBA`.
Source code is available [here](<https://gitlab.science.ru.nl/bharat/automata-lib>).
*/
//! L# Learning Library
//!
//! This library implements the L# learning algorithm
//! as mentioned in `A New Approach for Active Automata Learning Based on Apartness` at TACAS 2022,
//! [link](<https://rdcu.be/cO36l>).
//! Source code is available [here](<https://gitlab.science.ru.nl/sws/lsharp>).
pub mod ads;
pub mod automatadefs;
......
......@@ -2,7 +2,7 @@ use super::equivalence_trait::{CounterExample, EquivalenceOracle, InternalEquiva
use crate::{
automatadefs::{
mealy::{InputSymbol, Mealy, State},
traits::{FiniteStateMachine, InputWord, ObservationTree},
traits::{FiniteStateMachine, ObservationTree},
},
learner::apartness::{compute_witness, states_are_apart},
oracles::membership::Oracle as OQOracle,
......@@ -175,13 +175,13 @@ where
/// Run a single test sequence and return whether it is a CE or not.
#[inline]
#[must_use]
fn run_test(&mut self, hyp: &Mealy, input_seq: &InputWord) -> CounterExample {
fn run_test(&mut self, hyp: &Mealy, input_seq: &[InputSymbol]) -> CounterExample {
let hyp_output = hyp.trace(input_seq).1.to_vec();
let sut_output = RefCell::borrow_mut(&self.oq_oracle).output_query(input_seq);
if hyp_output == sut_output {
return None;
}
Some((input_seq.to_vec(), sut_output.to_vec()))
Some((input_seq.to_vec(), sut_output))
}
/// Compute the access sequences for the states
......
......@@ -2,7 +2,7 @@ use super::equivalence_trait::{CounterExample, EquivalenceOracle, ExternalEquiva
use crate::{
automatadefs::{
mealy::{InputSymbol, Mealy, OutputSymbol},
traits::{FiniteStateMachine, InputWord, ObservationTree},
traits::{FiniteStateMachine, ObservationTree},
},
oracles::membership::Oracle as OQOracle,
util::{learning_config::EqOracle, writers::overall as MealyWriter},
......@@ -203,7 +203,7 @@ impl<'a, T: ObservationTree + Send + Sync> SouchaOracle<'a, T> {
/// Run a single test sequence and return whether it is a CE or not.
#[inline]
#[must_use]
fn run_test(&mut self, hyp: &Mealy, input_seq: &InputWord) -> CounterExample {
fn run_test(&mut self, hyp: &Mealy, input_seq: &[InputSymbol]) -> CounterExample {
let hyp_output = hyp.trace(input_seq).1.to_vec();
let sut_output = RefCell::borrow_mut(&self.oq_oracle).output_query(input_seq);
if hyp_output == sut_output {
......
......@@ -2,7 +2,7 @@ use super::equivalence_trait::{CounterExample, EquivalenceOracle, InternalEquiva
use crate::{
automatadefs::{
mealy::{InputSymbol, Mealy, State},
traits::{FiniteStateMachine, InputWord, ObservationTree},
traits::{FiniteStateMachine, ObservationTree},
},
learner::apartness::{compute_witness, states_are_apart},
oracles::membership::Oracle as OQOracle,
......@@ -169,7 +169,7 @@ where
/// Run a single test sequence and return whether it is a CE or not.
#[inline]
#[must_use]
fn run_test(&mut self, hyp: &Mealy, input_seq: &InputWord) -> CounterExample {
fn run_test(&mut self, hyp: &Mealy, input_seq: &[InputSymbol]) -> CounterExample {
let hyp_output = hyp.trace(input_seq).1.to_vec();
let sut_output = RefCell::borrow_mut(&self.oq_oracle).output_query(input_seq);
if hyp_output == sut_output {
......
use super::system_under_learning::SystemUnderLearning;
use crate::automatadefs::{
mealy::{InputSymbol, Mealy, State},
traits::{FiniteStateMachine, InputWord, OutputWord},
mealy::{InputSymbol, Mealy, OutputSymbol, State},
traits::FiniteStateMachine,
};
use std::sync::Arc;
......@@ -30,7 +30,7 @@ impl SystemUnderLearning for Simulator {
}
}
fn step(&mut self, input_seq: &InputWord) -> Box<OutputWord> {
fn step(&mut self, input_seq: &[InputSymbol]) -> Box<[OutputSymbol]> {
let (dest, out_seq) = self.fsm.trace_from(self.curr_state, input_seq);
self.cnt_inputs += input_seq.len();
self.curr_state = dest;
......@@ -44,7 +44,7 @@ impl SystemUnderLearning for Simulator {
log::trace!("\tRESET");
}
fn trace(&mut self, input_seq: &InputWord) -> Box<OutputWord> {
fn trace(&mut self, input_seq: &[InputSymbol]) -> Box<[OutputSymbol]> {
self.reset();
let (_dest, out_seq) = self.fsm.trace_from(self.curr_state, input_seq);
self.cnt_inputs += input_seq.len();
......
use crate::automatadefs::{
mealy::{InputSymbol, Mealy},
traits::{InputWord, OutputWord},
};
use crate::automatadefs::mealy::{InputSymbol, Mealy, OutputSymbol};