//! Definitions for generating graphs from a simulation. use petgraph::{Graph, Directed}; use std::collections::HashMap; use super::structure::{RSlabel, RSsystem, RSset}; use super::support_structures::TransitionsIterator; use super::translator::{self, IdType}; use std::rc::Rc; pub type RSgraph = Graph; /// Creates a graph starting from a system as root node pub fn digraph( system: RSsystem ) -> Result { let mut graph = Graph::default(); let node = graph.add_node(system.clone()); let mut association = HashMap::new(); association.insert(system.clone(), node); let mut stack = vec![system]; while let Some(current) = stack.pop() { // depth first let current_node = *association.get(¤t).unwrap(); for (label, next) in TransitionsIterator::from(¤t)? { // 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) } 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()) } // ----------------------------------------------------------------------------- // helper functions // ----------------------------------------------------------------------------- // Nodes ----------------------------------------------------------------------- /// Helper structure that specifies what information to display for nodes. #[derive(Clone)] pub enum NodeDisplayBase { String { string: String }, Hide, Entities, MaskEntities { mask: RSset }, ExcludeEntities { mask: RSset }, Context, UncommonEntities, MaskUncommonEntities { mask: RSset } } pub struct NodeDisplay { pub base: Vec } type GraphMapNodesFnTy<'a> = dyn Fn(petgraph::prelude::NodeIndex, &'a RSsystem) -> String + 'a; fn match_node_display<'a>( base: &NodeDisplayBase, common_entities: Rc, translator: Rc ) -> Box> { use NodeDisplayBase::*; use super::format_helpers::graph_map_nodes_ty_from::*; match base { String { string } => { format_string(string.clone()) }, Hide => { format_hide(translator) }, Entities => { format_entities(translator) }, MaskEntities { mask } => { format_mask_entities(translator, mask.clone()) }, ExcludeEntities { mask } => { format_exclude_entities(translator, mask.clone()) }, Context => { format_context(translator) }, UncommonEntities => { format_exclude_entities(translator, (*common_entities).clone()) }, MaskUncommonEntities { mask } => { format_exclude_entities(translator, mask.intersection(&common_entities)) } } } impl NodeDisplay { fn contains_uncommon(&self) -> bool { self.base.iter().any( |b| matches!(b, NodeDisplayBase::UncommonEntities | NodeDisplayBase::MaskUncommonEntities { mask: _ })) } pub fn generate<'a>( self, translator: Rc, current_graph: &RSgraph ) -> Box> { let common_entities = if self.contains_uncommon() { Rc::new(common_entities(current_graph)) } else { Rc::new(RSset::new()) }; Box::new( move |i, n| { let mut accumulator = String::new(); for b in &self.base { let f = match_node_display(b, Rc::clone(&common_entities), Rc::clone(&translator)); accumulator.push_str(&(f)(i, n)); } accumulator } ) } } // Edges ----------------------------------------------------------------------- /// Helper structure that specifies what information to display for edges #[derive(Clone)] pub enum EdgeDisplayBase { String { string: String }, 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 }, } pub struct EdgeDisplay { pub base: Vec } type GraphMapEdgesFnTy<'a> = dyn Fn(petgraph::prelude::EdgeIndex, &'a RSlabel) -> String + 'a; fn match_edge_display<'a>( base: &'a EdgeDisplayBase, translator: Rc ) -> Box> { use EdgeDisplayBase::*; use super::format_helpers::graph_map_edges_ty_from::*; match base { String { string } => { format_string(translator, string.clone()) } Hide => { format_hide(translator) }, Products => { format_products(translator) }, MaskProducts { mask } => { format_mask_products(translator, mask.clone()) }, Entities => { format_entities(translator) }, MaskEntities { mask } => { format_mask_entities(translator, mask.clone()) }, Context => { format_context(translator) }, MaskContext { mask } => { format_mask_context(translator, mask.clone()) }, Union => { format_union(translator) }, MaskUnion { mask } => { format_mask_union(translator, mask.clone()) }, Difference => { format_difference(translator) }, MaskDifference { mask } => { format_mask_difference(translator, mask.clone()) }, EntitiesDeleted => { format_entities_deleted(translator) }, MaskEntitiesDeleted { mask } => { format_mask_entities_deleted(translator, mask.clone()) }, EntitiesAdded => { format_entities_added(translator) }, MaskEntitiesAdded { mask } => { format_mask_entities_added(translator, mask.clone()) }, } } impl EdgeDisplay { pub fn generate<'a>( self, translator: Rc, _current_graph: &RSgraph ) -> Box> { Box::new( move |i, n| { let mut accumulator = String::new(); for b in &self.base { let f = match_edge_display(b, Rc::clone(&translator)); accumulator.push_str(&(f)(i, n)); } accumulator } ) } } // ----------------------------------------------------------------------------- // Color Nodes & Edges // ----------------------------------------------------------------------------- // Node ------------------------------------------------------------------------ use petgraph::visit::{IntoEdgeReferences, IntoNodeReferences}; type RSdotGraph = Graph; type RSformatNodeTy<'a> = dyn Fn( &'a RSdotGraph, <&'a RSdotGraph as IntoNodeReferences>::NodeRef ) -> String + 'a; type RSformatNodeTyOpt<'a> = dyn Fn( &'a RSdotGraph, <&'a RSdotGraph as IntoNodeReferences>::NodeRef ) -> Option + 'a; #[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, } #[inline(always)] fn node_formatter_base_color( base_color: String ) -> String { ", fillcolor=".to_string() + &base_color } #[inline(always)] fn match_node_color_conditional<'a>( rule: &'a NodeColorConditional, color: &'a String, original_graph: Rc, star: Option ) -> Box> { use super::format_helpers::node_formatter::*; match rule { NodeColorConditional::ContextConditional(ccc) => { match ccc { ContextColorConditional::Nill => { format_nill(Rc::clone(&original_graph), color.to_string(), star) }, ContextColorConditional::RecursiveIdentifier(s) => { format_recursive_identifier(Rc::clone(&original_graph), color.to_string(), star, *s) }, ContextColorConditional::EntitySet(ot, set) => { format_entity_set(Rc::clone(&original_graph), color.to_string(), star, *ot, set.clone()) }, ContextColorConditional::NonDeterministicChoice => { format_non_deterministic_choice(Rc::clone(&original_graph), color.to_string(), star) }, ContextColorConditional::Summation => { format_summation(Rc::clone(&original_graph), color.to_string(), star) }, ContextColorConditional::WaitEntity => { format_wait_entity(Rc::clone(&original_graph), color.to_string(), star) }, } }, NodeColorConditional::EntitiesConditional(ot, set) => { format_entities_conditional(Rc::clone(&original_graph), color.to_string(), star, *ot, set.clone()) }, } } impl NodeColor { pub fn generate<'a>( self, original_graph: Rc, star: Option ) -> Box> { Box::new( move |i, n| { for (rule, color) in &self.conditionals { let f = match_node_color_conditional( rule, color, Rc::clone(&original_graph), star ); if let Some(s) = (f)(i, n) { return s } } node_formatter_base_color(self.base_color.clone()) } ) } } // Edge ------------------------------------------------------------------------ type RSformatEdgeTy<'a> = dyn Fn( &'a RSdotGraph, <&'a RSdotGraph as IntoEdgeReferences>::EdgeRef ) -> String + 'a; type RSformatEdgeTyOpt<'a> = dyn Fn( &'a RSdotGraph, <&'a RSdotGraph as IntoEdgeReferences>::EdgeRef ) -> Option + 'a; #[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 } fn edge_formatter_base_color( base_color: String ) -> String { ", color=".to_string() + &base_color } fn match_edge_color_conditional<'a>( rule: &'a EdgeColorConditional, color: &'a String, original_graph: Rc ) -> Box> { use super::format_helpers::edge_formatter::*; match rule { EdgeColorConditional::Entities(ot, set) => { format_entities(Rc::clone(&original_graph), color.to_string(), *ot, set.clone()) }, EdgeColorConditional::Context(ot, set) => { format_context(Rc::clone(&original_graph), color.to_string(), *ot, set.clone()) }, EdgeColorConditional::T(ot, set) => { format_t(Rc::clone(&original_graph), color.to_string(), *ot, set.clone()) }, EdgeColorConditional::Reactants(ot, set) => { format_reactants(Rc::clone(&original_graph), color.to_string(), *ot, set.clone()) }, EdgeColorConditional::ReactantsAbsent(ot, set) => { format_reactants_absent(Rc::clone(&original_graph), color.to_string(), *ot, set.clone()) }, EdgeColorConditional::Inhibitors(ot, set) => { format_inhibitors(Rc::clone(&original_graph), color.to_string(), *ot, set.clone()) }, EdgeColorConditional::InhibitorsPresent(ot, set) => { format_inhibitors_present(Rc::clone(&original_graph), color.to_string(), *ot, set.clone()) }, EdgeColorConditional::Products(ot, set) => { format_products(Rc::clone(&original_graph), color.to_string(), *ot, set.clone()) }, } } impl EdgeColor { pub fn generate<'a>( self, original_graph: Rc, ) -> Box> { Box::new( move |i, n| { for (rule, color) in &self.conditionals { let f = match_edge_color_conditional( rule, color, Rc::clone(&original_graph), ); if let Some(s) = (f)(i, n) { return s } } edge_formatter_base_color(self.base_color.clone()) } ) } }