use std::collections::BTreeSet;
use derive_more::derive::{AsRef, Deref, From};
use graph_cycles::Cycles;
use itertools::Itertools;
use petgraph::{
Graph,
algo::dijkstra,
dot::Dot,
graph::{EdgeIndex, NodeIndex},
};
use crate::{
common::{Arrow, Imply, LabelledArrow, Quiver},
state::EffectiveState,
};
#[derive(Clone)]
pub struct GraphDataMap {
nodes_graph: Vec<NodeIndex>,
nodes_object: Vec<EffectiveState>,
edges_graph: Vec<EdgeIndex>,
edges_object: Vec<LabelledArrow>,
}
impl GraphDataMap {
fn get_state_at_index(&self, index: &NodeIndex) -> Option<&EffectiveState> {
self.nodes_object
.get(self.nodes_graph.iter().position(|i| i == index)?)
}
fn get_edge_at_index(&self, index: &EdgeIndex) -> Option<&LabelledArrow> {
self.edges_object
.get(self.edges_graph.iter().position(|i| i == index)?)
}
fn get_edges_from_bounds(
&self,
source: &EffectiveState,
target: &EffectiveState,
) -> Vec<&LabelledArrow> {
self.edges_object
.iter()
.filter(|x| ((**x).arrow.source == *source) && ((**x).arrow.target() == *target))
.collect_vec()
}
fn get_edges_from_indexes(
&self,
source: &NodeIndex,
target: &NodeIndex,
) -> Vec<&LabelledArrow> {
let source_object = self.get_state_at_index(source);
let target_object = self.get_state_at_index(target);
if let (Some(s), Some(t)) = (source_object, target_object) {
self.get_edges_from_bounds(s, t)
} else {
vec![]
}
}
}
type InnerGraph = Graph<String, String>;
#[derive(Deref, Clone, AsRef, From)]
pub struct RewriteGraph(InnerGraph);
pub struct GraphWithData {
pub data: GraphDataMap,
pub graph: RewriteGraph,
}
impl GraphWithData {
pub fn from_arrow_list(q: Quiver) -> Self {
let arrows = q.clone();
let mut arrows_to_visit = arrows.clone();
let mut visited_arrows = BTreeSet::new();
let mut nodes = BTreeSet::new();
let mut edges = BTreeSet::new();
while let Some(arrow) = arrows_to_visit.pop_first() {
let source = arrow.arrow.source.clone();
let target = arrow.arrow.target();
for ar in arrows.iter() {
let ar_source = ar.arrow.source.clone();
let ar_target = ar.arrow.target();
if source.implies(&ar_source) && !(source.implies(&ar_target)) {
let new_arrow = ar.move_base_point(source.clone());
if visited_arrows.get(&new_arrow).is_none() {
arrows_to_visit.insert(new_arrow);
}
}
if target.implies(&ar_source) && !(target.implies(&ar_target)) {
let new_arrow = ar.move_base_point(target.clone());
if visited_arrows.get(&new_arrow).is_none() {
arrows_to_visit.insert(new_arrow);
}
}
}
nodes.insert(source.clone());
nodes.insert(target.clone());
edges.insert((source.clone(), target.clone(), arrow.clone()));
visited_arrows.insert(arrow.clone());
}
let mut graph: Graph<String, String> = Graph::new();
let nodes_list = Vec::from_iter(nodes);
let edges_list = Vec::from_iter(edges);
let nodes_graph_elements: Vec<_> = nodes_list
.iter()
.map(|s| graph.add_node(s.to_string()))
.collect();
let edges_graph_elements: Vec<_> = edges_list
.iter()
.map(|(s, t, a)| {
let si = nodes_list.iter().position(|x| x == s).unwrap();
let ti = nodes_list.iter().position(|x| x == t).unwrap();
graph.add_edge(
nodes_graph_elements[si],
nodes_graph_elements[ti],
a.name.clone(),
)
})
.collect();
let graph_data = GraphDataMap {
nodes_graph: nodes_graph_elements,
nodes_object: nodes_list,
edges_graph: edges_graph_elements,
edges_object: edges_list.iter().map(|(_, _, a)| a.clone()).collect(),
};
GraphWithData {
data: graph_data,
graph: graph.into(),
}
}
pub fn dot<'a>(&'a self) -> Dot<'a, &'a InnerGraph> {
let g = self.graph.as_ref();
Dot::new(g)
}
pub fn find_cycles<'a>(&'a self) -> Vec<Cycle<'a>> {
let raw_cycles = self.graph.as_ref().cycles();
let cycles = raw_cycles
.iter()
.map(|c| Cycle::from_node_indices(self, c.clone()))
.collect_vec();
cycles
}
pub fn has_cycles(&self) -> bool {
self.graph.cycles().iter().len() > 0
}
pub fn sources(&self) -> Vec<&NodeIndex> {
self.data
.nodes_graph
.iter()
.filter(|n| {
self.graph
.neighbors_directed(**n, petgraph::Direction::Incoming)
.next()
.is_none()
})
.collect_vec()
}
pub fn sinks(&self) -> Vec<&NodeIndex> {
self.data
.nodes_graph
.iter()
.filter(|n| {
self.graph
.neighbors_directed(**n, petgraph::Direction::Outgoing)
.next()
.is_none()
})
.collect_vec()
}
fn sinks_from_node(&self, node: &NodeIndex) -> Vec<NodeIndex> {
let shortest_paths = dijkstra(self.graph.as_ref(), *node, None, |_| 1);
shortest_paths
.iter()
.filter_map(|(i, _)| {
if self
.graph
.neighbors_directed(*i, petgraph::Direction::Outgoing)
.next()
.is_none()
{
Some(i.clone())
} else {
None
}
})
.collect_vec()
}
fn longest_paths(&self) -> Vec<(NodeIndex, NodeIndex)> {
let sources = self.sources();
sources
.iter()
.flat_map(|x| {
self.sinks_from_node(*x)
.iter()
.map(|y| ((*x).clone(), y.clone()))
.collect_vec()
})
.collect_vec()
}
pub fn find_transformations(&self) -> Vec<Arrow> {
self.longest_paths()
.iter()
.map(|(s, t)| {
let source = self
.data
.get_state_at_index(s)
.expect("The state should exist.");
let target = self
.data
.get_state_at_index(t)
.expect("The state should exist.");
source.compute_vector(target)
})
.collect_vec()
}
}
pub struct Cycle<'a> {
arrows: Vec<&'a LabelledArrow>,
nodes: Vec<&'a EffectiveState>,
}
impl<'a> Cycle<'a> {
fn from_node_indices(g: &'a GraphWithData, cycle: Vec<NodeIndex>) -> Self {
let previous_index = cycle.last().expect("Cycles have length at most one.");
let mut previous = g
.data
.get_state_at_index(previous_index)
.expect("No user data in input, so this should always succeed.");
let mut nodes = Vec::new();
let mut arrows = Vec::new();
for element in cycle {
let node = g
.data
.get_state_at_index(&element)
.expect("Should always succeed.");
nodes.push(node);
let arrows_between = g.data.get_edges_from_bounds(previous, node);
arrows.extend(arrows_between);
previous = node;
}
Cycle { arrows, nodes }
}
const LONG_CYCLE: usize = 5;
const INDENT: &'static str = " ";
fn as_string_cycle_node(&self) -> String {
let separator = if self.nodes.iter().len() >= Self::LONG_CYCLE {
let indent = Self::INDENT;
format!("\n{indent}-> ")
} else {
" -> ".to_string()
};
let inner_string = self.nodes.iter().map(ToString::to_string).join(&separator);
format!("Cycle … -> {inner_string} -> …")
}
fn as_string_cycle_arrows(&self) -> String {
format!(
"Induced by rules: {}",
self.arrows.iter().map(|ar| &ar.name).join(", ")
)
}
pub fn as_string(&self) -> String {
format!(
"{}\n{}",
self.as_string_cycle_node(),
self.as_string_cycle_arrows()
)
}
}