diff --git a/.gitignore b/.gitignore index 471e159..eafb637 100644 --- a/.gitignore +++ b/.gitignore @@ -116,3 +116,4 @@ Temporary Items *.cbor *.graphml +.dot diff --git a/src/examples.rs b/src/examples.rs deleted file mode 100644 index 403f785..0000000 --- a/src/examples.rs +++ /dev/null @@ -1,445 +0,0 @@ -use super::rsprocess::structure::{RSset, RSsystem}; -use super::rsprocess::{graph, rsdot, translator}; -use super::rsprocess::translator::Translator; -use super::rsprocess::{frequency, perpetual, statistics, transitions}; -use super::rsprocess::serialize; - -use std::env; -use std::fs::{self, File}; -use std::io; -use std::io::prelude::*; -use std::rc::Rc; - -// grammar is defined in lib.rs, calling lalrpop_mod! twice, generates twice -// the code -use super::grammar; - -fn read_file( - translator: &mut Translator, - path: std::path::PathBuf, - parser: F -) -> std::io::Result -where - F: Fn(&mut Translator, String) -> T -{ - // we read the file with a buffer - let f = fs::File::open(path.clone())?; - let mut buf_reader = io::BufReader::new(f); - let mut contents = String::new(); - buf_reader.read_to_string(&mut contents)?; - - // parse - let result = parser(translator, contents); - - Ok(result) -} - -fn save_file( - contents: String, - path: std::path::PathBuf -) -> std::io::Result<()> -{ - let mut f = fs::File::create(path.clone())?; - write!(f, "{contents}")?; - Ok(()) -} - -fn parser_system(translator: &mut Translator, contents: String) -> RSsystem { - grammar::SystemParser::new() - .parse(translator, &contents) - .unwrap() -} - -fn parser_experiment(translator: &mut Translator, contents: String) -> (Vec, Vec) { - grammar::ExperimentParser::new() - .parse(translator, &contents) - .unwrap() -} - - - -// equivalent main_do(stat) or main_do(stat, MissingE) -pub fn stats() -> std::io::Result<()> { - let mut translator = Translator::new(); - - let mut path = env::current_dir()?; - // file to read is inside testing/ - path = path.join("testing/first.system"); - let system = read_file(&mut translator, path, parser_system)?; - - // print statistics to screan - println!("{}", statistics::of_RSsystem(&translator, &system)); - - // to write to file: - //// path = path.with_extension("stats"); - //// let mut f = std::fs::File::create(path)?; - //// writeln!(f, "{}", statistics::of_RSsystem(translator, &result))?; - - Ok(()) -} - -// equivalent to main_do(target, E) -pub fn target() -> std::io::Result<()> { - let mut translator = Translator::new(); - - let mut path = env::current_dir()?; - // file to read is inside testing/ - path = path.join("testing/first.system"); - let system = read_file(&mut translator, path, parser_system)?; - - // the system needs to terminate to return - let res = match transitions::target(&system) { - Ok(o) => o, - Err(e) => { - println!("Error computing target: {e}"); - return Ok(()); - } - }; - - println!( - "After {} steps we arrive at state:\n{}", - res.0, - translator::RSsetDisplay::from(&translator, &res.1) - ); - - Ok(()) -} - -// equivalent to main_do(run,Es) -pub fn run() -> std::io::Result<()> { - let mut translator = Translator::new(); - - let mut path = env::current_dir()?; - // file to read is inside testing/ - path = path.join("testing/first.system"); - let system = read_file(&mut translator, path, parser_system)?; - - // the system needs to terminate to return - let res = match transitions::run_separated(&system) { - Ok(o) => o, - Err(e) => { - println!("Error computing target: {e}"); - return Ok(()); - } - }; - - println!("The trace is composed by the set of entities:"); - - for (e, _c, _t) in res { - println!("{}", translator::RSsetDisplay::from(&translator, &e)); - } - - Ok(()) -} - -// equivalent to main_do(loop,Es) -pub fn hoop() -> std::io::Result<()> { - let mut translator = Translator::new(); - - let mut path = env::current_dir()?; - // file to read is inside testing/ - path = path.join("testing/first.system"); - let system = read_file(&mut translator, path, parser_system)?; - - // we retrieve the id for "x" and use it to find the corresponding loop - let res = - match perpetual::lollipops_only_loop_named(system, - translator.encode("x")) { - Some(o) => o, - None => { - println!("No loop found."); - return Ok(()); - } - }; - - println!("The loop is composed by the sets:"); - - for e in res { - println!("{}", translator::RSsetDisplay::from(&translator, &e)); - } - - Ok(()) -} - -// equivalent to main_do(freq, PairList) -pub fn freq() -> std::io::Result<()> { - let mut translator = Translator::new(); - - let mut path = env::current_dir()?; - // file to read is inside testing/ - path = path.join("testing/first.system"); - let system = read_file(&mut translator, path, parser_system)?; - - let res = match frequency::naive_frequency(&system) { - Ok(f) => f, - Err(e) => { - println!("Error computing target: {e}"); - return Ok(()); - } - }; - - println!( - "Frequency of encountered symbols:\n{}", - translator::FrequencyDisplay::from(&translator, &res) - ); - - Ok(()) -} - -// equivalent to main_do(limitfreq, PairList) -pub fn limit_freq() -> std::io::Result<()> { - let mut translator = Translator::new(); - - let mut path = env::current_dir()?; - // file to read is inside testing/ - path = path.join("testing/first.system"); - let system = read_file(&mut translator, path, parser_system)?; - - path = env::current_dir()?; - path = path.join("testing/first.experiment"); - let (_, sets) = read_file(&mut translator, path, parser_experiment)?; - - let res = match frequency::limit_frequency(&sets, - &system.reaction_rules, - &system.available_entities) { - Some(e) => e, - None => {return Ok(());} - }; - - println!( - "Frequency of encountered symbols:\n{}", - translator::FrequencyDisplay::from(&translator, &res) - ); - - Ok(()) -} - -// equivalent to main_do(fastfreq, PairList) -pub fn fast_freq() -> std::io::Result<()> { - let mut translator = Translator::new(); - - let mut path = env::current_dir()?; - // file to read is inside testing/ - path = path.join("testing/first.system"); - let system = read_file(&mut translator, path, parser_system)?; - - path = env::current_dir()?; - path = path.join("testing/first.experiment"); - let (weights, sets) = read_file(&mut translator, path, parser_experiment)?; - - let res = match frequency::fast_frequency(&sets, - &system.reaction_rules, - &system.available_entities, - &weights) { - Some(e) => e, - None => {return Ok(());} - }; - - println!( - "Frequency of encountered symbols:\n{}", - translator::FrequencyDisplay::from(&translator, &res) - ); - - Ok(()) -} - -// equivalent to main_do(digraph, Arcs) -pub fn digraph() -> std::io::Result<()> { - let mut translator = Translator::new(); - - let mut path = env::current_dir()?; - // file to read is inside testing/ - path = path.join("testing/first.system"); - let system = read_file(&mut translator, path, parser_system)?; - - // the system needs to terminate to return - let res = match graph::digraph(system) { - Ok(o) => o, - Err(e) => { - println!("Error computing target: {e}"); - return Ok(()); - } - }; - - let rc_translator = Rc::new(translator); - - let old_res = Rc::new(res.clone()); - - // map each value to the corresponding value we want to display - let res = res.map( - |id, node| - graph::GraphMapNodesTy::from(graph::GraphMapNodes::Entities, - Rc::clone(&rc_translator)).get()(id, node) - + "; " + - &graph::GraphMapNodesTy::from(graph::GraphMapNodes::Context, - Rc::clone(&rc_translator)).get()(id, node), - graph::GraphMapEdgesTy::from(graph::GraphMapEdges::EntitiesAdded, - Rc::clone(&rc_translator)).get() - ); - - println!("Generated graph in dot notation:\n{}", - rsdot::RSDot::with_attr_getters( - &res, - &[], - &graph::default_edge_formatter(Rc::clone(&old_res)), - &graph::default_node_formatter(Rc::clone(&old_res)), - ) - ); - - Ok(()) -} - -pub fn graphml() -> std::io::Result<()> { - let mut translator = Translator::new(); - - let mut path = env::current_dir()?; - // file to read is inside testing/ - path = path.join("testing/first.system"); - let system = read_file(&mut translator, path.clone(), parser_system)?; - - // the system needs to terminate to return - let res = match graph::digraph(system) { - Ok(o) => o, - Err(e) => { - println!("Error computing target: {e}"); - return Ok(()); - } - }; - - let rc_translator = Rc::new(translator); - - //let old_res = Rc::new(res.clone()); - - // map each value to the corresponding value we want to display - let res = res.map( - |id, node| - graph::GraphMapNodesTy::from(graph::GraphMapNodes::Entities, - Rc::clone(&rc_translator)).get()(id, node) - + "; " + - &graph::GraphMapNodesTy::from(graph::GraphMapNodes::Context, - Rc::clone(&rc_translator)).get()(id, node), - graph::GraphMapEdgesTy::from(graph::GraphMapEdges::EntitiesAdded, - Rc::clone(&rc_translator)).get() - ); - - path.set_extension("graphml"); - - use petgraph_graphml::GraphMl; - let graphml = GraphMl::new(&res) - .pretty_print(true) - .export_node_weights_display() - .export_edge_weights_display(); - println!("Generated graph in graphml notation:\n{graphml}"); - save_file(graphml.to_string(), path)?; - - Ok(()) -} - -// equivalent to main_do(advdigraph, Arcs) -pub fn adversarial() -> std::io::Result<()> { - let mut translator = Translator::new(); - - let mut path = env::current_dir()?; - // file to read is inside testing/ - path = path.join("testing/adversarial.system"); - let system = read_file(&mut translator, path, parser_system)?; - - // the system needs to terminate to return - let res = match graph::digraph(system) { - Ok(o) => o, - Err(e) => { - println!("Error computing target: {e}"); - return Ok(()); - } - }; - - let rc_translator = Rc::new(translator); - - let old_res = Rc::new(res.clone()); - - // map each value to the corresponding value we want to display - let res = res.map( - |id, node| - graph::GraphMapNodesTy::from(graph::GraphMapNodes::Entities, - Rc::clone(&rc_translator)).get()(id, node) - + "; " + - &graph::GraphMapNodesTy::from(graph::GraphMapNodes::Context, - Rc::clone(&rc_translator)).get()(id, node), - graph::GraphMapEdgesTy::from(graph::GraphMapEdges::EntitiesAdded, - Rc::clone(&rc_translator)).get() - ); - - println!("Generated graph in dot notation:\n{}", - rsdot::RSDot::with_attr_getters( - &res, - &[], - &graph::default_edge_formatter(Rc::clone(&old_res)), - &graph::default_node_formatter(Rc::clone(&old_res)), - ) - ); - - Ok(()) -} - -pub fn serialize() -> std::io::Result<()> { - let mut translator = Translator::new(); - - let mut path = env::current_dir()?; - // file to read is inside testing/ - path = path.join("testing/adversarial.system"); - let system = read_file(&mut translator, path, parser_system)?; - - // the system needs to terminate to return - let graph = match graph::digraph(system) { - Ok(o) => o, - Err(e) => { - println!("Error computing target: {e}"); - return Ok(()); - } - }; - - let mut path = env::current_dir()?; - path = path.join("testing/adversarial.cbor"); - - let file = File::create(path)?; - - serialize::sr(file, &graph, &translator).unwrap(); - - Ok(()) -} - -pub fn deserialize() -> std::io::Result<()> { - let mut path = env::current_dir()?; - path = path.join("testing/adversarial.cbor"); - - let file = File::open(path)?; - - let (graph, translator) = serialize::dsr(file).unwrap(); - - let rc_translator = Rc::new(translator); - - let old_res = Rc::new(graph.clone()); - - // map each value to the corresponding value we want to display - let graph = graph.map( - |id, node| - graph::GraphMapNodesTy::from(graph::GraphMapNodes::Entities, - Rc::clone(&rc_translator)).get()(id, node) - + "; " + - &graph::GraphMapNodesTy::from(graph::GraphMapNodes::Context, - Rc::clone(&rc_translator)).get()(id, node), - graph::GraphMapEdgesTy::from(graph::GraphMapEdges::EntitiesAdded, - Rc::clone(&rc_translator)).get() - ); - - println!("Generated graph in dot notation:\n{}", - rsdot::RSDot::with_attr_getters( - &graph, - &[], - &graph::default_edge_formatter(Rc::clone(&old_res)), - &graph::default_node_formatter(Rc::clone(&old_res)), - ) - ); - - Ok(()) -} diff --git a/src/lib.rs b/src/lib.rs index 09c4441..e942550 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,6 @@ //! Module root pub mod rsprocess; -pub mod examples; lalrpop_util::lalrpop_mod!( #[allow(clippy::uninlined_format_args)] pub grammar, // name of module diff --git a/src/main.rs b/src/main.rs index 5537ce7..eea4fdf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,8 @@ -fn main() -> std::io::Result<()> { +fn main() { // let now = std::time::Instant::now(); // println!("{}", now.elapsed().as_micros()); - reactionsystems::examples::graphml()?; + let (g, t) = reactionsystems::rsprocess::presets::digraph("testing/first.system".into()).unwrap(); - Ok(()) + reactionsystems::rsprocess::presets::dot(&g, &t, "testing/first.dot".into()).unwrap(); } diff --git a/src/rsprocess/frequency.rs b/src/rsprocess/frequency.rs index 093c8d7..6e31e78 100644 --- a/src/rsprocess/frequency.rs +++ b/src/rsprocess/frequency.rs @@ -86,8 +86,8 @@ pub fn loop_frequency(system: &RSsystem, symb: IdType) -> Frequency { freq } -/// ```q[i]``` is given enough times such that the stabilizes in a loop, calculate the -/// frequency of the symbols in any state in the last loop +/// ```q[i]``` is given enough times such that the stabilizes in a loop, +/// calculate the frequency of the symbols in any state in the last loop /// see limitFreq pub fn limit_frequency( q: &[RSset], @@ -114,8 +114,8 @@ pub fn limit_frequency( Some(freq) } -/// ```q[i]``` is given enough times such that the stabilizes in a loop, calculate the -/// frequency of the symbols in any state in any loop, weighted. +/// ```q[i]``` is given enough times such that the stabilizes in a loop, +/// calculate the frequency of the symbols in any state in any loop, weighted. /// see fastFreq pub fn fast_frequency( q: &[RSset], diff --git a/src/rsprocess/mod.rs b/src/rsprocess/mod.rs index 9501442..5c1fe8a 100644 --- a/src/rsprocess/mod.rs +++ b/src/rsprocess/mod.rs @@ -12,3 +12,4 @@ pub mod translator; pub mod graph; pub mod rsdot; pub mod serialize; +pub mod presets; diff --git a/src/rsprocess/presets.rs b/src/rsprocess/presets.rs new file mode 100644 index 0000000..252b9b0 --- /dev/null +++ b/src/rsprocess/presets.rs @@ -0,0 +1,488 @@ +//! Module that holds useful presets for interacting with other modules. + +use std::env; +use std::fs; +use std::io; +use std::io::prelude::*; +use std::rc::Rc; + +use lalrpop_util::ParseError; +use petgraph::Graph; + +// grammar is defined in lib.rs, calling lalrpop_mod! twice, generates twice +// the code +use crate::grammar; + +use super::structure::RSlabel; +use super::structure::{RSset, RSsystem}; +use super::translator::Translator; +use super::*; + +// ----------------------------------------------------------------------------- +// Helper Functions +// ----------------------------------------------------------------------------- + +fn read_file( + translator: &mut Translator, + path_string: String, + parser: F +) -> Result +where + F: Fn(&mut Translator, String) -> Result +{ + // relative path + let mut path = match env::current_dir() { + Ok(p) => p, + Err(_) => return Err("Error getting current directory.".into()) + }; + path = path.join(path_string); + + // we read the file with a buffer + let f = match fs::File::open(path) { + Ok(f) => f, + Err(_) => return Err("Error opening file.".into()) + }; + let mut buf_reader = io::BufReader::new(f); + let mut contents = String::new(); + match buf_reader.read_to_string(&mut contents) { + Ok(_) => {}, + Err(_) => return Err("Error reading file.".into()) + } + + // parse + let result = parser(translator, contents)?; + + Ok(result) +} + +fn save_file( + contents: String, + path_string: String, + extension: String +) -> Result<(), String> +{ + // relative path + let mut path = match env::current_dir() { + Ok(p) => p, + Err(_) => return Err("Error getting current directory.".into()) + }; + path = path.join(path_string); + path.set_extension(extension); + + let mut f = match fs::File::create(&path) { + Ok(f) => f, + Err(_) => return Err(format!("Error creating file {}.", + path.to_str().unwrap())) + }; + match write!(f, "{contents}") { + Ok(_) => {} + Err(_) => return Err("Error writing to file.".into()) + } + Ok(()) +} + +fn parser_system( + translator: &mut Translator, + contents: String +) -> Result +{ + match grammar::SystemParser::new() + .parse(translator, &contents) + { + Ok(sys) => Ok(sys), + Err(e) => { + match e { + ParseError::ExtraToken { token: (l, t, r) } => { + Err(format!( + "Unexpected token \"{t}\" \ + between positions {l} and {r}." + )) + }, + ParseError::UnrecognizedEof { location: _, expected: _ } => { + Err("End of file encountered while parsing.".into()) + }, + ParseError::InvalidToken { location } => { + Err(format!("Invalid token at position {location}.")) + }, + ParseError::UnrecognizedToken { token: (l, t, r), expected: _ } + => { + Err(format!( + "Unexpected token \"{t}\" \ + between positions {l} and {r}." + )) + }, + ParseError::User { error } => { + Err(error.to_string()) + } + } + } + } +} + +fn parser_experiment( + translator: &mut Translator, + contents: String +) -> Result<(Vec, Vec), String> +{ + match grammar::ExperimentParser::new() + .parse(translator, &contents) + { + Ok(sys) => Ok(sys), + Err(e) => { + match e { + ParseError::ExtraToken { token: (l, t, r) } => { + Err(format!( + "Unexpected token \"{t}\" \ + between positions {l} and {r}." + )) + }, + ParseError::UnrecognizedEof { location: _, expected: _ } => { + Err("End of file encountered while parsing.".into()) + }, + ParseError::InvalidToken { location } => { + Err(format!("Invalid token at position {location}.")) + }, + ParseError::UnrecognizedToken { token: (l, t, r), expected: _ } + => { + Err(format!( + "Unexpected token \"{t}\" \ + between positions {l} and {r}." + )) + }, + ParseError::User { error } => { + Err(error.to_string()) + } + } + } + } +} + + +// ----------------------------------------------------------------------------- +// Methods +// ----------------------------------------------------------------------------- + +/// Prints statistics of the system. +/// Equivalent main_do(stat) or main_do(stat, MissingE) +pub fn stats(path_string: String) -> Result<(), String> { + let mut translator = Translator::new(); + + let system = read_file(&mut translator, path_string, parser_system)?; + + // print statistics to screan + println!("{}", statistics::of_RSsystem(&translator, &system)); + + Ok(()) +} + + +/// Prints a final set of entities in a terminating Reaction System. +/// Equivalent to main_do(target, E) +pub fn target(path_string: String) -> Result<(), String> { + let mut translator = Translator::new(); + + let system = read_file(&mut translator, path_string, parser_system)?; + + // the system needs to terminate to return + let res = transitions::target(&system)?; + + println!( + "After {} steps we arrive at state:\n{}", + res.0, + translator::RSsetDisplay::from(&translator, &res.1) + ); + + Ok(()) +} + +/// Finds the list of traversed states in a (deterministic) terminating +/// reaction. +/// equivalent to main_do(run,Es) +pub fn run(path_string: String) -> Result<(), String> { + let mut translator = Translator::new(); + + let system = read_file(&mut translator, path_string, parser_system)?; + + // the system needs to terminate to return + let res = transitions::run_separated(&system)?; + + println!("The trace is composed by the set of entities:"); + for (e, _c, _t) in res { + println!("{}", translator::RSsetDisplay::from(&translator, &e)); + } + + Ok(()) +} + + + +/// Finds the looping list of states in a reaction system with a perpetual +/// context IMPORTANT: for loops, we assume Delta defines the process constant +/// x = Q.x and the context process is x . +/// equivalent to main_do(loop,Es) +pub fn hoop(path_string: String) -> Result<(), String> { + let mut translator = Translator::new(); + + let system = read_file(&mut translator, path_string, parser_system)?; + + // we retrieve the id for "x" and use it to find the corresponding loop + let res = + match perpetual::lollipops_only_loop_named(system, + translator.encode("x")) { + Some(o) => o, + None => { + return Err("No loop found.".into()); + } + }; + + println!("The loop is composed by the sets:"); + for e in res { + println!("{}", translator::RSsetDisplay::from(&translator, &e)); + } + + Ok(()) +} + +/// Finds the frequency of each entity in the traversed states for a +/// (deterministic) terminating Reaction System. +/// equivalent to main_do(freq, PairList) +pub fn freq(path_string: String) -> Result<(), String> { + let mut translator = Translator::new(); + + let system = read_file(&mut translator, path_string, parser_system)?; + + let res = frequency::naive_frequency(&system)?; + + println!( + "Frequency of encountered symbols:\n{}", + translator::FrequencyDisplay::from(&translator, &res) + ); + + Ok(()) +} + + +/// Finds the frequency of each entity in the limit loop of a nonterminating +/// Reaction System whose context has the form Q1 ... Q1.Q2 ... Q2 ... Qn ... +/// equivalent to main_do(limitfreq, PairList) +pub fn limit_freq( + path_string_system: String, + path_string_experiment: String +) -> Result<(), String> +{ + let mut translator = Translator::new(); + + let system = read_file(&mut translator, + path_string_system, + parser_system)?; + + let (_, sets) = read_file(&mut translator, + path_string_experiment, + parser_experiment)?; + + let res = match frequency::limit_frequency(&sets, + &system.reaction_rules, + &system.available_entities) { + Some(e) => e, + None => {return Err("Error calculating frequency.".into());} + }; + + println!( + "Frequency of encountered symbols:\n{}", + translator::FrequencyDisplay::from(&translator, &res) + ); + + Ok(()) +} + + +/// Finds the frequency of each entity in the traversed loops of a terminating +/// reaction system whose context has the form +/// Q1 ... Q1.Q2 ... Q2 ... Qn ... Qn.nil and each Qi is repeated Wi times +/// read from a corresponding file. +/// equivalent to main_do(fastfreq, PairList) +pub fn fast_freq( + path_string_system: String, + path_string_experiment: String +) -> Result<(), String> +{ + let mut translator = Translator::new(); + + let system = read_file(&mut translator, + path_string_system, + parser_system)?; + + let (weights, sets) = read_file(&mut translator, + path_string_experiment, + parser_experiment)?; + + let res = match frequency::fast_frequency(&sets, + &system.reaction_rules, + &system.available_entities, + &weights) { + Some(e) => e, + None => {return Err("Error calculating frequency.".into());} + }; + + println!( + "Frequency of encountered symbols:\n{}", + translator::FrequencyDisplay::from(&translator, &res) + ); + + Ok(()) +} + +/// Computes the LTS. +/// equivalent to main_do(digraph, Arcs) or to main_do(advdigraph, Arcs) +pub fn digraph( + path_string: String +) -> Result<(Graph, Translator), String> { + let mut translator = Translator::new(); + + let system = read_file(&mut translator, path_string, parser_system)?; + + // the system needs to terminate to return + let res = graph::digraph(system)?; + Ok((res, translator)) +} + + +// ---------------------------------------------------------------------------- +// Output Functions +// ---------------------------------------------------------------------------- + +/// Writes the specified graph to a file in .dot format. +pub fn dot( + graph: &Graph, + translator: &Translator, + output: String +) -> Result<(), String> +{ + let rc_translator = Rc::new(translator.to_owned()); + + // map each value to the corresponding value we want to display + let modified_graph = graph.map( + |id, node| + graph::GraphMapNodesTy::from( + graph::GraphMapNodes::Entities, + Rc::clone(&rc_translator) + ).get()(id, node) + + "; " + + &graph::GraphMapNodesTy::from( + graph::GraphMapNodes::Context, + Rc::clone(&rc_translator) + ).get()(id, node), + graph::GraphMapEdgesTy::from( + graph::GraphMapEdges::EntitiesAdded, + Rc::clone(&rc_translator) + ).get() + ); + + let graph = Rc::new(graph.to_owned()); + + let edge_formatter = graph::default_edge_formatter(Rc::clone(&graph)); + let node_formatter = graph::default_node_formatter(Rc::clone(&graph)); + + let dot = rsdot::RSDot::with_attr_getters( + &modified_graph, + &[], + &edge_formatter, + &node_formatter, + ); + + save_file(format!("{dot}"), output, "dot".into())?; + + Ok(()) +} + +/// Writes the specified graph to a file in .graphml format. +pub fn graphml( + graph: &Graph, + translator: &Translator, + output: String +) -> Result<(), String> +{ + let rc_translator = Rc::new(translator.to_owned()); + + // map each value to the corresponding value we want to display + let modified_graph = graph.map( + |id, node| + graph::GraphMapNodesTy::from( + graph::GraphMapNodes::Entities, + Rc::clone(&rc_translator) + ).get()(id, node) + + "; " + + &graph::GraphMapNodesTy::from( + graph::GraphMapNodes::Context, + Rc::clone(&rc_translator) + ).get()(id, node), + graph::GraphMapEdgesTy::from(graph::GraphMapEdges::EntitiesAdded, + Rc::clone(&rc_translator)).get() + ); + + use petgraph_graphml::GraphMl; + let graphml = GraphMl::new(&modified_graph) + .pretty_print(true) + .export_node_weights_display() + .export_edge_weights_display(); + + save_file(format!("{graphml}"), output, "graphml".into())?; + + Ok(()) +} + +/// Writes the specified graph, translator tuple to file. +/// N.B. graph size in memory might be much larger after serialization and +/// deserialization. +pub fn serialize( + graph: &Graph, + translator: &Translator, + output_path: String +) -> Result<(), String> +{ + // relative path + let mut path = match env::current_dir() { + Ok(p) => p, + Err(_) => return Err("Error getting current directory.".into()) + }; + path = path.join(output_path); + path.set_extension("cbor"); + + let f = match fs::File::create(&path) { + Ok(f) => f, + Err(_) => return Err(format!("Error creating file {}.", + path.to_str().unwrap())) + }; + + match serialize::ser(f, graph, translator) { + Ok(_) => Ok(()), + Err(_) => Err("Error during serialization.".into()) + } +} + +/// Reads the specified serialized system from a file. +/// N.B. graph size in memory might be much larger after serialization and +/// deserialization +pub fn deserialize( + input_path: String +) -> Result<(Graph, Translator), String> +{ + // relative path + let mut path = match env::current_dir() { + Ok(p) => p, + Err(_) => return Err("Error getting current directory.".into()) + }; + path = path.join(input_path); + path.set_extension("cbor"); + + let f = match fs::File::open(&path) { + Ok(f) => f, + Err(_) => return Err(format!("Error opening file {}.", + path.to_str().unwrap())) + }; + + match serialize::de(f) { + Ok(a) => Ok(a), + Err(_) => Err("Error during deserialization.".into()) + } +} diff --git a/src/rsprocess/rsdot.rs b/src/rsprocess/rsdot.rs index f03b166..d7ecd82 100644 --- a/src/rsprocess/rsdot.rs +++ b/src/rsprocess/rsdot.rs @@ -1,6 +1,8 @@ //! Slightly modified Simple graphviz dot file format output. //! See petgraph::dot::mod. +static PRINTNAMES: bool = false; + use core::fmt::{self, Display, Write}; use petgraph:: { @@ -205,10 +207,15 @@ where // output all labels for node in g.node_references() { - write!(f, "{INDENT}\"")?; - // Escaped(FnFmt(node.weight(), &node_fmt)).fmt(f)?; - write!(f, "{}", g.to_index(node.id()))?; - write!(f, "\" [ ")?; + if PRINTNAMES { + write!(f, "{INDENT}\"")?; + Escaped(FnFmt(node.weight(), &node_fmt)).fmt(f)?; + write!(f, "\" [ ")?; + } else { + write!(f, "{INDENT}")?; + write!(f, "{}", g.to_index(node.id()))?; + write!(f, " [ ")?; + } write!(f, "label = \"")?; Escaped(FnFmt(node.weight(), &node_fmt)).fmt(f)?; @@ -218,15 +225,21 @@ where } // output all edges for edge in g.edge_references() { - write!(f, "{INDENT}\"")?; - // let node_source_weight = g.node_weight(edge.source()).unwrap(); - // Escaped(FnFmt(node_source_weight, &node_fmt)).fmt(f)?; - write!(f, "{}", g.to_index(edge.source()))?; - write!(f, "\" {} \"", EDGE[g.is_directed() as usize])?; - // let node_target_weight = g.node_weight(edge.target()).unwrap(); - // Escaped(FnFmt(node_target_weight, &node_fmt)).fmt(f)?; - write!(f, "{}", g.to_index(edge.target()))?; - write!(f, "\" [ ")?; + if PRINTNAMES { + write!(f, "{INDENT}\"")?; + let node_source_weight = g.node_weight(edge.source()).unwrap(); + Escaped(FnFmt(node_source_weight, &node_fmt)).fmt(f)?; + write!(f, "\" {} \"", EDGE[g.is_directed() as usize])?; + let node_target_weight = g.node_weight(edge.target()).unwrap(); + Escaped(FnFmt(node_target_weight, &node_fmt)).fmt(f)?; + write!(f, "\" [ ")?; + } else { + write!(f, "{INDENT}")?; + write!(f, "{} ", g.to_index(edge.source()))?; + write!(f, "{} ", EDGE[g.is_directed() as usize])?; + write!(f, "{} ", g.to_index(edge.target()))?; + write!(f, "[ ")?; + } write!(f, "label = \"")?; Escaped(FnFmt(edge.weight(), &edge_fmt)).fmt(f)?; diff --git a/src/rsprocess/serialize.rs b/src/rsprocess/serialize.rs index d584eb7..2b99ff7 100644 --- a/src/rsprocess/serialize.rs +++ b/src/rsprocess/serialize.rs @@ -18,7 +18,7 @@ struct GraphAndTranslator { } /// Serializer for graph and translator. -pub fn sr( +pub fn ser( writer: W, graph: &Graph, translator: &Translator @@ -34,7 +34,7 @@ where } /// Deserializer for file that contains graph and translator. -pub fn dsr( +pub fn de( reader: R ) -> Result<(Graph, Translator), serde_cbor_2::Error> where diff --git a/src/rsprocess/support_structures.rs b/src/rsprocess/support_structures.rs index d1c56dd..731557e 100644 --- a/src/rsprocess/support_structures.rs +++ b/src/rsprocess/support_structures.rs @@ -4,7 +4,6 @@ use super::structure::{RSlabel, RSprocess, RSset, RSsystem}; use super::transitions::unfold; use std::rc::Rc; -/// #[derive(Clone, Debug)] pub struct TransitionsIterator<'a> { choices_iterator: std::vec::IntoIter<(Rc, Rc)>,