Refactoring
This commit is contained in:
488
src/rsprocess/presets.rs
Normal file
488
src/rsprocess/presets.rs
Normal file
@ -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<T, F>(
|
||||
translator: &mut Translator,
|
||||
path_string: String,
|
||||
parser: F
|
||||
) -> Result<T, String>
|
||||
where
|
||||
F: Fn(&mut Translator, String) -> Result<T, 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);
|
||||
|
||||
// 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<RSsystem, String>
|
||||
{
|
||||
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<u32>, Vec<RSset>), 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<RSsystem, RSlabel>, 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<RSsystem, RSlabel>,
|
||||
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<RSsystem, RSlabel>,
|
||||
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<RSsystem, RSlabel>,
|
||||
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<RSsystem, RSlabel>, 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())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user