Files
ReactionSystems/src/rsprocess/graph.rs

491 lines
12 KiB
Rust
Raw Normal View History

2025-07-10 15:02:14 +02:00
//! Definitions for generating graphs from a simulation.
use petgraph::{Graph, Directed};
2025-07-05 14:54:43 +02:00
use std::collections::HashMap;
2025-07-26 16:46:48 +02:00
use super::structure::{RSlabel, RSsystem, RSset};
2025-07-05 14:54:43 +02:00
use super::support_structures::TransitionsIterator;
2025-07-13 17:28:13 +02:00
use super::translator::{self, IdType};
2025-07-07 01:25:38 +02:00
use std::rc::Rc;
2025-07-05 14:54:43 +02:00
2025-07-16 00:05:14 +02:00
pub type RSgraph = Graph<RSsystem, RSlabel, Directed, u32>;
2025-07-10 15:02:14 +02:00
/// Creates a graph starting from a system as root node
2025-07-05 14:54:43 +02:00
pub fn digraph(
system: RSsystem
) -> Result<RSgraph, String> {
let mut graph = Graph::default();
2025-07-05 14:54:43 +02:00
let node = graph.add_node(system.clone());
let mut association = HashMap::new();
association.insert(system.clone(), node);
let mut stack = vec![system];
let mut current;
while !stack.is_empty() {
// depth first
current = stack.pop().unwrap();
let current_node = *association.get(&current).unwrap();
for (label, next) in TransitionsIterator::from(&current)? {
2025-07-05 14:54:43 +02:00
// if not already visited
let next_node = association.entry(next.clone()).or_insert_with(|| {
stack.push(next.clone());
graph.add_node(next)
});
graph.add_edge(current_node, *next_node, label);
}
}
Ok(graph)
}
2025-07-07 01:25:38 +02:00
pub fn common_entities(
graph: &RSgraph
) -> RSset {
graph.node_references().fold(
None,
|acc, node|
match acc {
None => Some(node.1.available_entities.clone()),
Some(acc) => Some(node.1.available_entities.intersection(&acc))
}
).unwrap_or(RSset::new())
}
2025-07-07 01:25:38 +02:00
// -----------------------------------------------------------------------------
// helper functions
// -----------------------------------------------------------------------------
// Nodes -----------------------------------------------------------------------
/// Helper structure that specifies what information to display for nodes.
#[derive(Clone)]
2025-07-07 01:25:38 +02:00
pub enum GraphMapNodes {
String { string: String },
2025-07-07 01:25:38 +02:00
Hide,
Entities,
MaskEntities { mask: RSset },
ExcludeEntities { mask: RSset },
2025-07-07 01:25:38 +02:00
Context,
}
type GraphMapNodesFnTy =
dyn Fn(petgraph::prelude::NodeIndex, &RSsystem) -> String;
/// Helper structure that holds a formatting function from node as RSsystem to
/// string
2025-07-07 01:25:38 +02:00
pub struct GraphMapNodesTy {
functions: Vec<Box<GraphMapNodesFnTy>>,
translator: Rc<translator::Translator>
2025-07-07 01:25:38 +02:00
}
impl<const N: usize> From<([GraphMapNodes; N], Rc<translator::Translator>)> for GraphMapNodesTy {
fn from(value: ([GraphMapNodes; N], Rc<translator::Translator>)) -> Self {
Self::from((value.0.to_vec(), value.1))
}
}
2025-07-26 20:16:32 +02:00
impl From<(&[GraphMapNodes], Rc<translator::Translator>)> for GraphMapNodesTy {
fn from(value: (&[GraphMapNodes], Rc<translator::Translator>)) -> Self {
Self::from((value.0.to_vec(), value.1))
}
}
impl From<(Vec<GraphMapNodes>, Rc<translator::Translator>)> for GraphMapNodesTy {
fn from(value: (Vec<GraphMapNodes>, Rc<translator::Translator>)) -> Self {
2025-07-07 01:25:38 +02:00
use GraphMapNodes::*;
2025-07-26 16:46:48 +02:00
use super::format_helpers::graph_map_nodes_ty_from::*;
let mut new = GraphMapNodesTy {functions: vec![], translator: value.1};
for f in value.0 {
2025-07-07 01:25:38 +02:00
match f {
String { string } => {
new.functions.push(format_string(string.clone()));
}
2025-07-07 01:25:38 +02:00
Hide => {
new.functions.push(format_hide(
Rc::clone(&new.translator)
));
2025-07-07 01:25:38 +02:00
},
Entities => {
new.functions.push(format_entities(
Rc::clone(&new.translator)
));
2025-07-07 01:25:38 +02:00
},
MaskEntities { mask } => {
new.functions.push(format_mask_entities(
Rc::clone(&new.translator),
mask.clone()
));
2025-07-07 01:25:38 +02:00
},
ExcludeEntities { mask } => {
new.functions.push(format_exclude_entities(
Rc::clone(&new.translator),
mask.clone()
));
}
2025-07-07 01:25:38 +02:00
Context => {
new.functions.push(format_context(
Rc::clone(&new.translator)
));
2025-07-07 01:25:38 +02:00
},
};
}
2025-07-07 01:25:38 +02:00
new
2025-07-07 01:25:38 +02:00
}
}
impl GraphMapNodesTy {
pub fn generate<'a>(
2025-07-26 20:01:46 +02:00
self
2025-07-26 20:16:32 +02:00
) -> Box<GraphMapNodesFnTy>
{
2025-07-26 20:16:32 +02:00
let mut accumulator: Box<GraphMapNodesFnTy> =
2025-07-26 20:01:46 +02:00
super::format_helpers::graph_map_nodes_ty_from::format_hide(
Rc::clone(&self.translator)
);
for f in self.functions {
accumulator = Box::new(move |i, n| {
(accumulator)(i, n)
+ &f(i, n)
})
}
accumulator
2025-07-07 01:25:38 +02:00
}
}
2025-07-07 01:25:38 +02:00
// Edges -----------------------------------------------------------------------
/// Helper structure that specifies what information to display for edges
#[derive(Clone)]
2025-07-07 01:25:38 +02:00
pub enum GraphMapEdges {
2025-07-26 20:01:46 +02:00
String { string: String },
2025-07-07 01:25:38 +02:00
Hide,
Products,
MaskProducts { mask: RSset },
Entities,
MaskEntities { mask: RSset },
Context,
MaskContext { mask: RSset },
Union,
MaskUnion { mask: RSset },
Difference,
MaskDifference { mask: RSset },
EntitiesDeleted,
MaskEntitiesDeleted { mask: RSset },
EntitiesAdded,
MaskEntitiesAdded { mask: RSset },
}
type GraphMapEdgesFnTy = dyn Fn(petgraph::prelude::EdgeIndex, &RSlabel) -> String;
/// Helper structure that holds a formatting function from node as RSsystem to
/// string
2025-07-07 01:25:38 +02:00
pub struct GraphMapEdgesTy {
2025-07-26 20:16:32 +02:00
functions: Vec<Box<GraphMapEdgesFnTy>>,
2025-07-26 20:01:46 +02:00
translator: Rc<translator::Translator>
2025-07-07 01:25:38 +02:00
}
2025-07-26 20:16:32 +02:00
impl<const N: usize> From<([GraphMapEdges; N], Rc<translator::Translator>)> for GraphMapEdgesTy {
fn from(value: ([GraphMapEdges; N], Rc<translator::Translator>)) -> Self {
Self::from((value.0.to_vec(), value.1))
}
}
impl From<(&[GraphMapEdges], Rc<translator::Translator>)> for GraphMapEdgesTy {
fn from(value: (&[GraphMapEdges], Rc<translator::Translator>)) -> Self {
Self::from((value.0.to_vec(), value.1))
}
}
impl From<(Vec<GraphMapEdges>, Rc<translator::Translator>)> for GraphMapEdgesTy {
fn from(value: (Vec<GraphMapEdges>, Rc<translator::Translator>)) -> Self {
2025-07-07 01:25:38 +02:00
use GraphMapEdges::*;
2025-07-26 16:46:48 +02:00
use super::format_helpers::graph_map_edges_ty_from::*;
2025-07-26 20:16:32 +02:00
let mut new = GraphMapEdgesTy {functions: vec![], translator: value.1};
for f in value.0 {
2025-07-07 01:25:38 +02:00
match f {
2025-07-26 20:01:46 +02:00
String { string } => {
2025-07-26 20:16:32 +02:00
new.functions.push(format_string(
Rc::clone(&new.translator), string))
2025-07-26 20:01:46 +02:00
}
2025-07-07 01:25:38 +02:00
Hide => {
2025-07-26 20:16:32 +02:00
new.functions.push(format_hide(
Rc::clone(&new.translator)
))
2025-07-07 01:25:38 +02:00
},
Products => {
2025-07-26 20:16:32 +02:00
new.functions.push(format_products(
Rc::clone(&new.translator)
))
2025-07-07 01:25:38 +02:00
},
MaskProducts { mask } => {
2025-07-26 20:16:32 +02:00
new.functions.push(format_mask_products(
Rc::clone(&new.translator), mask))
2025-07-07 01:25:38 +02:00
},
Entities => {
2025-07-26 20:16:32 +02:00
new.functions.push(format_entities(
Rc::clone(&new.translator)
))
2025-07-07 01:25:38 +02:00
},
MaskEntities { mask } => {
2025-07-26 20:16:32 +02:00
new.functions.push(format_mask_entities(
Rc::clone(&new.translator), mask))
2025-07-07 01:25:38 +02:00
},
Context => {
2025-07-26 20:16:32 +02:00
new.functions.push(format_context(
Rc::clone(&new.translator)
))
2025-07-07 01:25:38 +02:00
},
MaskContext { mask } => {
2025-07-26 20:16:32 +02:00
new.functions.push(format_mask_context(
Rc::clone(&new.translator), mask))
2025-07-07 01:25:38 +02:00
},
Union => {
2025-07-26 20:16:32 +02:00
new.functions.push(format_union(
Rc::clone(&new.translator)
))
2025-07-07 01:25:38 +02:00
},
MaskUnion { mask } => {
2025-07-26 20:16:32 +02:00
new.functions.push(format_mask_union(
Rc::clone(&new.translator), mask))
2025-07-07 01:25:38 +02:00
},
Difference => {
2025-07-26 20:16:32 +02:00
new.functions.push(format_difference(
Rc::clone(&new.translator)
))
2025-07-07 01:25:38 +02:00
},
MaskDifference { mask } => {
2025-07-26 20:16:32 +02:00
new.functions.push(format_mask_difference(
Rc::clone(&new.translator), mask))
2025-07-07 01:25:38 +02:00
},
EntitiesDeleted => {
2025-07-26 20:16:32 +02:00
new.functions.push(format_entities_deleted(
Rc::clone(&new.translator)
))
2025-07-07 01:25:38 +02:00
},
MaskEntitiesDeleted { mask } => {
2025-07-26 20:16:32 +02:00
new.functions.push(format_mask_entities_deleted(
Rc::clone(&new.translator), mask))
2025-07-07 01:25:38 +02:00
},
EntitiesAdded => {
2025-07-26 20:16:32 +02:00
new.functions.push(format_entities_added(
Rc::clone(&new.translator)
))
2025-07-07 01:25:38 +02:00
},
MaskEntitiesAdded { mask } => {
2025-07-26 20:16:32 +02:00
new.functions.push(format_mask_entities_added(
Rc::clone(&new.translator), mask))
2025-07-07 01:25:38 +02:00
},
};
2025-07-26 20:16:32 +02:00
}
new
2025-07-07 01:25:38 +02:00
}
2025-07-26 20:16:32 +02:00
}
2025-07-07 01:25:38 +02:00
2025-07-26 20:16:32 +02:00
impl GraphMapEdgesTy {
pub fn generate(
self
) -> Box<GraphMapEdgesFnTy> {
let mut accumulator: Box<GraphMapEdgesFnTy> =
super::format_helpers::graph_map_edges_ty_from::format_hide(
Rc::clone(&self.translator)
);
for f in self.functions {
accumulator = Box::new(move |i, n| {
(accumulator)(i, n)
+ &f(i, n)
})
}
accumulator
2025-07-07 01:25:38 +02:00
}
}
// -----------------------------------------------------------------------------
2025-07-26 20:01:46 +02:00
// Color Nodes & Edges
// -----------------------------------------------------------------------------
2025-07-26 16:46:48 +02:00
use petgraph::visit::{IntoEdgeReferences, IntoNodeReferences};
type RSdotGraph = Graph<String, String, Directed, u32>;
type RSformatNodeTy =
2025-07-13 17:28:13 +02:00
dyn Fn(
&RSdotGraph,
<&RSdotGraph as IntoNodeReferences>::NodeRef
) -> Option<String>;
2025-07-13 17:28:13 +02:00
#[derive(Clone, Copy)]
pub enum OperationType {
Equals,
Subset,
SubsetEqual,
Superset,
SupersetEqual
}
impl OperationType {
pub fn evaluate(&self, a: &RSset, b: &RSset) -> bool {
match self {
Self::Equals => {
a.is_subset(b) && b.is_subset(a)
},
Self::Subset => {
a.is_subset(b) && !b.is_subset(a)
},
Self::SubsetEqual => {
a.is_subset(b)
},
Self::Superset => {
b.is_subset(a) && !a.is_subset(b)
},
Self::SupersetEqual => {
b.is_subset(a)
}
}
}
}
#[derive(Clone)]
pub enum ContextColorConditional {
Nill,
RecursiveIdentifier(IdType),
EntitySet(OperationType, RSset),
NonDeterministicChoice,
Summation,
WaitEntity
}
#[derive(Clone)]
pub enum NodeColorConditional {
ContextConditional(ContextColorConditional),
EntitiesConditional(OperationType, RSset)
}
#[derive(Clone)]
pub struct NodeColor {
pub conditionals: Vec<(NodeColorConditional, String)>,
pub base_color: String
}
2025-07-13 18:14:35 +02:00
pub fn node_formatter_base_color(
base_color: String
) -> String
{
", fillcolor=".to_string() + &base_color
}
2025-07-26 16:46:48 +02:00
2025-07-13 17:28:13 +02:00
pub fn node_formatter(
original_graph: Rc<RSgraph>,
rule: NodeColorConditional,
color: String,
star: Option<IdType>,
) -> Box<RSformatNodeTy>
{
2025-07-26 16:46:48 +02:00
use super::format_helpers::node_formatter::*;
2025-07-13 17:28:13 +02:00
match rule {
NodeColorConditional::ContextConditional(ccc) => {
match ccc {
ContextColorConditional::Nill => {
2025-07-26 16:46:48 +02:00
format_nill(original_graph, color, star)
2025-07-13 17:28:13 +02:00
},
ContextColorConditional::RecursiveIdentifier(s) => {
2025-07-26 16:46:48 +02:00
format_recursive_identifier(original_graph, color, star, s)
2025-07-13 17:28:13 +02:00
},
ContextColorConditional::EntitySet(ot, set) => {
2025-07-26 16:46:48 +02:00
format_entity_set(original_graph, color, star, ot, set)
2025-07-13 17:28:13 +02:00
},
ContextColorConditional::NonDeterministicChoice => {
2025-07-26 16:46:48 +02:00
format_non_deterministic_choice(original_graph, color, star)
2025-07-13 17:28:13 +02:00
},
ContextColorConditional::Summation => {
2025-07-26 16:46:48 +02:00
format_summation(original_graph, color, star)
2025-07-13 17:28:13 +02:00
},
ContextColorConditional::WaitEntity => {
2025-07-26 16:46:48 +02:00
format_wait_entity(original_graph, color, star)
2025-07-13 17:28:13 +02:00
},
}
2025-07-13 17:28:13 +02:00
},
NodeColorConditional::EntitiesConditional(ot, set) => {
2025-07-26 16:46:48 +02:00
format_entities_conditional(original_graph, color, star, ot, set)
2025-07-13 17:28:13 +02:00
},
}
}
2025-07-13 19:08:39 +02:00
type RSformatEdgeTy =
dyn Fn(
&RSdotGraph,
<&RSdotGraph as IntoEdgeReferences>::EdgeRef
) -> Option<String>;
#[derive(Clone)]
pub enum EdgeColorConditional {
Entities(OperationType, RSset),
Context(OperationType, RSset),
T(OperationType, RSset),
Reactants(OperationType, RSset),
ReactantsAbsent(OperationType, RSset),
Inhibitors(OperationType, RSset),
InhibitorsPresent(OperationType, RSset),
Products(OperationType, RSset),
}
#[derive(Clone)]
pub struct EdgeColor {
pub conditionals: Vec<(EdgeColorConditional, String)>,
pub base_color: String
}
pub fn edge_formatter_base_color(
base_color: String
) -> String
{
2025-07-13 19:32:11 +02:00
", color=".to_string() + &base_color
2025-07-13 19:08:39 +02:00
}
2025-07-26 16:46:48 +02:00
2025-07-13 17:28:13 +02:00
pub fn edge_formatter(
2025-07-13 19:08:39 +02:00
original_graph: Rc<RSgraph>,
rule: EdgeColorConditional,
color: String,
) -> Box<RSformatEdgeTy>
{
2025-07-26 16:46:48 +02:00
use super::format_helpers::edge_formatter::*;
2025-07-13 19:08:39 +02:00
match rule {
EdgeColorConditional::Entities(ot, set) => {
2025-07-26 16:46:48 +02:00
format_entities(original_graph, color, ot, set)
2025-07-13 19:08:39 +02:00
},
EdgeColorConditional::Context(ot, set) => {
2025-07-26 16:46:48 +02:00
format_context(original_graph, color, ot, set)
2025-07-13 19:08:39 +02:00
},
EdgeColorConditional::T(ot, set) => {
2025-07-26 16:46:48 +02:00
format_t(original_graph, color, ot, set)
2025-07-13 19:08:39 +02:00
},
EdgeColorConditional::Reactants(ot, set) => {
2025-07-26 16:46:48 +02:00
format_reactants(original_graph, color, ot, set)
2025-07-13 19:08:39 +02:00
},
EdgeColorConditional::ReactantsAbsent(ot, set) => {
2025-07-26 16:46:48 +02:00
format_reactants_absent(original_graph, color, ot, set)
2025-07-13 19:08:39 +02:00
},
EdgeColorConditional::Inhibitors(ot, set) => {
2025-07-26 16:46:48 +02:00
format_inhibitors(original_graph, color, ot, set)
2025-07-13 19:08:39 +02:00
},
EdgeColorConditional::InhibitorsPresent(ot, set) => {
2025-07-26 16:46:48 +02:00
format_inhibitors_present(original_graph, color, ot, set)
2025-07-13 19:08:39 +02:00
},
EdgeColorConditional::Products(ot, set) => {
2025-07-26 16:46:48 +02:00
format_products(original_graph, color, ot, set)
2025-07-13 19:08:39 +02:00
},
}
}