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

General update

parent 834a6cb3
Pipeline #61077 failed with stages
in 4 minutes and 26 seconds
......@@ -35,6 +35,7 @@ strum_macros = "0.24"
num-format = "0.4.0"
priority-queue = "1.2.1"
libloading = "0.7"
cxx = "1.0"
[dev-dependencies]
assert_cmd = "2.0"
......
......@@ -9,7 +9,7 @@ use crate::{
},
oracles::{
equivalence::{
hads_tree::{HadsConfigBuilder, HadsTree, Mode, Prefix, Suffix},
hads::{ConfigBuilder as HadsConfigBuilder, Mode, Prefix, Suffix},
incomplete::iads::IadsEO,
sep_seq::SequenceOracle,
soucha::{ConfigBuilder as SouchaConfigBuilder, Oracle as SouchaOracle},
......@@ -68,7 +68,7 @@ pub fn learn_fsm<S: ::std::hash::BuildHasher + Default>(
output_map.iter().map(|(x, y)| (*y, x.clone())).collect();
let mut rng = StdRng::seed_from_u64(options.seed);
let hads_config = HadsConfigBuilder::default()
let mut hads_oracle_tree = HadsConfigBuilder::default()
.mode(Mode::Random)
.prefix(Prefix::Buggy)
.suffix(Suffix::Hads)
......@@ -76,14 +76,9 @@ pub fn learn_fsm<S: ::std::hash::BuildHasher + Default>(
.expected_rnd_length(options.extra_states)
.seed(options.seed)
.build()
.expect("HADS oracle config building failed.")
.oracle(Rc::clone(&oq_oracle), "./hybrid-ads/build/main")
.expect("Safe");
println!("HADS config : {:?}", hads_config);
let mut hads_oracle_tree = HadsTree::build(
Rc::clone(&oq_oracle),
hads_config,
"./hybrid-ads/build/main",
)
.expect("Safe");
let soucha_config = SouchaConfigBuilder::default()
.exec_loc("/Users/bharat/research/FSMlib/fsm_lib")
......@@ -148,7 +143,8 @@ pub fn learn_fsm<S: ::std::hash::BuildHasher + Default>(
&hypothesis,
&rev_input_map,
&rev_output_map,
);
)
.expect("Could not write hypothesis to file.");
}
let ideal_ce = shortest_separating_sequence(&sul.clone(), &hypothesis, None, None);
if ideal_ce.is_none() {
......
......@@ -10,7 +10,6 @@
pub mod ads;
pub mod automatadefs;
pub mod learner;
// pub mod new_sul;
pub mod oracles;
pub mod sul;
pub mod util;
......@@ -166,8 +166,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
result_vec.clear();
};
}
}
if Path::new(path_name).is_dir() {
} else if Path::new(path_name).is_dir() {
for file_dir_entry in Path::new(path_name)
.read_dir()
.unwrap_or_else(|_| panic!("Failed to read files in {}", path_name))
......@@ -218,6 +217,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
};
}
}
} else {
unreachable!("How can the path be neither a file nor a directory?");
}
Ok(())
}
......
......@@ -9,10 +9,11 @@ use crate::{
util::writers::overall as MealyWriter,
};
use derive_builder::Builder;
use fnv::FnvHashMap;
use fnv::FnvBuildHasher;
use itertools::Itertools;
use std::{
cell::RefCell,
collections::HashMap,
io::{BufRead, BufReader, Write},
process::{Child, Command, Stdio},
rc::Rc,
......@@ -21,7 +22,8 @@ use std::{
};
#[derive(Default, Builder, Debug)]
pub struct HadsConfig {
// #[builder(build_fn(skip))]
pub struct Config {
mode: Mode,
prefix: Prefix,
suffix: Suffix,
......@@ -30,6 +32,31 @@ pub struct HadsConfig {
#[builder(default = "5")]
expected_rnd_length: usize,
seed: u64,
#[builder(default = "Duration::new(20*60,0)")]
max_duration: Duration,
}
impl Config {
/// # Errors
/// If the path is not valid UTF-8 or cannot be canonicalised.
pub fn oracle<'a, T>(
self,
oq_oracle: Rc<RefCell<OQOracle<'a, T>>>,
location: &str,
) -> Result<Oracle<'a, T>, Box<dyn std::error::Error>> {
let hads_path = std::fs::canonicalize(location)?;
let location = hads_path
.to_str()
.ok_or("Path is not valid UTF-8")?
.to_string();
let config = self;
let ret = Oracle {
oq_oracle,
config,
location,
};
Ok(ret)
}
}
/**
......@@ -41,16 +68,16 @@ we write the hypothesis to the hybrid-ads program and use the tests generated
by the same. The implementation of the oracle is in the hybrid-ads submodule of
the repository.
*/
pub struct HadsTree<'a, T> {
pub struct Oracle<'a, T> {
oq_oracle: Rc<RefCell<OQOracle<'a, T>>>,
config: HadsConfig,
config: Config,
location: String,
}
impl<'a, T> EquivalenceOracle<'a, T> for HadsTree<'a, T>
impl<'a, T> EquivalenceOracle<'a, T> for Oracle<'a, T>
where
T: ObservationTree + Sync + Send,
{
type Options = HadsConfig;
type Options = Config;
fn get_counts(&mut self) -> (usize, usize) {
RefCell::borrow_mut(&self.oq_oracle).get_counts()
......@@ -77,11 +104,11 @@ where
.format(MealyWriter::MealyEncoding::Dot)
.build()
.unwrap();
let byte_hyp = MealyWriter::write_machine(
let byte_hyp = MealyWriter::write_machine::<FnvBuildHasher, _>(
&write_config,
hypothesis,
&FnvHashMap::default(),
&FnvHashMap::default(),
&HashMap::default(),
&HashMap::default(),
)
.expect("Writer did not return the byte-encoded mealy machine.");
let child_stdin = child.stdin.as_mut().unwrap();
......@@ -103,6 +130,8 @@ where
let hyp_output = hypothesis.trace(input_vec).1.to_vec();
let sut_output = oracle_mut_borrow.output_query(input_vec);
if hyp_output != sut_output {
// We need to kill the child, as we cannot signal the
// H-ADS process to terminate.
child
.kill()
.expect("Could not manage to kill H-ADS process.");
......@@ -113,36 +142,17 @@ where
}
}
buffer.clear();
if start.elapsed().as_secs() >= 20 * 60 {
if start.elapsed() >= self.config.max_duration {
return None;
}
}
}
}
impl<'a, T> HadsTree<'a, T>
impl<'a, T> Oracle<'a, T>
where
T: ObservationTree + Sync,
{
/// # Errors
/// Should not be any.
pub fn build(
output_oracle: Rc<RefCell<OQOracle<'a, T>>>,
config: HadsConfig,
location: &str,
) -> Result<Self, Box<dyn std::error::Error>> {
let hads_path = std::fs::canonicalize(location)?;
let ret = Self {
oq_oracle: output_oracle,
location: hads_path
.to_str()
.ok_or("Path is not valid UTF-8")?
.to_string(),
config,
};
Ok(ret)
}
fn hads_process(
&self,
lookahead_custom: Option<usize>,
......
......@@ -12,7 +12,7 @@ use std::cell::RefCell;
use std::rc::Rc;
/// Hybrid-ADS oracle.
pub mod hads_tree;
pub mod hads;
/// Soucha IADS algorithm.
pub mod incomplete;
/// Separating Sequence (tree-based) oracle.
......
......@@ -104,3 +104,10 @@ impl<S: std::hash::BuildHasher> SystemUnderLearning for External<'_, S> {
.collect()
}
}
#[cxx::bridge(namespace = "std")]
mod ffi {
unsafe extern "C++" {
include!("cxx-demo/include/blobstore.h");
}
}
......@@ -10,6 +10,8 @@ use crate::automatadefs::mealy::{InputSymbol, OutputSymbol};
pub mod external;
/// Simulate an SUL.
pub mod simulator;
/// External SUL which communicates over stdio.
pub mod stdio;
/// System Under Learning, as a trait.
///
......
use crate::automatadefs::mealy::{InputSymbol, OutputSymbol};
use bimap::BiHashMap;
use itertools::Itertools;
use std::{
collections::HashMap,
hash::BuildHasher,
io::{BufRead, BufReader, ErrorKind, Write},
path::Path,
process::{Child, ChildStderr, ChildStdin, ChildStdout, Command},
};
use super::SystemUnderLearning;
/// An external SUL struct interacting with the actual SUL over stdio.
///
/// ### NOTE
/// We initialise the child process during the construction of the struct
/// itself.
pub struct Stdio<S> {
cnt_inputs: usize,
cnt_resets: usize,
input_map: BiHashMap<String, InputSymbol, S, S>,
output_map: BiHashMap<String, OutputSymbol, S, S>,
#[allow(dead_code)]
child: Child,
sul_stdin: ChildStdin,
sul_stdout: BufReader<ChildStdout>,
reset_code: String,
#[allow(dead_code)]
sul_stderr: ChildStderr,
}
impl<S: BuildHasher + Default> Stdio<S> {
/// Initialise the struct.
///
/// ### Errors
/// If the SUL could not be initialised: the path is incorrect; or the input, output, or error streams
/// could not be obtained.
pub fn new(
loc: &dyn AsRef<Path>,
input_map: &HashMap<String, InputSymbol, S>,
reset_code: String,
) -> std::io::Result<Self> {
let i_m = input_map.iter().map(|(s, i)| (s.clone(), *i)).collect();
let canon_loc = std::fs::canonicalize(loc)?;
let mut child = Command::new(canon_loc)
.stdin(std::process::Stdio::piped())
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped())
.spawn()?;
let stderr_err =
std::io::Error::new(ErrorKind::Other, "Could not get handle to SUL's stderr.");
let stdout_err =
std::io::Error::new(ErrorKind::Other, "Could not get handle to SUL's stdout.");
let stdin_err =
std::io::Error::new(ErrorKind::Other, "Could not get handle to SUL's stdin.");
let sul_stdin = child.stdin.take().ok_or(stdin_err)?;
let sul_stdout = BufReader::new(child.stdout.take().ok_or(stdout_err)?);
let sul_stderr = child.stderr.take().ok_or(stderr_err)?;
Ok(Self {
cnt_inputs: 0,
cnt_resets: 0,
input_map: i_m,
output_map: BiHashMap::default(),
child,
sul_stdin,
sul_stdout,
sul_stderr,
reset_code,
})
}
}
impl<S: std::hash::BuildHasher> SystemUnderLearning for Stdio<S> {
fn step(&mut self, input_seq: &[InputSymbol]) -> Box<[OutputSymbol]> {
let mut o_buf = String::new();
let mut o_s = Vec::with_capacity(input_seq.len());
let i_s = input_seq
.iter()
.map(|i| self.input_map.get_by_right(i).expect("Safe"))
.collect_vec();
for i in i_s {
o_buf.clear();
self.sul_stdin
.write_all(i.as_bytes())
.expect("Writing input to SUL failed.");
self.sul_stdin
.write_all("\n".as_bytes())
.expect("Writing input to SUL failed.");
self.sul_stdout
.read_line(&mut o_buf)
.expect("Could not read from SUL.");
let mut osymb = self.output_map.get_by_left(&o_buf).copied();
if osymb.is_none() {
#[allow(clippy::cast_possible_truncation)]
self.output_map.insert(
o_buf.clone(),
OutputSymbol::new(self.output_map.len() as u16),
);
osymb = self.output_map.get_by_left(&o_buf).copied();
}
o_s.push(osymb.expect("Safe"));
}
self.cnt_inputs += input_seq.len();
log::trace!(" {:?} / {:?}", input_seq, o_s);
o_s.into_boxed_slice()
}
fn reset(&mut self) {
self.cnt_resets += 1;
self.sul_stdin
.write_all(self.reset_code.as_bytes())
.expect("Safe");
}
fn trace(&mut self, input_seq: &[InputSymbol]) -> Box<[OutputSymbol]> {
self.reset();
self.step(input_seq)
}
fn get_counts(&mut self) -> (usize, usize) {
let ret = (self.cnt_inputs, self.cnt_resets);
self.cnt_inputs = 0;
self.cnt_resets = 0;
ret
}
fn input_map(&self) -> Vec<(String, InputSymbol)> {
self.input_map
.iter()
.map(|(s, i)| (s.clone(), *i))
.collect()
}
fn output_map(&self) -> Vec<(String, OutputSymbol)> {
self.output_map
.iter()
.map(|(s, o)| (s.clone(), *o))
.collect()
}
}
......@@ -16,7 +16,7 @@ pub fn write_to_dot<S: BuildHasher + Default, M: FiniteStateMachine>(
fsm: &M,
input_rev_map: &HashMap<InputSymbol, String, S>,
output_rev_map: &HashMap<OutputSymbol, String, S>,
) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
) -> std::io::Result<Vec<u8>> {
let mut vec = Vec::new();
writeln!(&mut vec, "digraph g {{\n")?;
let mut state_info = HashMap::<_, _, S>::default();
......@@ -61,8 +61,8 @@ pub fn write_to_dot<S: BuildHasher + Default, M: FiniteStateMachine>(
"\t s{} -> s{} [label=\"{} / {}\"];",
src.raw(),
dest.raw(),
input_rev_map.get(&i_s).ok_or("Input absent from map")?,
output_rev_map.get(&o_s).ok_or("Output absent from map")?
input_rev_map.get(&i_s).expect("Input absent from map"),
output_rev_map.get(&o_s).expect("Output absent from map")
)?;
}
}
......
......@@ -24,9 +24,6 @@ pub struct WriteConfig {
}
impl WriteConfig {
fn to_file(&self) -> bool {
self.file_name.is_some()
}
fn encoding(&self) -> MealyEncoding {
self.format
}
......@@ -35,34 +32,31 @@ impl WriteConfig {
}
}
/// # Errors
/// If we're unable to write to a file.
pub fn write_machine<S: BuildHasher + Default, Machine: FiniteStateMachine>(
config: &WriteConfig,
fsm: &Machine,
input_rev_map: &HashMap<InputSymbol, String, S>,
output_rev_map: &HashMap<OutputSymbol, String, S>,
) -> Option<Vec<u8>> {
) -> std::io::Result<Vec<u8>> {
let byte_mealy = match config.encoding() {
MealyEncoding::Soucha => {
super::soucha_fmt::write_to_soucha(fsm, input_rev_map, output_rev_map)
}
MealyEncoding::Soucha => super::soucha_fmt::write_to_soucha::<S, Machine>(fsm),
MealyEncoding::Dot => super::dot_fmt::write_to_dot(fsm, input_rev_map, output_rev_map),
}?;
if let Some(file) = config.file() {
write_to_file(&file, &byte_mealy)?;
}
.ok()?;
if config.to_file() {
write_to_file(&config.file().expect("Safe"), &byte_mealy);
return None;
}
Some(byte_mealy)
Ok(byte_mealy)
}
/// Accepts a file name, fsm, input, and output conversion maps and writes FSM
/// to the file.
///
/// # Panics
/// # Errors
/// If `file` could not be created or written to.
pub fn write_to_file(file: &str, byte_mealy: &[u8]) {
let mut f =
std::fs::File::create(file).unwrap_or_else(|_| panic!("Could not create file {}", file));
f.write_all(byte_mealy)
.unwrap_or_else(|_| panic!("Write exception when writing to file {}", file));
pub fn write_to_file(file: &str, byte_mealy: &[u8]) -> std::io::Result<()> {
let mut f = std::fs::File::create(file)?;
f.write_all(byte_mealy)?;
Ok(())
}
......@@ -3,7 +3,7 @@ use crate::automatadefs::{
FiniteStateMachine,
};
use itertools::Itertools;
use std::{collections::HashMap, hash::BuildHasher, io::Write};
use std::{hash::BuildHasher, io::Write};
/// Convert a Mealy Machine to Soucha's textual format.
///
......@@ -11,9 +11,7 @@ use std::{collections::HashMap, hash::BuildHasher, io::Write};
/// If writing to the internal buffers fail.
pub fn write_to_soucha<S: BuildHasher + Default, Machine: FiniteStateMachine>(
fsm: &Machine,
_input_rev_map: &HashMap<InputSymbol, String, S>,
_output_rev_map: &HashMap<OutputSymbol, String, S>,
) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
) -> std::io::Result<Vec<u8>> {
let t = 2; // Always a mealy machine.
let r = 1; // Always reduced.
let num_states = fsm.states().len(); // Num states
......
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