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

IADS update

parent afff8993
......@@ -10,7 +10,6 @@ use crate::{
equivalence::{
automata::shortest_separating_sequence,
equivalence_trait::{CounterExample, EquivalenceOracle, InternalEquivalenceOracle},
incomplete::tree::TreeCons,
},
membership::Oracle as OQOracle,
},
......@@ -28,12 +27,20 @@ use rand::{
use rand_distr::{Distribution, Uniform};
use rayon::iter::{IntoParallelIterator, IntoParallelRefIterator, ParallelIterator};
use rustc_hash::{FxHashMap, FxHashSet};
use std::{cell::RefCell, collections::VecDeque, rc::Rc, time::Instant};
use std::{
cell::RefCell,
collections::{HashMap, HashSet, VecDeque},
rc::Rc,
time::Instant,
};
#[allow(clippy::module_name_repetitions)]
pub struct IadsEO<'a, T> {
oq_oracle: Rc<RefCell<OQOracle<'a, T>>>,
lookahead: usize,
seed: u64,
revisit: usize,
ads_map: FnvHashMap<Vec<State>, AdsTree>,
frontier_states: FnvHashMap<State, Vec<State>>, // frontier -> basis
// transitions from the basis using an input that goes to the frontier
frontier_trans: FnvHashSet<(State, InputSymbol)>,
......@@ -48,8 +55,10 @@ where
oq_oracle,
lookahead,
seed,
frontier_states: Default::default(),
frontier_trans: Default::default(),
revisit: 0,
ads_map: HashMap::default(),
frontier_states: HashMap::default(),
frontier_trans: HashSet::default(),
}
}
}
......@@ -81,24 +90,21 @@ where
self.frontier_trans = self.frontier_transitions(hyp);
let access_map = self.access_map(hyp);
// let access_map = random_access_map(hyp, self.seed);
let rig = RandomInfixGenerator::new(self.seed, hyp.input_alphabet().len(), 0, lookahead);
let rnd_infix_gen =
RandomInfixGenerator::new(self.seed, hyp.input_alphabet().len(), 0, lookahead);
log::info!("Running AIC sequences.");
let splitting_tree =
TreeCons::construct(hyp, &hyp_states.iter().copied().collect_vec(), false);
let mut splitting_tree = SplittingTree::new(hyp, &hyp_states.iter().copied().collect_vec());
// TreeCons::construct(hyp, &hyp_states.iter().copied().collect_vec(), false);
log::info!("Finished making ST-IADS.");
let mut rng = StdRng::seed_from_u64(self.seed);
let access_vec = access_map.into_values().collect_vec();
let mut time_now = Instant::now();
for (cnt, infix) in rig.generate().enumerate() {
let st_num = rng.gen_range(0..num_states);
let access = access_vec.get(st_num).expect("Safe");
// let access = access_vec.choose(&mut rng).expect("Safe");
for (cnt, infix) in rnd_infix_gen.generate().enumerate() {
let access = access_vec.choose(&mut rng).expect("Safe");
let prefix = toolbox::concat_slices(&[access, &infix]);
let ce = self.run_ads_test(&prefix, hyp, &splitting_tree, rng.gen());
let ce = self.run_ads_test(&prefix, hyp, &mut splitting_tree, rng.gen());
if ce.is_some() {
let mut rng: StdRng = SeedableRng::seed_from_u64(self.seed);
self.seed = rng.gen::<u64>();
self.seed = rng.gen();
return ce;
}
if cnt % 10000 == 0 {
......@@ -111,42 +117,12 @@ where
}
}
/// Checks whether the hypothesis and the observation tree agree after a given prefix.
#[must_use]
fn _check_consistency_from<T: ObservationTree + Sync>(
prefix: &[InputSymbol],
o_tree: &T,
hypothesis: &Mealy,
) -> CounterExample {
let tree_start = o_tree.get_succ(State::new(0), prefix).expect("Safe");
let hyp_start = hypothesis.trace(prefix).0;
let input_size = hypothesis.input_alphabet().len();
let mut queue = VecDeque::from([(tree_start, hyp_start)]);
while let Some((q, r)) = queue.pop_front() {
for i in toolbox::inputs_iterator(input_size) {
let tree_hyp_next_pair = o_tree
.get_out_succ(q, i)
.zip(Some(hypothesis.step_from(r, i)));
if let Some(((out_t, dest_t), (dest_h, out_h))) = tree_hyp_next_pair {
if out_h == out_t {
queue.push_back((dest_t, dest_h));
} else {
let mut inputs = o_tree.get_access_seq(q);
inputs.push(i);
let outputs = o_tree.get_observation(None, &inputs).expect("Safe");
return Some((inputs, outputs));
}
}
}
}
None
}
impl<'a, T> IadsEO<'a, T>
where
T: ObservationTree + Send + Sync,
{
pub fn load_frontier_states(&mut self, frontier: FnvHashMap<State, Vec<State>>) {
self.revisit = 0;
self.frontier_states = frontier;
}
......@@ -155,12 +131,12 @@ where
&mut self,
prefix: &[InputSymbol],
hyp: &Mealy,
splitting_tree: &SplittingTree,
splitting_tree: &mut SplittingTree,
seed: u64,
) -> CounterExample {
let hyp_dest = hyp.trace(prefix).0;
// State hyp_dest must be separated from the following states.
let states_to_sep = self.must_sep_from(prefix, hyp);
let mut states_to_sep = self.must_sep_from(prefix, hyp);
log::debug!("States to separate: {:?}", &states_to_sep);
match &states_to_sep[..] {
[] => {
......@@ -185,16 +161,24 @@ where
_ => {
// There are at least three states (in total) we need to split,
// so it makes sense to use an IADS now.
let mut st_ads = AdsTree::construct(hyp, splitting_tree, &states_to_sep, seed);
states_to_sep.sort_unstable();
let st_ads = self
.ads_map
.entry(states_to_sep.clone())
.or_insert_with(|| {
AdsTree::construct(hyp, splitting_tree, &states_to_sep, seed)
});
// let stree = TreeCons::construct(hyp, &states_to_sep, false);
// let mut st_ads = AdsTree::construct(hyp, &stree, &states_to_sep, seed);
let other_states = states_to_sep
.iter()
.copied()
.filter(|&x| x != hyp_dest)
.collect_vec();
st_ads.randomise_root(hyp_dest, &other_states);
log::debug!("IADS:{:?}", &st_ads);
let ce =
RefCell::borrow_mut(&self.oq_oracle).ads_equiv_test(&mut st_ads, prefix, hyp);
let ce = RefCell::borrow_mut(&self.oq_oracle).ads_equiv_test(st_ads, prefix, hyp);
st_ads.reset_to_root();
ce
}
......@@ -203,21 +187,22 @@ where
/// Returns the states which `tree_state` is not separated from in the observation tree.
#[must_use]
fn must_sep_from(&self, prefix: &[InputSymbol], hyp: &Mealy) -> Vec<State> {
fn must_sep_from(&mut self, prefix: &[InputSymbol], hyp: &Mealy) -> Vec<State> {
let temp = RefCell::borrow(&self.oq_oracle);
let tree = temp.borrow_tree();
let tree_dest = tree.get_succ(State::new(0), prefix);
let mut other_states = hyp.states().into_iter().collect();
if tree_dest == Some(State::new(6000)) {
let _x = "";
}
let ts = match tree_dest {
Some(ts) => ts,
None => return other_states,
};
let org_num = other_states.len();
let are_not_sep = |s1: &State| !states_are_apart(tree, *s1, ts);
other_states.retain(are_not_sep);
if other_states.len() < org_num {
self.revisit += 1;
}
other_states
}
......@@ -250,7 +235,7 @@ where
if hyp_output == sut_output {
return None;
}
Some((input_seq.to_vec(), sut_output.to_vec()))
Some((input_seq.to_vec(), sut_output))
}
#[allow(unused)]
......@@ -287,7 +272,6 @@ where
.collect()
}
#[allow(unused)]
pub(crate) fn access_map(&self, hyp: &Mealy) -> FxHashMap<State, Vec<InputSymbol>> {
let temp = RefCell::borrow(&self.oq_oracle);
let o_tree = temp.borrow_tree();
......@@ -317,15 +301,11 @@ fn random_access_map(fsm: &Mealy, seed: u64) -> FxHashMap<State, Vec<InputSymbol
let mut rng: StdRng = SeedableRng::seed_from_u64(seed);
let mut work_list = VecDeque::from([State::new(0)]);
ret.insert(State::new(0), vec![]);
let uni_dist = Uniform::new_inclusive(0.0, 1.0);
while !work_list.is_empty() {
let state = {
let sample = uni_dist.sample(&mut rng);
let scaled_sample = {
let scaled = (work_list.len() as f64 * sample).floor() as usize;
scaled.clamp(0, work_list.len() - 1)
};
work_list.remove(scaled_sample).expect("Safe")
let uni_dist = Uniform::new(0, work_list.len());
let idx = uni_dist.sample(&mut rng);
work_list.remove(idx).expect("Safe")
};
let mut inputs = fsm.input_alphabet().into_iter().collect_vec();
inputs.shuffle(&mut rng);
......@@ -363,6 +343,6 @@ mod tests {
fn all_states_found(#[case] file_name: &str) {
let (fsm, _input_map, _) = read_mealy_from_file(file_name);
let access_states: HashSet<_, _> = random_access_map(&fsm, 10).into_keys().collect();
assert_eq!(fsm.states(), access_states)
assert_eq!(fsm.states(), access_states);
}
}
use super::tree::SplittingTree;
use super::tree::{Helpers, SplittingTree}; //, TreeCons};
use crate::{
ads::traits::{AdaptiveDistinguishingSequence, AdsStatus},
automatadefs::{
......@@ -14,7 +14,7 @@ use rand::{
};
use rayon::iter::{IntoParallelIterator, ParallelIterator};
use rustc_hash::{FxHashMap, FxHashSet};
use std::collections::VecDeque;
use std::collections::{HashMap, VecDeque};
#[derive(Debug, Default, Clone, PartialEq)]
pub struct AdsNode {
......@@ -75,7 +75,7 @@ impl AdsTree {
pub fn construct(
fsm: &Mealy,
splitting_tree: &SplittingTree,
splitting_tree: &mut SplittingTree,
initial_label: &[State],
seed: u64,
) -> Self {
......@@ -89,7 +89,12 @@ impl AdsTree {
ret
}
fn _construct(&mut self, splitting_tree: &SplittingTree, fsm: &Mealy, initial_label: &[State]) {
fn _construct(
&mut self,
og_splitting_tree: &mut SplittingTree,
fsm: &Mealy,
initial_label: &[State],
) {
// let init_root = splitting_tree.get_lca(initial_label);
let all_states = initial_label.to_vec(); // splitting_tree.arena[init_root.to_index()].val.label.clone();
let mut undistinguished = VecDeque::from([all_states]);
......@@ -98,22 +103,31 @@ impl AdsTree {
self.roots.push(curr_root_idx);
let mut unprocessed = VecDeque::from([curr_root_idx]);
while let Some(mut curr_node_idx) = unprocessed.pop_front() {
let st_node_idx = {
let block: Vec<_> = self.tree.arena[curr_node_idx]
.val
.current
.iter()
.copied()
.unique()
.collect();
splitting_tree.get_lca(&block).to_index()
};
let (&sep, xfer_seq) = splitting_tree.arena[st_node_idx]
let block: Vec<_> = self.tree.arena[curr_node_idx]
.val
.sep_seq
.seq()
.split_last()
.expect("Separating sequence cannot be empty!");
.current
.iter()
.copied()
.unique()
.collect();
let sep_seq = {
if let Some(seq) = og_splitting_tree.separating_sequence(&block) {
seq
} else {
let mut h = Helpers::default();
og_splitting_tree.construct(fsm, &block, &mut h);
// let new_stree = TreeCons::construct(fsm, &block, false);
og_splitting_tree.separating_sequence(&block).expect("Safe")
}
};
// let st_node_idx = { og_splitting_tree.get_lca(&block).to_index() };
// let (&sep, xfer_seq) = og_splitting_tree.arena[st_node_idx]
// .val
// .sep_seq
// .seq()
// .split_last()
// .expect("Separating sequence cannot be empty!");
let (&sep, xfer_seq) = sep_seq.split_last().expect("Sep seq cannot be empty!");
for &input in xfer_seq {
let r = self.mut_ref_at(curr_node_idx);
r.input = Some(input);
......@@ -143,7 +157,7 @@ impl AdsTree {
.iter()
.zip(r.initial.iter())
.map(|(&cur, &ini)| (fsm.step_from(cur, sep), ini))
.fold(Default::default(), |mut acc, ((new_curr, out), ini)| {
.fold(HashMap::default(), |mut acc, ((new_curr, out), ini)| {
acc.entry(out).or_default().push((new_curr, ini));
acc
});
......@@ -230,8 +244,8 @@ impl AdaptiveDistinguishingSequence for AdsTree {
impl AdsTree {
fn validate(&self, fsm: &Mealy) {
let mut work_list = VecDeque::from_iter(self.roots.iter().copied());
type LocalIdentType = Vec<(Vec<InputSymbol>, Vec<OutputSymbol>)>;
let mut work_list: VecDeque<_> = self.roots.iter().copied().collect();
let mut separating_seqs: FxHashMap<State, LocalIdentType> = FxHashMap::default();
while let Some(root) = work_list.pop_front() {
let ads_sep_seq = self.validate_ads(root);
......@@ -264,9 +278,9 @@ impl AdsTree {
for (&output, &next_node) in &self.tree.ref_at(curr_node).chidren {
let mut new_ip_seq = unique_ip.clone();
new_ip_seq.push(next_ip);
let mut new_op_seq = unique_op.clone();
new_op_seq.push(output);
work_list.push_back((new_ip_seq, new_op_seq, next_node));
let mut new_output_seq = unique_op.clone();
new_output_seq.push(output);
work_list.push_back((new_ip_seq, new_output_seq, next_node));
}
} else {
// No next node, this is a root node, we must send back the paths.
......@@ -293,10 +307,7 @@ impl AdsTree {
#[cfg(test)]
mod tests {
use super::*;
use crate::{
oracles::equivalence::incomplete::tree::TreeCons,
util::parsers::machine::read_mealy_from_file,
};
use crate::util::parsers::machine::read_mealy_from_file;
use std::path::Path;
#[test]
......@@ -304,8 +315,8 @@ mod tests {
let file_name = Path::new("iads_example.dot");
let (fsm, _, _) = read_mealy_from_file(file_name.to_str().expect("Safe"));
let all_states = fsm.states().into_iter().collect_vec();
let tree = TreeCons::construct(&fsm, &all_states, false);
let ads = AdsTree::construct(&fsm, &tree, &all_states, 0);
let mut tree = SplittingTree::new(&fsm, &all_states);
let ads = AdsTree::construct(&fsm, &mut tree, &all_states, 0);
assert_eq!(ads.roots.len(), 3);
}
}
......@@ -4,7 +4,14 @@ use derive_more::From;
pub struct Index(usize);
impl Index {
#[inline]
pub fn to_index(self) -> usize {
self.0
}
}
impl From<Index> for usize {
fn from(idx: Index) -> Self {
idx.to_index()
}
}
use super::tree::{Helpers, Node};
use super::tree::Node;
use crate::automatadefs::{
mealy::{InputSymbol, Mealy},
traits::FiniteStateMachine,
......@@ -6,34 +6,20 @@ use crate::automatadefs::{
use rustc_hash::{FxHashMap, FxHashSet};
use std::collections::HashMap;
pub(super) fn score_sep(r: &Node, x: InputSymbol, fsm: &Mealy, helpers: &Helpers) -> usize {
_score(r, x, None, fsm, helpers)
pub(super) fn score_sep(r: &Node, x: InputSymbol, fsm: &Mealy) -> usize {
score(r, x, None, fsm)
}
pub(super) fn score_xfer(
r: &Node,
x: InputSymbol,
r_x: &Node,
fsm: &Mealy,
helpers: &Helpers,
) -> usize {
_score(r, x, r_x, fsm, helpers)
pub(super) fn score_xfer(r: &Node, x: InputSymbol, r_x: &Node, fsm: &Mealy) -> usize {
score(r, x, r_x, fsm)
}
fn _score<'a>(
r: &Node,
x: InputSymbol,
r_x: impl Into<Option<&'a Node>>,
fsm: &Mealy,
_helpers: &Helpers,
) -> usize {
fn score<'a>(r: &Node, x: InputSymbol, r_x: impl Into<Option<&'a Node>>, fsm: &Mealy) -> usize {
let w = {
let mut ret = vec![x];
if let Some(rx) = r_x.into() {
let seq: &[_] = rx.sep_seq.seq();
if seq.is_empty() {
panic!("Sep Seq should not be empty.");
}
assert!(!seq.is_empty(), "Sep Seq should not be empty.");
ret.extend_from_slice(seq);
}
ret
......@@ -43,7 +29,7 @@ fn _score<'a>(
.iter()
.map(|&s| (s, fsm.trace_from(s, &w)))
.map(|(s, (d, o))| (s, o, d))
.fold(Default::default(), |mut acc, (s, o, d)| {
.fold(HashMap::default(), |mut acc, (s, o, d)| {
acc.entry(o).or_default().entry(d).or_default().insert(s);
acc
});
......@@ -65,7 +51,6 @@ fn _score<'a>(
let mut num_undist_states = 0;
let n_r = r.label.len();
let e = w.len();
// println!("Output_dest_map : {:?}", output_dest_map);
for (_resp, dest_src_map) in output_dest_map {
let src_states: FxHashSet<_> = dest_src_map.values().flatten().copied().collect();
let dest_states: FxHashSet<_> = dest_src_map.keys().copied().collect();
......
......@@ -3,7 +3,7 @@ use std::{fmt::Debug, hash::Hash};
/// Helper struct to hold all the state pairs.
/// `K` is the type of the states of the FSM.
#[derive(Debug, Default)]
#[derive(Debug, Default, Clone)]
pub(super) struct SeparatingNodes<K, V> {
/// Each pair of states maps to the node index which separates the pair.
inner: FnvHashMap<(K, K), V>,
......
......@@ -15,16 +15,6 @@ pub(super) enum Type {
Useless,
}
impl Type {
/// `true` if the split is an injective separating or transfer input.
pub(super) fn is_injective(self) -> bool {
matches!(self, Self::XferInj | Self::SepInj)
}
fn _is_non_inj(self) -> bool {
matches!(self, Self::SepNonInj | Self::XferNonInj)
}
}
#[derive(Debug, Default)]
struct SplitMap {
output_dest_map: FxHashMap<Box<[OutputSymbol]>, FxHashMap<State, FxHashSet<State>>>,
......@@ -32,7 +22,7 @@ struct SplitMap {
impl SplitMap {
fn analyse(fsm: &Mealy, block: &[State], is: &[InputSymbol]) -> Self {
let mut output_dest_map: FxHashMap<_, FxHashMap<_, FxHashSet<_>>> = Default::default();
let mut output_dest_map: FxHashMap<_, FxHashMap<_, FxHashSet<_>>> = HashMap::default();
block
.iter()
.map(|&s| (s, fsm.trace_from(s, is)))
......@@ -123,9 +113,6 @@ impl SplitInfo {
}
pub(super) fn i_type(&self) -> Type {
if self.useless {
return Type::Useless;
}
if self.is_separating() {
if self._is_valid() {
Type::SepInj
......
This diff is collapsed.
......@@ -184,7 +184,7 @@ where
.get_observation(None, input_seq)
.unwrap_or_else(|| self.sul.trace(input_seq).to_vec());
let _ = self.obs_tree.insert_observation(None, input_seq, &out_seq);
out_seq.to_vec()
out_seq
}
/// Make an adaptive output query of the form (prefix -> infix -> ADS).
......@@ -221,7 +221,7 @@ where
input_seq.append(&mut suffix_inputs); //concat_slices(&[prefix, &suffix_inputs]);
let output_seq = toolbox::concat_slices(&[&prefix_out, &suffix_outputs]);
let _ = self.add_observation(input_seq, &output_seq);
(input_seq.to_vec(), output_seq)
(input_seq.clone(), output_seq)
}
// Assuming the prefix has been sent to the SUL, perform the adaptive query.
......@@ -260,11 +260,16 @@ where
return Some((prefix.to_vec(), tree_prefix_out));
}
let ts = self.obs_tree.get_succ(State::new(0), prefix).expect("Safe");
assert!(ads.next_input(None).is_ok());
ads.next_input(None)
.ok()
.expect("ADS should have at least one input.");
let ads_res = self.answer_ads_from_tree(ads, ts);
match ads_res {
Ok((ads_inputs, ads_outputs)) => {
println!("Tree had a reply, duplicate query!");
println!("ts: {}", ts);
let hs = fsm.trace(prefix).0;
println!("hs: {}", hs);
let mut input_seq = Vec::from(prefix);
input_seq.extend(ads_inputs.iter());
let mut output_seq = tree_prefix_out;
......@@ -285,8 +290,8 @@ where
log::info!("Found CE in the random prefix.");
return Some((prefix.to_vec(), prefix_out.to_vec()));
}
let mut inputs_sent = Vec::from_iter(prefix.iter().copied());
let mut outputs_received = Vec::from_iter(prefix_out.iter().copied());
let mut inputs_sent = prefix.to_vec();
let mut outputs_received = prefix_out.to_vec();
let mut hyp_out;
let mut prev_output = None;
loop {
......@@ -322,11 +327,7 @@ where
let mut queue = VecDeque::from([(State::new(0), State::new(0))]);
while !queue.is_empty() {
let (q, r) = queue.pop_front().expect("Always safe!");
for i in (0..hypothesis.input_alphabet().len())
.into_iter()
.map(|x| x as u16)
.map(InputSymbol::from)
{
for i in toolbox::inputs_iterator(hypothesis.input_alphabet().len()) {
if let Some(((out_tree, dest_tree), (dest_hyp, out_hyp))) = self
.obs_tree
.get_out_succ(q, i)
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment