Added way to mask common entities in labels

This commit is contained in:
elvis
2025-07-27 19:08:27 +02:00
parent 7fcaa5c13b
commit 4b866b63da
3 changed files with 290 additions and 104 deletions

View File

@ -39,11 +39,17 @@ match {
"Stats", "Target", "Run", "Loop", "Frequency", "LimitFrequency", "Stats", "Target", "Run", "Loop", "Frequency", "LimitFrequency",
"FastFrequency", "Digraph", "Bisimilarity", "FastFrequency", "Digraph", "Bisimilarity",
"Deserialize", "Deserialize",
"Hide", "Entities", "MaskEntities", "MaskContext", "Hide",
"Products", "MaskProducts", "Union", "MaskUnion", "Entities", "MaskEntities", "UncommonEntities", "UncommonMaskEntities",
"MaskContext", "UncommonContext", "UncommonMaskContext",
"Products", "MaskProducts", "UncommonProducts", "UncommonMaskProducts",
"Union", "MaskUnion", "UncommonUnion", "UncommonMaskUnion",
"Difference", "MaskDifference", "Difference", "MaskDifference",
"UncommonDifference", "UncommonMaskDifference",
"EntitiesDeleted", "MaskEntitiesDeleted", "EntitiesDeleted", "MaskEntitiesDeleted",
"EntitiesAdded", "MaskEntitiesAdded" "UncommonEntitiesDeleted", "UncommonMaskEntitiesDeleted",
"EntitiesAdded", "MaskEntitiesAdded",
"UncommonEntitiesAdded", "UncommonMaskEntitiesAdded",
} else { } else {
r"[0-9]+" => NUMBER r"[0-9]+" => NUMBER
} else { } else {
@ -320,34 +326,97 @@ SeparatorNode: graph::NodeDisplay = {
EdgeDisplay: graph::EdgeDisplayBase = { EdgeDisplay: graph::EdgeDisplayBase = {
"Hide" => "Hide" =>
graph::EdgeDisplayBase::Hide, graph::EdgeDisplayBase::Hide,
"Products" => "Products" =>
graph::EdgeDisplayBase::Products, graph::EdgeDisplayBase::Products
{ mask: None, filter_common: false },
"MaskProducts" <mask: Set> => "MaskProducts" <mask: Set> =>
graph::EdgeDisplayBase::MaskEntities{ mask }, graph::EdgeDisplayBase::Entities
{ mask: Some(mask), filter_common: false },
"UncommonProducts" =>
graph::EdgeDisplayBase::Products
{ mask: None, filter_common: true },
"UncommonMaskProducts" <mask: Set> =>
graph::EdgeDisplayBase::Entities
{ mask: Some(mask), filter_common: true },
"Entities" => "Entities" =>
graph::EdgeDisplayBase::Entities, graph::EdgeDisplayBase::Entities
{ mask: None, filter_common: false },
"MaskEntities" <mask: Set> => "MaskEntities" <mask: Set> =>
graph::EdgeDisplayBase::MaskEntities{ mask }, graph::EdgeDisplayBase::Entities
{ mask: Some(mask), filter_common: false },
"UncommonEntities" =>
graph::EdgeDisplayBase::Entities
{ mask: None, filter_common: true },
"UncommonMaskEntities" <mask: Set> =>
graph::EdgeDisplayBase::Entities
{ mask: Some(mask), filter_common: true },
"Context" => "Context" =>
graph::EdgeDisplayBase::Context, graph::EdgeDisplayBase::Context
{ mask: None, filter_common: false },
"MaskContext" <mask: Set> => "MaskContext" <mask: Set> =>
graph::EdgeDisplayBase::MaskContext{ mask }, graph::EdgeDisplayBase::Context
{ mask: Some(mask), filter_common: false },
"UncommonContext" =>
graph::EdgeDisplayBase::Context
{ mask: None, filter_common: true },
"UncommonMaskContext" <mask: Set> =>
graph::EdgeDisplayBase::Context
{ mask: Some(mask), filter_common: true },
"Union" => "Union" =>
graph::EdgeDisplayBase::Union, graph::EdgeDisplayBase::Union
{ mask: None, filter_common: false },
"MaskUnion" <mask: Set> => "MaskUnion" <mask: Set> =>
graph::EdgeDisplayBase::MaskUnion{ mask }, graph::EdgeDisplayBase::Union
{ mask: Some(mask), filter_common: false },
"UncommonUnion" =>
graph::EdgeDisplayBase::Union
{ mask: None, filter_common: true },
"UncommonMaskUnion" <mask: Set> =>
graph::EdgeDisplayBase::Union
{ mask: Some(mask), filter_common: true },
"Difference" => "Difference" =>
graph::EdgeDisplayBase::Difference, graph::EdgeDisplayBase::Difference
{ mask: None, filter_common: false },
"MaskDifference" <mask: Set> => "MaskDifference" <mask: Set> =>
graph::EdgeDisplayBase::MaskDifference{ mask }, graph::EdgeDisplayBase::Difference
{ mask: Some(mask), filter_common: false },
"UncommonDifference" =>
graph::EdgeDisplayBase::Difference
{ mask: None, filter_common: true },
"UncommonMaskDifference" <mask: Set> =>
graph::EdgeDisplayBase::Difference
{ mask: Some(mask), filter_common: true },
"EntitiesDeleted" => "EntitiesDeleted" =>
graph::EdgeDisplayBase::EntitiesDeleted, graph::EdgeDisplayBase::EntitiesDeleted
{ mask: None, filter_common: false },
"MaskEntitiesDeleted" <mask: Set> => "MaskEntitiesDeleted" <mask: Set> =>
graph::EdgeDisplayBase::MaskEntitiesDeleted{ mask }, graph::EdgeDisplayBase::EntitiesDeleted
{ mask: Some(mask), filter_common: false },
"UncommonEntitiesDeleted" =>
graph::EdgeDisplayBase::EntitiesDeleted
{ mask: None, filter_common: true },
"UncommonMaskEntitiesDeleted" <mask: Set> =>
graph::EdgeDisplayBase::EntitiesDeleted
{ mask: Some(mask), filter_common: true },
"EntitiesAdded" => "EntitiesAdded" =>
graph::EdgeDisplayBase::EntitiesAdded, graph::EdgeDisplayBase::EntitiesAdded
{ mask: None, filter_common: false },
"MaskEntitiesAdded" <mask: Set> => "MaskEntitiesAdded" <mask: Set> =>
graph::EdgeDisplayBase::MaskEntitiesAdded{ mask }, graph::EdgeDisplayBase::EntitiesAdded
{ mask: Some(mask), filter_common: false },
"UncommonEntitiesAdded" =>
graph::EdgeDisplayBase::EntitiesAdded
{ mask: None, filter_common: true },
"UncommonMaskEntitiesAdded" <mask: Set> =>
graph::EdgeDisplayBase::EntitiesAdded
{ mask: Some(mask), filter_common: true },
} }
/// Edge display formatters separated by arbitrary strings in quotes /// Edge display formatters separated by arbitrary strings in quotes

View File

@ -39,7 +39,7 @@ pub fn digraph(
} }
pub fn common_entities(graph: &RSgraph) -> RSset { fn common_system_entities(graph: &RSgraph) -> RSset {
graph.node_references().fold( graph.node_references().fold(
None, None,
|acc, node| |acc, node|
@ -50,6 +50,71 @@ pub fn common_entities(graph: &RSgraph) -> RSset {
).unwrap_or(RSset::new()) ).unwrap_or(RSset::new())
} }
macro_rules! common_label {
(
$name:ident,
[$edge_name:ident, $acc_name:ident],
$empty_expr:expr,
$some_expr:expr
) => {
fn $name(graph: &RSgraph) -> RSset {
graph.edge_references().fold(
None,
|$acc_name, $edge_name| {
let $edge_name = $edge_name.weight();
match $acc_name {
None => Some($empty_expr),
Some($acc_name) => Some($some_expr)
}
}
).unwrap_or(RSset::new())
}
};
}
common_label!(
common_label_products,
[edge, acc],
edge.products.clone(),
edge.products.intersection(&acc)
);
common_label!(
common_label_entities,
[edge, acc],
edge.available_entities.clone(),
edge.available_entities.intersection(&acc)
);
common_label!(
common_label_context,
[edge, acc],
edge.context.clone(),
edge.context.intersection(&acc)
);
common_label!(
common_label_union,
[edge, acc],
edge.t.clone(),
edge.t.intersection(&acc)
);
common_label!(
common_label_difference,
[edge, acc],
edge.context.subtraction(&edge.available_entities),
edge.context.subtraction(&edge.available_entities).intersection(&acc)
);
common_label!(
common_label_entities_deleted,
[edge, acc],
edge.available_entities.subtraction(&edge.products),
edge.available_entities.subtraction(&edge.products).intersection(&acc)
);
common_label!(
common_label_entities_added,
[edge, acc],
edge.products.subtraction(&edge.available_entities),
edge.products.subtraction(&edge.available_entities).intersection(&acc)
);
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// helper functions // helper functions
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -131,7 +196,7 @@ impl NodeDisplay {
) -> Box<GraphMapNodesFnTy<'a>> { ) -> Box<GraphMapNodesFnTy<'a>> {
let common_entities = let common_entities =
if self.contains_uncommon() { if self.contains_uncommon() {
Rc::new(common_entities(current_graph)) Rc::new(common_system_entities(current_graph))
} else { } else {
Rc::new(RSset::new()) Rc::new(RSset::new())
}; };
@ -155,38 +220,41 @@ impl NodeDisplay {
// Edges ----------------------------------------------------------------------- // Edges -----------------------------------------------------------------------
/// Helper structure that specifies what information to display for edges
#[derive(Clone)] #[derive(Clone)]
pub enum EdgeDisplayBase { pub enum EdgeDisplayBase {
String { string: String }, String { string: String },
Hide, Hide,
Products, Products { mask: Option<RSset>, filter_common: bool },
MaskProducts { mask: RSset }, Entities { mask: Option<RSset>, filter_common: bool },
Entities, Context { mask: Option<RSset>, filter_common: bool },
MaskEntities { mask: RSset }, Union { mask: Option<RSset>, filter_common: bool },
Context, Difference { mask: Option<RSset>, filter_common: bool },
MaskContext { mask: RSset }, EntitiesDeleted { mask: Option<RSset>, filter_common: bool },
Union, EntitiesAdded { mask: Option<RSset>, filter_common: bool },
MaskUnion { mask: RSset },
Difference,
MaskDifference { mask: RSset },
EntitiesDeleted,
MaskEntitiesDeleted { mask: RSset },
EntitiesAdded,
MaskEntitiesAdded { mask: RSset },
} }
pub struct EdgeDisplay { pub struct EdgeDisplay {
pub base: Vec<EdgeDisplayBase> pub base: Vec<EdgeDisplayBase>,
} }
type GraphMapEdgesFnTy<'a> = type GraphMapEdgesFnTy<'a> =
dyn Fn(petgraph::prelude::EdgeIndex, &'a RSlabel) -> String + 'a; dyn Fn(petgraph::prelude::EdgeIndex, &'a RSlabel) -> String + 'a;
#[derive(Default, Clone)]
struct CommonEntities {
common_products: RSset,
common_entities: RSset,
common_context: RSset,
common_union: RSset,
common_difference: RSset,
common_entities_deleted: RSset,
common_entities_added: RSset,
}
fn match_edge_display<'a>( fn match_edge_display<'a>(
base: &'a EdgeDisplayBase, base: &'a EdgeDisplayBase,
translator: Rc<translator::Translator> translator: Rc<translator::Translator>,
common: CommonEntities
) -> Box<GraphMapEdgesFnTy<'a>> { ) -> Box<GraphMapEdgesFnTy<'a>> {
use EdgeDisplayBase::*; use EdgeDisplayBase::*;
use super::format_helpers::graph_map_edges_ty_from::*; use super::format_helpers::graph_map_edges_ty_from::*;
@ -198,65 +266,144 @@ fn match_edge_display<'a>(
Hide => { Hide => {
format_hide(translator) format_hide(translator)
}, },
Products => { Products { mask, filter_common } => {
format_products(translator) if *filter_common {
format_products(translator, mask.clone(),
Some(common.common_products))
} else {
format_products(translator, mask.clone(), None)
}
}, },
MaskProducts { mask } => { Entities { mask, filter_common } => {
format_mask_products(translator, mask.clone()) if *filter_common {
format_entities(translator, mask.clone(),
Some(common.common_entities))
} else {
format_entities(translator, mask.clone(), None)
}
}, },
Entities => { Context { mask, filter_common } => {
format_entities(translator) if *filter_common {
format_context(translator, mask.clone(),
Some(common.common_context))
} else {
format_context(translator, mask.clone(), None)
}
}, },
MaskEntities { mask } => { Union { mask, filter_common } => {
format_mask_entities(translator, mask.clone()) if *filter_common {
format_union(translator, mask.clone(),
Some(common.common_union))
} else {
format_union(translator, mask.clone(), None)
}
}, },
Context => { Difference { mask, filter_common } => {
format_context(translator) if *filter_common {
format_difference(translator, mask.clone(),
Some(common.common_difference))
} else {
format_difference(translator, mask.clone(), None)
}
}, },
MaskContext { mask } => { EntitiesDeleted { mask, filter_common } => {
format_mask_context(translator, mask.clone()) if *filter_common {
format_entities_deleted(translator, mask.clone(),
Some(common.common_entities_deleted))
} else {
format_entities_deleted(translator, mask.clone(), None)
}
}, },
Union => { EntitiesAdded { mask, filter_common } => {
format_union(translator) if *filter_common {
}, format_entities_added(translator, mask.clone(),
MaskUnion { mask } => { Some(common.common_entities_added))
format_mask_union(translator, mask.clone()) } else {
}, format_entities_added(translator, mask.clone(), None)
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())
}, },
} }
} }
macro_rules! common_entity {
($name:ident, $match:pat, $filter_common:ident) => {
fn $name(&self) -> bool {
self.base.iter().any(
|b|
if let $match = b {
*$filter_common
} else {
false
}
)
}
};
}
impl EdgeDisplay { impl EdgeDisplay {
common_entity!(common_products,
EdgeDisplayBase::Products {mask: _, filter_common},
filter_common);
common_entity!(common_entities,
EdgeDisplayBase::Entities {mask: _, filter_common},
filter_common);
common_entity!(common_context,
EdgeDisplayBase::Context {mask: _, filter_common},
filter_common);
common_entity!(common_union,
EdgeDisplayBase::Union {mask: _, filter_common},
filter_common);
common_entity!(common_difference,
EdgeDisplayBase::Difference {mask: _, filter_common},
filter_common);
common_entity!(common_entities_deleted,
EdgeDisplayBase::EntitiesDeleted {mask: _, filter_common},
filter_common);
common_entity!(common_entities_added,
EdgeDisplayBase::EntitiesAdded {mask: _, filter_common},
filter_common);
pub fn generate<'a>( pub fn generate<'a>(
self, self,
translator: Rc<translator::Translator>, translator: Rc<translator::Translator>,
_current_graph: &RSgraph current_graph: &RSgraph
) -> Box<GraphMapEdgesFnTy<'a>> { ) -> Box<GraphMapEdgesFnTy<'a>> {
// create the structure for common entities if required
let common = {
let mut tmp = CommonEntities::default();
if self.common_products() {
tmp.common_products = common_label_products(current_graph);
}
if self.common_entities() {
tmp.common_entities = common_label_entities(current_graph);
}
if self.common_context() {
tmp.common_context = common_label_context(current_graph);
}
if self.common_union() {
tmp.common_union = common_label_union(current_graph);
}
if self.common_difference() {
tmp.common_difference = common_label_difference(current_graph);
}
if self.common_entities_deleted() {
tmp.common_entities_deleted = common_label_entities_deleted(current_graph);
}
if self.common_entities_added() {
tmp.common_entities_added = common_label_entities_added(current_graph);
}
tmp
};
Box::new( Box::new(
move |i, n| { move |i, n| {
let mut accumulator = String::new(); let mut accumulator = String::new();
for b in &self.base { for b in &self.base {
let f = match_edge_display(b, let f = match_edge_display(b,
Rc::clone(&translator)); Rc::clone(&translator),
common.clone());
accumulator.push_str(&(f)(i, n)); accumulator.push_str(&(f)(i, n));
} }
accumulator accumulator

View File

@ -546,36 +546,6 @@ pub fn bisimilar(
// Output Functions // Output Functions
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// type GraphMapEdgesFnTy<'a> =
// dyn Fn(petgraph::prelude::EdgeIndex, &'a RSlabel) -> String + 'a;
// fn generate_edge_printing_fn<'a>(
// edge_display: graph::EdgeDisplay,
// translator: Rc<Translator>,
// ) -> Box<GraphMapEdgesFnTy<'a>> {
// // The type cannot be aliased since rust doesnt like generics.
// // We are iterating over the edge_display and constructing a function
// // (accumulator) that prints out our formatted nodes. So at each step we
// // call the previous function and add the next string or function.
// let edge_display = edge_display.iter().map(
// |e| {
// match e {
// EdgeDisplay::Display(d) => {
// d.clone()
// },
// EdgeDisplay::Separator(s) => {
// graph::EdgeDisplayBase::String { string: s.clone() }
// }
// }
// }
// ).collect::<Vec<_>>();
// let gmet = graph::GraphMapEdgesTy::from(
// (edge_display, Rc::clone(&translator))
// );
// gmet.generate()
// }
/// Writes the specified graph to a file in .dot format. /// Writes the specified graph to a file in .dot format.
pub fn dot( pub fn dot(
system: &EvaluatedSystem, system: &EvaluatedSystem,