Files
ReactionSystems/execution/src/presets.rs

845 lines
26 KiB
Rust
Raw Normal View History

2025-07-11 19:36:20 +02:00
//! Module that holds useful presets for interacting with other modules.
2025-09-10 22:41:40 +02:00
use std::collections::HashMap;
use std::io::prelude::*;
2025-07-11 19:36:20 +02:00
use std::rc::Rc;
2025-09-11 02:49:14 +02:00
use std::{env, fs, io};
2025-07-11 19:36:20 +02:00
2025-09-11 02:49:14 +02:00
use petgraph::Graph;
use crate::data::MapEdges;
use rsprocess::set::Set;
use rsprocess::system::ExtensionsSystem;
use rsprocess::translator::Translator;
use rsprocess::*;
// -----------------------------------------------------------------------------
pub trait FileParsers {
fn parse_experiment(
translator: &mut Translator,
contents: String,
) -> Result<(Vec<u32>, Vec<Set>), String>;
fn parse_instructions(
translator: &mut Translator,
contents: String,
) -> Result<Instructions, String>;
}
// -----------------------------------------------------------------------------
// Structures
// -----------------------------------------------------------------------------
/// Describes how the result of some computation has to be saved.
pub struct SaveOptions {
pub print: bool,
2025-09-11 02:49:14 +02:00
pub save: Option<Vec<String>>,
}
impl SaveOptions {
pub fn combine(&mut self, other: &mut Self) {
2025-07-12 15:41:10 +02:00
self.print = self.print || other.print;
match (self.save.is_some(), other.save.is_some()) {
2025-09-11 02:49:14 +02:00
| (false, false) | (true, false) => {},
| (false, true) => {
2025-07-12 15:41:10 +02:00
self.save = other.save.to_owned();
2025-09-11 02:49:14 +02:00
},
| (true, true) => {
2025-07-12 15:41:10 +02:00
self.save
.as_mut()
.unwrap()
.append(other.save.as_mut().unwrap());
2025-09-11 02:49:14 +02:00
},
2025-07-12 15:41:10 +02:00
}
}
pub fn new() -> Self {
2025-07-12 15:41:10 +02:00
SaveOptions {
print: false,
2025-09-11 02:49:14 +02:00
save: None,
2025-07-12 15:41:10 +02:00
}
}
}
impl Default for SaveOptions {
fn default() -> Self {
2025-07-12 15:41:10 +02:00
SaveOptions::new()
}
}
// Describes output options for a graph.
pub enum GraphSaveOptions {
2025-07-12 15:41:10 +02:00
Dot {
node_display: graph::NodeDisplay,
edge_display: graph::EdgeDisplay,
node_color: graph::NodeColor,
edge_color: graph::EdgeColor,
2025-07-12 15:41:10 +02:00
so: SaveOptions,
},
GraphML {
node_display: graph::NodeDisplay,
edge_display: graph::EdgeDisplay,
2025-07-12 15:41:10 +02:00
so: SaveOptions,
},
Serialize {
path: String,
},
}
/// Describes the computation to apply to the input system or graph.
pub enum Instruction {
Stats {
so: SaveOptions,
},
Target {
so: SaveOptions,
},
Run {
so: SaveOptions,
},
Loop {
symbol: String,
2025-09-11 02:49:14 +02:00
so: SaveOptions,
},
Frequency {
so: SaveOptions,
},
LimitFrequency {
experiment: String,
so: SaveOptions,
},
FastFrequency {
experiment: String,
so: SaveOptions,
},
Digraph {
2025-09-10 22:41:40 +02:00
group: Option<Box<assert::grouping::Assert>>,
2025-09-11 02:49:14 +02:00
gso: Vec<GraphSaveOptions>,
},
Bisimilarity {
system_b: String,
2025-09-10 22:41:40 +02:00
edge_relabeler: Box<assert::relabel::Assert>,
so: SaveOptions,
},
}
/// Describes a system or a graph.
2025-09-10 22:41:40 +02:00
#[derive(Clone)]
pub enum System {
Deserialize { path: String },
System { sys: system::System },
}
2025-07-12 02:42:28 +02:00
impl System {
/// Deserialize the graph if applicable.
2025-09-10 22:41:40 +02:00
pub fn compute(
&self,
translator: Translator,
) -> Result<EvaluatedSystem, String> {
2025-07-12 15:41:10 +02:00
match self {
2025-09-11 02:49:14 +02:00
| Self::System { sys } => Ok(EvaluatedSystem::System {
2025-07-12 15:41:10 +02:00
sys: sys.to_owned(),
translator,
}),
2025-09-11 02:49:14 +02:00
| Self::Deserialize { path } => {
2025-07-12 15:41:10 +02:00
let (graph, translator) = deserialize(path.into())?;
Ok(EvaluatedSystem::Graph { graph, translator })
2025-09-11 02:49:14 +02:00
},
2025-07-12 15:41:10 +02:00
}
2025-07-12 02:42:28 +02:00
}
}
2025-09-10 22:41:40 +02:00
#[derive(Clone)]
2025-07-16 00:05:14 +02:00
pub enum EvaluatedSystem {
Graph {
2025-09-11 02:49:14 +02:00
graph: graph::SystemGraph,
2025-07-16 00:05:14 +02:00
translator: Translator,
},
System {
sys: system::System,
2025-07-16 00:05:14 +02:00
translator: Translator,
},
}
impl EvaluatedSystem {
pub fn get_translator(&mut self) -> &mut Translator {
match self {
2025-09-11 02:49:14 +02:00
| EvaluatedSystem::Graph {
graph: _,
translator,
} => translator,
2025-09-11 02:49:14 +02:00
| EvaluatedSystem::System { sys: _, translator } => translator,
}
2025-07-16 00:05:14 +02:00
}
}
/// Holds all the computations to be done on the system
pub struct Instructions {
pub system: System,
2025-07-12 15:41:10 +02:00
pub instructions: Vec<Instruction>,
}
2025-07-11 19:36:20 +02:00
// -----------------------------------------------------------------------------
// IO Helper Functions
2025-07-11 19:36:20 +02:00
// -----------------------------------------------------------------------------
2025-09-10 22:41:40 +02:00
fn read_file<T, F>(
translator: &mut Translator,
path_string: String,
parser: F,
) -> Result<T, String>
2025-07-11 19:36:20 +02:00
where
2025-07-12 15:41:10 +02:00
F: Fn(&mut Translator, String) -> Result<T, String>,
2025-07-11 19:36:20 +02:00
{
// relative path
let mut path = match env::current_dir() {
2025-09-11 02:49:14 +02:00
| Ok(p) => p,
| Err(e) => {
return Err(format!("Error getting current directory: {e}"));
},
2025-07-11 19:36:20 +02:00
};
path = path.join(path_string);
// we read the file with a buffer
let f = match fs::File::open(path) {
2025-09-11 02:49:14 +02:00
| Ok(f) => f,
| Err(e) => return Err(format!("Error opening file: {e}.")),
2025-07-11 19:36:20 +02:00
};
let mut buf_reader = io::BufReader::new(f);
let mut contents = String::new();
match buf_reader.read_to_string(&mut contents) {
2025-09-11 02:49:14 +02:00
| Ok(_) => {},
| Err(e) => return Err(format!("Error reading file: {e}")),
2025-07-11 19:36:20 +02:00
}
// parse
let result = parser(translator, contents)?;
Ok(result)
}
2025-07-12 15:41:10 +02:00
fn save_file(contents: &String, path_string: String) -> Result<(), String> {
// relative path
let mut path = match env::current_dir() {
2025-09-11 02:49:14 +02:00
| Ok(p) => p,
| Err(_) => return Err("Error getting current directory.".into()),
};
path = path.join(path_string);
let mut f = match fs::File::create(&path) {
2025-09-11 02:49:14 +02:00
| Ok(f) => f,
| Err(_) => {
2025-09-10 22:41:40 +02:00
return Err(format!(
"Error creating file {}.",
path.to_str().unwrap()
));
2025-09-11 02:49:14 +02:00
},
};
match write!(f, "{contents}") {
2025-09-11 02:49:14 +02:00
| Ok(_) => {},
| Err(_) => return Err("Error writing to file.".into()),
}
Ok(())
}
2025-07-11 19:36:20 +02:00
// -----------------------------------------------------------------------------
// main_do
2025-07-11 19:36:20 +02:00
// -----------------------------------------------------------------------------
/// Prints statistics of the system.
/// Equivalent main_do(stat) or main_do(stat, MissingE)
2025-07-12 02:42:28 +02:00
pub fn stats(system: &EvaluatedSystem) -> Result<String, String> {
match system {
2025-09-11 02:49:14 +02:00
| EvaluatedSystem::System { sys, translator } =>
Ok(sys.statistics(translator)),
| EvaluatedSystem::Graph { graph, translator } => {
2025-07-12 15:41:10 +02:00
let Some(sys) = graph.node_weights().next() else {
2025-09-02 01:34:01 +02:00
return Err("No node found in graph.".into());
2025-07-12 15:41:10 +02:00
};
Ok(sys.statistics(translator))
2025-09-11 02:49:14 +02:00
},
2025-07-12 02:42:28 +02:00
}
2025-07-11 19:36:20 +02:00
}
/// Prints a final set of entities in a terminating Reaction System.
2025-07-12 02:42:28 +02:00
/// The system needs to terminate to return.
2025-07-11 19:36:20 +02:00
/// Equivalent to main_do(target, E)
2025-07-12 02:42:28 +02:00
pub fn target(system: &EvaluatedSystem) -> Result<String, String> {
let (res, translator) = match system {
2025-09-11 02:49:14 +02:00
| EvaluatedSystem::System { sys, translator } =>
(sys.target()?, translator),
| EvaluatedSystem::Graph { graph, translator } => {
2025-07-12 15:41:10 +02:00
let Some(sys) = graph.node_weights().next() else {
2025-09-02 01:34:01 +02:00
return Err("No node found in graph.".into());
2025-07-12 15:41:10 +02:00
};
(sys.target()?, translator)
2025-09-11 02:49:14 +02:00
},
2025-07-12 02:42:28 +02:00
};
Ok(format!(
2025-07-12 15:41:10 +02:00
"After {} steps we arrive at state:\n{}",
res.0,
translator::Formatter::from(translator, &res.1)
2025-07-12 02:42:28 +02:00
))
2025-07-11 19:36:20 +02:00
}
/// Finds the list of traversed states in a (deterministic) terminating
/// reaction.
2025-07-12 02:42:28 +02:00
/// The system needs to terminate to return.
2025-07-11 19:36:20 +02:00
/// equivalent to main_do(run,Es)
2025-07-12 02:42:28 +02:00
pub fn traversed(system: &EvaluatedSystem) -> Result<String, String> {
let (res, translator) = match system {
2025-09-11 02:49:14 +02:00
| EvaluatedSystem::System { sys, translator } =>
(sys.run_separated()?, translator),
| EvaluatedSystem::Graph { graph, translator } => {
2025-07-12 15:41:10 +02:00
let Some(sys) = graph.node_weights().next() else {
2025-09-02 01:34:01 +02:00
return Err("No node found in graph.".into());
2025-07-12 15:41:10 +02:00
};
(sys.run_separated()?, translator)
2025-09-11 02:49:14 +02:00
},
2025-07-12 02:42:28 +02:00
};
2025-07-11 19:36:20 +02:00
2025-07-12 02:42:28 +02:00
let mut output = String::new();
2025-07-11 19:36:20 +02:00
2025-09-02 01:34:01 +02:00
output.push_str("The trace is composed by the set of entities: ");
2025-07-11 19:36:20 +02:00
for (e, _c, _t) in res {
2025-09-10 22:41:40 +02:00
output.push_str(&format!(
"{}",
translator::Formatter::from(translator, &e)
));
2025-07-11 19:36:20 +02:00
}
2025-07-12 02:42:28 +02:00
Ok(output)
2025-07-11 19:36:20 +02:00
}
/// Finds the looping list of states in a reaction system with a perpetual
/// context. IMPORTANT: for loops, we assume Delta defines the process constant
2025-07-11 19:36:20 +02:00
/// x = Q.x and the context process is x .
/// equivalent to main_do(loop,Es)
2025-09-10 22:41:40 +02:00
pub fn hoop(
system: &EvaluatedSystem,
symbol: String,
) -> Result<String, String> {
use system::LoopSystem;
2025-07-12 02:42:28 +02:00
let (res, translator) = match system {
2025-09-11 02:49:14 +02:00
| EvaluatedSystem::System { sys, translator } => (sys, translator),
| EvaluatedSystem::Graph { graph, translator } => {
2025-07-12 15:41:10 +02:00
let Some(sys) = graph.node_weights().next() else {
2025-09-02 01:34:01 +02:00
return Err("No node found in graph.".into());
2025-07-12 15:41:10 +02:00
};
(sys, translator)
2025-09-11 02:49:14 +02:00
},
2025-07-11 19:36:20 +02:00
};
2025-07-12 02:42:28 +02:00
// we retrieve the id for "x" and use it to find the corresponding loop
2025-07-12 15:41:10 +02:00
let Some(id) = translator.encode_not_mut(&symbol) else {
2025-09-02 01:34:01 +02:00
return Err(format!("Symbol {symbol} not found."));
2025-07-12 15:41:10 +02:00
};
let res = match res.lollipops_only_loop_named(id) {
2025-09-11 02:49:14 +02:00
| Some(o) => o,
| None => {
2025-07-12 15:41:10 +02:00
return Err("No loop found.".into());
2025-09-11 02:49:14 +02:00
},
2025-07-12 02:42:28 +02:00
};
let mut output = String::new();
2025-07-11 19:36:20 +02:00
2025-09-02 01:34:01 +02:00
output.push_str("The loop is composed by the sets: ");
2025-07-11 19:36:20 +02:00
for e in res {
2025-09-10 22:41:40 +02:00
output.push_str(&format!(
"{}",
translator::Formatter::from(translator, &e)
));
2025-07-11 19:36:20 +02:00
}
2025-07-12 02:42:28 +02:00
Ok(output)
2025-07-11 19:36:20 +02:00
}
/// Finds the frequency of each entity in the traversed states for a
/// (deterministic) terminating Reaction System.
/// equivalent to main_do(freq, PairList)
2025-07-12 15:41:10 +02:00
pub fn freq(system: &EvaluatedSystem) -> Result<String, String> {
use frequency::BasicFrequency;
2025-07-12 02:42:28 +02:00
let (sys, translator) = match system {
2025-09-11 02:49:14 +02:00
| EvaluatedSystem::System { sys, translator } => (sys, translator),
| EvaluatedSystem::Graph { graph, translator } => {
2025-07-12 15:41:10 +02:00
let Some(sys) = graph.node_weights().next() else {
2025-09-02 01:34:01 +02:00
return Err("No node found in graph.".into());
2025-07-12 15:41:10 +02:00
};
(sys, translator)
2025-09-11 02:49:14 +02:00
},
2025-07-12 02:42:28 +02:00
};
2025-07-11 19:36:20 +02:00
let res = frequency::Frequency::naive_frequency(sys)?;
2025-07-11 19:36:20 +02:00
2025-07-12 02:42:28 +02:00
Ok(format!(
2025-07-12 15:41:10 +02:00
"Frequency of encountered symbols:\n{}",
translator::Formatter::from(translator, &res)
2025-07-12 02:42:28 +02:00
))
2025-07-11 19:36:20 +02:00
}
/// 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<F>(
2025-09-10 22:41:40 +02:00
system: &mut EvaluatedSystem,
experiment: String,
parser_experiment: F,
) -> Result<String, String>
where
F: Fn(&mut Translator, String) -> Result<(Vec<u32>, Vec<Set>), String>
{
use frequency::BasicFrequency;
let (sys, translator): (&system::System, &mut Translator) = match system {
2025-09-11 02:49:14 +02:00
| EvaluatedSystem::System { sys, translator } => (sys, translator),
| EvaluatedSystem::Graph { graph, translator } => {
2025-07-12 15:41:10 +02:00
let Some(sys) = graph.node_weights().next() else {
2025-09-02 01:34:01 +02:00
return Err("No node found in graph.".into());
2025-07-12 15:41:10 +02:00
};
(sys, translator)
2025-09-11 02:49:14 +02:00
},
2025-07-12 02:42:28 +02:00
};
2025-07-11 19:36:20 +02:00
2025-07-12 15:41:10 +02:00
let (_, sets) = read_file(translator, experiment, parser_experiment)?;
2025-07-11 19:36:20 +02:00
let res = match frequency::Frequency::limit_frequency(
&sets,
&sys.reaction_rules,
&sys.available_entities,
) {
2025-09-11 02:49:14 +02:00
| Some(e) => e,
| None => {
2025-07-12 15:41:10 +02:00
return Err("Error calculating frequency.".into());
2025-09-11 02:49:14 +02:00
},
2025-07-11 19:36:20 +02:00
};
2025-07-12 02:42:28 +02:00
Ok(format!(
2025-07-12 15:41:10 +02:00
"Frequency of encountered symbols:\n{}",
translator::Formatter::from(translator, &res)
2025-07-12 02:42:28 +02:00
))
2025-07-11 19:36:20 +02:00
}
/// 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<F>(
2025-09-10 22:41:40 +02:00
system: &mut EvaluatedSystem,
experiment: String,
parser_experiment: F,
) -> Result<String, String>
where
F: Fn(&mut Translator, String) -> Result<(Vec<u32>, Vec<Set>), String>
{
use frequency::BasicFrequency;
let (sys, translator): (&system::System, &mut Translator) = match system {
2025-09-11 02:49:14 +02:00
| EvaluatedSystem::System { sys, translator } => (sys, translator),
| EvaluatedSystem::Graph { graph, translator } => {
2025-07-12 15:41:10 +02:00
let Some(sys) = graph.node_weights().next() else {
return Err("No node found in graph".into());
};
(sys, translator)
2025-09-11 02:49:14 +02:00
},
2025-07-12 02:42:28 +02:00
};
2025-07-11 19:36:20 +02:00
2025-07-12 15:41:10 +02:00
let (weights, sets) = read_file(translator, experiment, parser_experiment)?;
let res = match frequency::Frequency::fast_frequency(
&sets,
&sys.reaction_rules,
&sys.available_entities,
&weights,
) {
2025-09-11 02:49:14 +02:00
| Some(e) => e,
| None => {
return Err("Error calculating frequency.".into());
2025-09-11 02:49:14 +02:00
},
};
2025-07-11 19:36:20 +02:00
2025-07-12 02:42:28 +02:00
Ok(format!(
2025-07-12 15:41:10 +02:00
"Frequency of encountered symbols:\n{}",
translator::Formatter::from(translator, &res)
2025-07-12 02:42:28 +02:00
))
2025-07-11 19:36:20 +02:00
}
/// Computes the LTS.
/// equivalent to main_do(digraph, Arcs) or to main_do(advdigraph, Arcs)
2025-07-12 15:41:10 +02:00
pub fn digraph(system: &mut EvaluatedSystem) -> Result<(), String> {
2025-07-16 00:05:14 +02:00
if let EvaluatedSystem::System { sys, translator } = system {
*system = EvaluatedSystem::Graph {
2025-09-11 02:49:14 +02:00
graph: sys.digraph()?,
translator: translator.to_owned(),
};
2025-07-16 00:05:14 +02:00
}
2025-07-12 02:42:28 +02:00
Ok(())
2025-07-11 19:36:20 +02:00
}
2025-09-10 22:41:40 +02:00
pub fn grouping(
system: &mut EvaluatedSystem,
group: &assert::grouping::Assert,
) -> Result<(), String> {
let mut buckets = HashMap::new();
let mut leader: HashMap<petgraph::prelude::NodeIndex, _> = HashMap::new();
if let EvaluatedSystem::Graph { graph, translator } = system {
for node in graph.node_indices() {
let val = group.execute(graph, &node, translator)?;
2025-09-11 02:17:40 +02:00
println!("node: {node:?} -> val: {val:?}");
2025-09-10 22:41:40 +02:00
buckets.entry(val.clone()).or_insert(vec![]).push(node);
let l = buckets.get(&val).unwrap().first().unwrap();
leader.insert(node, (*l, val));
}
for node in graph.node_indices().rev() {
let (origin, val) = leader.get(&node).unwrap();
if *origin == node {
continue;
}
if buckets.get(val).unwrap().len() <= 1 {
continue;
}
let mut edges =
graph.neighbors_directed(node, petgraph::Outgoing).detach();
while let Some(edge) = edges.next_edge(graph) {
graph.add_edge(
*origin,
graph.edge_endpoints(edge).unwrap().1,
graph.edge_weight(edge).unwrap().clone(),
);
}
let mut edges =
graph.neighbors_directed(node, petgraph::Incoming).detach();
while let Some(edge) = edges.next_edge(graph) {
graph.add_edge(
graph.edge_endpoints(edge).unwrap().0,
*origin,
graph.edge_weight(edge).unwrap().clone(),
);
}
graph
.remove_node(node)
.ok_or(format!("Could not remove node {node:?}"))?;
}
Ok(())
} else {
Err("Grouping can be done only on graphs.".into())
}
}
/// Computes bisimularity of two provided systems
pub fn bisimilar<F>(
2025-07-16 00:05:14 +02:00
system_a: &mut EvaluatedSystem,
2025-09-10 22:41:40 +02:00
edge_relabeler: &assert::relabel::Assert,
system_b: String,
parser_instructions: F
) -> Result<String, String>
where
F: Fn(&mut Translator, String) -> Result<Instructions, String>
{
2025-09-10 22:41:40 +02:00
use assert::relabel::AssertReturnValue;
2025-08-15 00:32:58 +02:00
let system_b = read_file(
system_a.get_translator(),
system_b.to_string(),
parser_instructions,
)?;
2025-09-10 22:41:40 +02:00
let mut system_b = match system_b
.system
.compute(system_a.get_translator().clone())?
{
2025-09-11 02:49:14 +02:00
| EvaluatedSystem::System { sys, translator } =>
EvaluatedSystem::System { sys, translator },
| EvaluatedSystem::Graph { graph, translator } => {
if translator != *system_a.get_translator() {
return Err("Bisimilarity not implemented for systems with \
2025-09-10 22:41:40 +02:00
different encodings. Serialize the systems \
with the same translator."
.into());
}
EvaluatedSystem::Graph { graph, translator }
2025-09-11 02:49:14 +02:00
},
};
2025-07-16 00:05:14 +02:00
digraph(system_a)?;
2025-07-16 00:05:14 +02:00
digraph(&mut system_b)?;
// since we ran digraph on both they have to be graphs
2025-08-15 00:32:58 +02:00
match (system_a, &mut system_b) {
2025-09-11 02:49:14 +02:00
| (
EvaluatedSystem::Graph {
graph: a,
translator: _,
},
EvaluatedSystem::Graph {
graph: b,
translator: translator_b,
},
) => {
let a: Graph<system::System, AssertReturnValue> =
a.map_edges(edge_relabeler, translator_b)?;
let b: Graph<system::System, AssertReturnValue> =
b.map_edges(edge_relabeler, translator_b)?;
Ok(format!(
"{}",
2025-09-11 02:49:14 +02:00
// bisimilarity::bisimilarity_kanellakis_smolka::bisimilarity(&
// &a, &&b)
2025-09-10 22:41:40 +02:00
// bisimilarity::bisimilarity_paige_tarjan::bisimilarity_ignore_labels(&&a, &&b)
bisimilarity::bisimilarity_paige_tarkan::bisimilarity(&&a, &&b)
))
2025-09-11 02:49:14 +02:00
},
| _ => {
unreachable!()
2025-09-11 02:49:14 +02:00
},
2025-07-16 00:05:14 +02:00
}
}
// -----------------------------------------------------------------------------
2025-07-11 19:36:20 +02:00
// Output Functions
// -----------------------------------------------------------------------------
2025-07-11 19:36:20 +02:00
/// Writes the specified graph to a file in .dot format.
pub fn dot(
system: &EvaluatedSystem,
node_display: graph::NodeDisplay,
edge_display: graph::EdgeDisplay,
2025-07-26 21:22:30 +02:00
node_color: graph::NodeColor,
edge_color: graph::EdgeColor,
2025-07-12 15:41:10 +02:00
) -> Result<String, String> {
2025-07-12 02:42:28 +02:00
match system {
2025-09-11 02:49:14 +02:00
| EvaluatedSystem::System {
2025-07-12 15:41:10 +02:00
sys: _,
translator: _,
} => Err("Supplied system is not a graph".into()),
2025-09-11 02:49:14 +02:00
| EvaluatedSystem::Graph { graph, translator } => {
2025-07-12 15:41:10 +02:00
let rc_translator = Rc::new(translator.clone());
let modified_graph = graph.map(
node_display.generate(Rc::clone(&rc_translator), graph),
edge_display.generate(Rc::clone(&rc_translator), graph),
2025-07-12 15:41:10 +02:00
);
let graph = Rc::new(graph.to_owned());
2025-09-10 22:41:40 +02:00
let node_formatter = node_color
.generate(Rc::clone(&graph), translator.encode_not_mut("*"));
let edge_formatter = edge_color.generate(Rc::clone(&graph));
2025-09-10 22:41:40 +02:00
let dot = dot::Dot::with_attr_getters(
&modified_graph,
&[],
&edge_formatter,
&node_formatter,
);
2025-07-12 15:41:10 +02:00
Ok(format!("{dot}"))
2025-09-11 02:49:14 +02:00
},
2025-07-12 02:42:28 +02:00
}
2025-07-11 19:36:20 +02:00
}
/// Writes the specified graph to a file in .graphml format.
2025-07-13 17:28:13 +02:00
pub fn graphml(
system: &EvaluatedSystem,
node_display: graph::NodeDisplay,
edge_display: graph::EdgeDisplay,
2025-07-13 17:28:13 +02:00
) -> Result<String, String> {
2025-07-12 02:42:28 +02:00
match system {
2025-09-11 02:49:14 +02:00
| EvaluatedSystem::System {
2025-07-12 15:41:10 +02:00
sys: _,
translator: _,
} => Err("Supplied system is not a graph".into()),
2025-09-11 02:49:14 +02:00
| EvaluatedSystem::Graph { graph, translator } => {
2025-07-12 15:41:10 +02:00
let rc_translator = Rc::new(translator.to_owned());
// map each value to the corresponding value we want to display
let modified_graph = graph.map(
node_display.generate(Rc::clone(&rc_translator), graph),
edge_display.generate(rc_translator, graph),
2025-07-12 15:41:10 +02:00
);
use petgraph_graphml::GraphMl;
let graphml = GraphMl::new(&modified_graph)
.pretty_print(true)
.export_node_weights_display()
.export_edge_weights_display();
Ok(format!("{graphml}"))
2025-09-11 02:49:14 +02:00
},
2025-07-12 02:42:28 +02:00
}
2025-07-11 19:36:20 +02:00
}
/// Writes the specified graph, translator tuple to file.
/// N.B. graph size in memory might be much larger after serialization and
/// deserialization.
2025-07-12 15:41:10 +02:00
pub fn serialize(system: &EvaluatedSystem, path: String) -> Result<(), String> {
2025-07-12 02:42:28 +02:00
match system {
2025-09-11 02:49:14 +02:00
| EvaluatedSystem::System {
2025-07-12 15:41:10 +02:00
sys: _,
translator: _,
} => Err("Supplied system is not a graph".into()),
2025-09-11 02:49:14 +02:00
| EvaluatedSystem::Graph { graph, translator } => {
2025-07-12 15:41:10 +02:00
// relative path
let mut path = std::path::PathBuf::from(path);
path.set_extension("cbor");
let f = match fs::File::create(&path) {
2025-09-11 02:49:14 +02:00
| Ok(f) => f,
| Err(_) => {
2025-09-10 22:41:40 +02:00
return Err(format!(
"Error creating file {}.",
path.to_str().unwrap()
));
2025-09-11 02:49:14 +02:00
},
2025-07-12 15:41:10 +02:00
};
match serialize::ser(f, graph, translator) {
2025-09-11 02:49:14 +02:00
| Ok(_) => Ok(()),
| Err(_) => Err("Error during serialization.".into()),
2025-07-12 15:41:10 +02:00
}
2025-09-11 02:49:14 +02:00
},
2025-07-11 19:36:20 +02:00
}
}
/// Reads the specified serialized system from a file.
/// N.B. graph size in memory might be much larger after serialization and
/// deserialization
2025-09-10 22:41:40 +02:00
pub fn deserialize(
input_path: String,
) -> Result<(graph::SystemGraph, Translator), String> {
2025-07-11 19:36:20 +02:00
// relative path
let mut path = match env::current_dir() {
2025-09-11 02:49:14 +02:00
| Ok(p) => p,
| Err(_) => return Err("Error getting current directory.".into()),
2025-07-11 19:36:20 +02:00
};
path = path.join(input_path);
path.set_extension("cbor");
let f = match fs::File::open(&path) {
2025-09-11 02:49:14 +02:00
| Ok(f) => f,
| Err(_) => {
2025-09-10 22:41:40 +02:00
return Err(format!(
"Error opening file {}.",
path.to_str().unwrap()
));
2025-09-11 02:49:14 +02:00
},
2025-07-11 19:36:20 +02:00
};
match serialize::de(f) {
2025-09-11 02:49:14 +02:00
| Ok(a) => Ok(a),
| Err(_) => Err("Error during deserialization.".into()),
2025-07-11 19:36:20 +02:00
}
}
//------------------------------------------------------------------------------
// Interpreting Instructions
//------------------------------------------------------------------------------
2025-07-12 02:42:28 +02:00
macro_rules! save_options {
($assignment: expr, $so: ident) => {
2025-07-12 15:41:10 +02:00
let SaveOptions { print, save } = $so;
let output = $assignment;
if print {
println!("{output}");
}
if let Some(save) = save {
for file in save {
save_file(&output, file)?;
}
}
2025-07-12 02:42:28 +02:00
};
}
fn execute<P: FileParsers>(
2025-09-10 22:41:40 +02:00
instruction: Instruction,
system: &mut EvaluatedSystem,
) -> Result<(), String> {
2025-07-12 02:42:28 +02:00
match instruction {
2025-09-11 02:49:14 +02:00
| Instruction::Stats { so } => {
2025-07-12 15:41:10 +02:00
save_options!(stats(system)?, so);
2025-09-11 02:49:14 +02:00
},
| Instruction::Target { so } => {
2025-07-12 15:41:10 +02:00
save_options!(target(system)?, so);
2025-09-11 02:49:14 +02:00
},
| Instruction::Run { so } => {
2025-07-12 15:41:10 +02:00
save_options!(traversed(system)?, so);
2025-09-11 02:49:14 +02:00
},
| Instruction::Loop { symbol, so } => {
2025-07-12 15:41:10 +02:00
save_options!(hoop(system, symbol)?, so);
2025-09-11 02:49:14 +02:00
},
| Instruction::Frequency { so } => {
2025-07-12 15:41:10 +02:00
save_options!(freq(system)?, so);
2025-09-11 02:49:14 +02:00
},
| Instruction::LimitFrequency { experiment, so } => {
save_options!(limit_freq(system, experiment, P::parse_experiment)?, so);
2025-09-11 02:49:14 +02:00
},
| Instruction::FastFrequency { experiment, so } => {
save_options!(fast_freq(system, experiment, P::parse_experiment)?, so);
2025-09-11 02:49:14 +02:00
},
| Instruction::Digraph { group, gso } => {
2025-09-11 17:42:54 +02:00
digraph(system)?;
2025-09-10 22:41:40 +02:00
let mut graph = system.clone();
2025-09-11 17:42:54 +02:00
2025-09-10 22:41:40 +02:00
if let Some(group) = group {
group.typecheck()?;
grouping(&mut graph, &group)?;
}
2025-07-12 15:41:10 +02:00
for save in gso {
match save {
2025-09-11 02:49:14 +02:00
| GraphSaveOptions::Dot {
2025-07-12 15:41:10 +02:00
node_display: nd,
edge_display: ed,
node_color: nc,
edge_color: ec,
2025-07-12 15:41:10 +02:00
so,
} => {
2025-09-10 22:41:40 +02:00
save_options!(dot(&graph, nd, ed, nc, ec)?, so);
2025-09-11 02:49:14 +02:00
},
| GraphSaveOptions::GraphML {
2025-07-13 17:28:13 +02:00
node_display: nd,
edge_display: ed,
2025-07-12 15:41:10 +02:00
so,
} => {
2025-09-10 22:41:40 +02:00
save_options!(graphml(&graph, nd, ed)?, so);
2025-09-11 02:49:14 +02:00
},
| GraphSaveOptions::Serialize { path } => {
2025-09-10 22:41:40 +02:00
serialize(&graph, path)?;
2025-09-11 02:49:14 +02:00
},
2025-07-12 15:41:10 +02:00
}
}
2025-09-11 02:49:14 +02:00
},
| Instruction::Bisimilarity {
system_b,
edge_relabeler,
so,
} => {
edge_relabeler.typecheck()?;
save_options!(bisimilar(system, &edge_relabeler, system_b, P::parse_instructions)?, so);
2025-09-11 02:49:14 +02:00
},
2025-07-12 02:42:28 +02:00
}
Ok(())
}
/// Interprets file at supplied path, then executes the code specified as
/// instructions inside the file.
pub fn run<P: FileParsers>(
path: String,
) -> Result<(), String> {
let mut translator = Translator::new();
2025-07-12 15:41:10 +02:00
let Instructions {
system,
instructions,
} = read_file(&mut translator,
path,
P::parse_instructions)?;
2025-07-12 02:42:28 +02:00
let mut system = system.compute(translator)?;
for instr in instructions {
execute::<P>(instr, &mut system)?;
2025-07-12 02:42:28 +02:00
}
Ok(())
}