Skip to main content

nm_tagrewriter/
graph.rs

1use std::collections::BTreeSet;
2
3use derive_more::derive::{AsRef, Deref, From};
4use graph_cycles::Cycles;
5use itertools::Itertools;
6use petgraph::{
7    Graph,
8    algo::dijkstra,
9    dot::Dot,
10    graph::{EdgeIndex, NodeIndex},
11};
12
13use crate::{
14    common::{Arrow, Imply, LabelledArrow, Quiver},
15    state::EffectiveState,
16};
17
18/// A types that holds the data for a graph
19#[derive(Clone)]
20pub struct GraphDataMap {
21    nodes_graph: Vec<NodeIndex>,
22    nodes_object: Vec<EffectiveState>,
23
24    edges_graph: Vec<EdgeIndex>,
25    edges_object: Vec<LabelledArrow>,
26}
27
28impl GraphDataMap {
29    fn get_state_at_index(&self, index: &NodeIndex) -> Option<&EffectiveState> {
30        self.nodes_object
31            .get(self.nodes_graph.iter().position(|i| i == index)?)
32    }
33
34    fn get_edge_at_index(&self, index: &EdgeIndex) -> Option<&LabelledArrow> {
35        self.edges_object
36            .get(self.edges_graph.iter().position(|i| i == index)?)
37    }
38
39    fn get_edges_from_bounds(
40        &self,
41        source: &EffectiveState,
42        target: &EffectiveState,
43    ) -> Vec<&LabelledArrow> {
44        self.edges_object
45            .iter()
46            .filter(|x| ((**x).arrow.source == *source) && ((**x).arrow.target() == *target))
47            .collect_vec()
48    }
49
50    fn get_edges_from_indexes(
51        &self,
52        source: &NodeIndex,
53        target: &NodeIndex,
54    ) -> Vec<&LabelledArrow> {
55        let source_object = self.get_state_at_index(source);
56        let target_object = self.get_state_at_index(target);
57
58        if let (Some(s), Some(t)) = (source_object, target_object) {
59            self.get_edges_from_bounds(s, t)
60        } else {
61            vec![]
62        }
63    }
64}
65
66type InnerGraph = Graph<String, String>;
67/// A wrapper around a graph
68#[derive(Deref, Clone, AsRef, From)]
69pub struct RewriteGraph(InnerGraph);
70
71/// A graph with all its data
72pub struct GraphWithData {
73    pub data: GraphDataMap,
74    pub graph: RewriteGraph,
75}
76
77// TODO: Likely inefficient, too much data is copied
78impl GraphWithData {
79    /// Construct a graph from a quiver of arrows.
80    pub fn from_arrow_list(q: Quiver) -> Self {
81        let arrows = q.clone();
82        let mut arrows_to_visit = arrows.clone();
83        let mut visited_arrows = BTreeSet::new();
84        let mut nodes = BTreeSet::new();
85        let mut edges = BTreeSet::new();
86
87        while let Some(arrow) = arrows_to_visit.pop_first() {
88            let source = arrow.arrow.source.clone();
89            let target = arrow.arrow.target();
90
91            for ar in arrows.iter() {
92                let ar_source = ar.arrow.source.clone();
93                let ar_target = ar.arrow.target();
94
95                if source.implies(&ar_source) && !(source.implies(&ar_target)) {
96                    // x_1 -> x_2 and y_1 -> y2 and y_1 => x_1
97                    // Then the rule x_1 -> x_2 applies to y_1
98
99                    let new_arrow = ar.move_base_point(source.clone());
100                    if visited_arrows.get(&new_arrow).is_none() {
101                        arrows_to_visit.insert(new_arrow);
102                    }
103                }
104                if target.implies(&ar_source) && !(target.implies(&ar_target)) {
105                    let new_arrow = ar.move_base_point(target.clone());
106                    if visited_arrows.get(&new_arrow).is_none() {
107                        arrows_to_visit.insert(new_arrow);
108                    }
109                }
110            }
111            nodes.insert(source.clone());
112            nodes.insert(target.clone());
113            edges.insert((source.clone(), target.clone(), arrow.clone()));
114            visited_arrows.insert(arrow.clone());
115        }
116        let mut graph: Graph<String, String> = Graph::new();
117        let nodes_list = Vec::from_iter(nodes);
118        let edges_list = Vec::from_iter(edges);
119        let nodes_graph_elements: Vec<_> = nodes_list
120            .iter()
121            .map(|s| graph.add_node(s.to_string()))
122            .collect();
123        let edges_graph_elements: Vec<_> = edges_list
124            .iter()
125            .map(|(s, t, a)| {
126                let si = nodes_list.iter().position(|x| x == s).unwrap();
127                let ti = nodes_list.iter().position(|x| x == t).unwrap();
128
129                graph.add_edge(
130                    nodes_graph_elements[si],
131                    nodes_graph_elements[ti],
132                    a.name.clone(),
133                )
134            })
135            .collect();
136
137        let graph_data = GraphDataMap {
138            nodes_graph: nodes_graph_elements,
139            nodes_object: nodes_list,
140            edges_graph: edges_graph_elements,
141            edges_object: edges_list.iter().map(|(_, _, a)| a.clone()).collect(),
142        };
143        GraphWithData {
144            data: graph_data,
145            graph: graph.into(),
146        }
147    }
148
149    /// Get a graphviz representation of this graph
150    pub fn dot<'a>(&'a self) -> Dot<'a, &'a InnerGraph> {
151        let g = self.graph.as_ref();
152        Dot::new(g)
153    }
154
155    pub fn find_cycles<'a>(&'a self) -> Vec<Cycle<'a>> {
156        let raw_cycles = self.graph.as_ref().cycles();
157        let cycles = raw_cycles
158            .iter()
159            .map(|c| Cycle::from_node_indices(self, c.clone()))
160            .collect_vec();
161        cycles
162    }
163
164    pub fn has_cycles(&self) -> bool {
165        self.graph.cycles().iter().len() > 0
166    }
167
168    /// Find nodes with in-degree 0
169    pub fn sources(&self) -> Vec<&NodeIndex> {
170        self.data
171            .nodes_graph
172            .iter()
173            .filter(|n| {
174                self.graph
175                    .neighbors_directed(**n, petgraph::Direction::Incoming)
176                    .next()
177                    .is_none()
178            })
179            .collect_vec()
180    }
181
182    /// Find nodes with out-degree 0
183    pub fn sinks(&self) -> Vec<&NodeIndex> {
184        self.data
185            .nodes_graph
186            .iter()
187            .filter(|n| {
188                self.graph
189                    .neighbors_directed(**n, petgraph::Direction::Outgoing)
190                    .next()
191                    .is_none()
192            })
193            .collect_vec()
194    }
195
196    fn sinks_from_node(&self, node: &NodeIndex) -> Vec<NodeIndex> {
197        let shortest_paths = dijkstra(self.graph.as_ref(), *node, None, |_| 1);
198        shortest_paths
199            .iter()
200            .filter_map(|(i, _)| {
201                if self
202                    .graph
203                    .neighbors_directed(*i, petgraph::Direction::Outgoing)
204                    .next()
205                    .is_none()
206                {
207                    Some(i.clone())
208                } else {
209                    None
210                }
211            })
212            .collect_vec()
213    }
214
215    /// Find extremal paths
216    fn longest_paths(&self) -> Vec<(NodeIndex, NodeIndex)> {
217        let sources = self.sources();
218        sources
219            .iter()
220            .flat_map(|x| {
221                self.sinks_from_node(*x)
222                    .iter()
223                    .map(|y| ((*x).clone(), y.clone()))
224                    .collect_vec()
225            })
226            .collect_vec()
227    }
228
229    /// Find transformations to apply
230    pub fn find_transformations(&self) -> Vec<Arrow> {
231        self.longest_paths()
232            .iter()
233            .map(|(s, t)| {
234                let source = self
235                    .data
236                    .get_state_at_index(s)
237                    .expect("The state should exist.");
238                let target = self
239                    .data
240                    .get_state_at_index(t)
241                    .expect("The state should exist.");
242
243                source.compute_vector(target)
244            })
245            .collect_vec()
246    }
247}
248
249pub struct Cycle<'a> {
250    arrows: Vec<&'a LabelledArrow>,
251    nodes: Vec<&'a EffectiveState>,
252}
253
254impl<'a> Cycle<'a> {
255    fn from_node_indices(g: &'a GraphWithData, cycle: Vec<NodeIndex>) -> Self {
256        let previous_index = cycle.last().expect("Cycles have length at most one.");
257        let mut previous = g
258            .data
259            .get_state_at_index(previous_index)
260            .expect("No user data in input, so this should always succeed.");
261
262        let mut nodes = Vec::new();
263        let mut arrows = Vec::new();
264        for element in cycle {
265            let node = g
266                .data
267                .get_state_at_index(&element)
268                .expect("Should always succeed.");
269            nodes.push(node);
270            let arrows_between = g.data.get_edges_from_bounds(previous, node);
271            arrows.extend(arrows_between);
272            previous = node;
273        }
274
275        Cycle { arrows, nodes }
276    }
277
278    const LONG_CYCLE: usize = 5;
279    const INDENT: &'static str = "  ";
280
281    fn as_string_cycle_node(&self) -> String {
282        let separator = if self.nodes.iter().len() >= Self::LONG_CYCLE {
283            let indent = Self::INDENT;
284            format!("\n{indent}-> ")
285        } else {
286            " -> ".to_string()
287        };
288
289        let inner_string = self.nodes.iter().map(ToString::to_string).join(&separator);
290        format!("Cycle … -> {inner_string} -> …")
291    }
292
293    fn as_string_cycle_arrows(&self) -> String {
294        format!(
295            "Induced by rules: {}",
296            self.arrows.iter().map(|ar| &ar.name).join(", ")
297        )
298    }
299
300    pub fn as_string(&self) -> String {
301        format!(
302            "{}\n{}",
303            self.as_string_cycle_node(),
304            self.as_string_cycle_arrows()
305        )
306    }
307}