smt_scope/analysis/graph/
visible.rs

1use std::ops::{Index, IndexMut};
2
3use fxhash::FxHashMap;
4use petgraph::{
5    graph::DiGraph,
6    visit::EdgeRef,
7    Direction::{self, Incoming, Outgoing},
8};
9
10use crate::{
11    graph_idx,
12    items::{ENodeIdx, EqGivenIdx},
13    BoxSlice, NonMaxU32,
14};
15
16use super::{
17    analysis::reconnect::{BwdReachableAnalysis, ReachVisible, ReconnectAnalysis},
18    raw::{EdgeKind, NodeKind},
19    InstGraph, RawEdgeIndex, RawNodeIndex,
20};
21
22graph_idx!(visible_idx, VisibleNodeIndex, VisibleEdgeIndex, VisibleIx);
23
24#[derive(Default)]
25pub struct VisibleInstGraph {
26    pub graph: DiGraph<VisibleNode, VisibleEdge, VisibleIx>,
27    reverse: FxHashMap<RawNodeIndex, VisibleNodeIndex>,
28    pub generation: u32,
29}
30
31impl InstGraph {
32    pub fn to_visible(&self) -> VisibleInstGraph {
33        let mut reach = BwdReachableAnalysis::<ReachVisible>::default();
34        let bwd_vis_reachable = self.topo_analysis(&mut reach);
35        let mut reconnect = self.topo_analysis(&mut ReconnectAnalysis(bwd_vis_reachable));
36        let (mut nodes, mut edges) = (0, 0);
37        for (idx, data) in reconnect.iter_mut_enumerated() {
38            if !self.raw[idx].visible() {
39                data.above.clear();
40                continue;
41            }
42            nodes += 1;
43            edges += data.above.len();
44        }
45
46        let mut graph: DiGraph<VisibleNode, VisibleEdge, VisibleIx> =
47            DiGraph::with_capacity(nodes, edges);
48
49        for idx in self.raw.node_indices() {
50            if !self.raw[idx].visible() {
51                continue;
52            }
53            let hidden_parents = self
54                .raw
55                .neighbors_directed(idx, Incoming, &self.analysis.reach)
56                .count_hidden();
57            let hidden_children = self
58                .raw
59                .neighbors_directed(idx, Outgoing, &self.analysis.reach)
60                .count_hidden();
61            let v_node = VisibleNode {
62                idx,
63                hidden_parents: hidden_parents as u32,
64                hidden_children: hidden_children as u32,
65            };
66            graph.add_node(v_node);
67        }
68
69        let reverse: FxHashMap<_, _> = graph
70            .node_indices()
71            .map(VisibleNodeIndex)
72            .map(|idx| (graph[idx.0].idx, idx))
73            .collect();
74        let mut self_ = VisibleInstGraph {
75            graph,
76            reverse,
77            generation: self.raw.stats.generation,
78        };
79
80        for (i_idx, data) in reconnect.into_iter_enumerated() {
81            let Some(v_idx) = self_.reverse(i_idx) else {
82                assert!(data.above.is_empty());
83                continue;
84            };
85            let mut edges_to_add = Vec::new();
86            for (edge, path) in data.above {
87                let from_v = edge.from.visible;
88                let from_h = edge.from.hidden;
89                let v_from_v = self_.reverse(from_v).unwrap();
90                if edge.is_direct_visible() {
91                    assert!(from_h.is_none() && !edge.indirect);
92                    let old_len = edges_to_add.len();
93                    for edge in self.raw.graph.edges_connecting(from_v.0, i_idx.0) {
94                        let edge = RawEdgeIndex(edge.id());
95                        edges_to_add.push((edge, v_from_v, Ok(edge), VisibleEdge::Direct(edge)));
96                    }
97                    // The path is Some if there is only one possible path.
98                    if path.is_some() {
99                        assert!(edges_to_add.len() == old_len + 1);
100                    } else {
101                        assert!(edges_to_add.len() > old_len + 1);
102                    }
103                } else {
104                    assert_ne!(from_v, edge.to_h);
105                    let from = from_h
106                        .map(|fh| RawEdgeIndex(self.raw.graph.find_edge(from_v.0, fh.0).unwrap()));
107                    let to = RawEdgeIndex(self.raw.graph.find_edge(edge.to_h.0, i_idx.0).unwrap());
108                    edges_to_add.push((
109                        to,
110                        v_from_v,
111                        from.ok_or(()),
112                        VisibleEdge::Indirect {
113                            from,
114                            to,
115                            indirect: edge.indirect,
116                            path,
117                        },
118                    ));
119                }
120            }
121            edges_to_add
122                .sort_unstable_by_key(|&(to_edge, from, from_edge, _)| (to_edge, from, from_edge));
123
124            for (_, from, _, edge) in edges_to_add {
125                self_.graph.add_edge(from.0, v_idx.0, edge);
126            }
127        }
128        self_
129    }
130}
131impl VisibleInstGraph {
132    /// Does the hidden instantiation graph contain the given node?
133    pub fn contains(&self, i_idx: RawNodeIndex) -> bool {
134        self.reverse.contains_key(&i_idx)
135    }
136
137    pub(super) fn reverse(&self, i_idx: RawNodeIndex) -> Option<VisibleNodeIndex> {
138        self.reverse.get(&i_idx).copied()
139    }
140}
141
142impl Index<VisibleNodeIndex> for VisibleInstGraph {
143    type Output = VisibleNode;
144    fn index(&self, idx: VisibleNodeIndex) -> &Self::Output {
145        &self.graph[idx.0]
146    }
147}
148impl IndexMut<VisibleNodeIndex> for VisibleInstGraph {
149    fn index_mut(&mut self, idx: VisibleNodeIndex) -> &mut Self::Output {
150        &mut self.graph[idx.0]
151    }
152}
153impl Index<VisibleEdgeIndex> for VisibleInstGraph {
154    type Output = VisibleEdge;
155    fn index(&self, idx: VisibleEdgeIndex) -> &Self::Output {
156        &self.graph[idx.0]
157    }
158}
159impl IndexMut<VisibleEdgeIndex> for VisibleInstGraph {
160    fn index_mut(&mut self, idx: VisibleEdgeIndex) -> &mut Self::Output {
161        &mut self.graph[idx.0]
162    }
163}
164
165#[derive(Debug, Clone)]
166pub struct VisibleNode {
167    pub idx: RawNodeIndex,
168    pub hidden_parents: u32,
169    pub hidden_children: u32,
170}
171
172#[derive(Clone, PartialEq, Eq, Hash)]
173pub enum VisibleEdge {
174    Direct(RawEdgeIndex),
175    Indirect {
176        from: Option<RawEdgeIndex>,
177        to: RawEdgeIndex,
178        indirect: bool,
179        /// None if there are more than one possible paths.
180        path: Option<BoxSlice<RawNodeIndex>>,
181    },
182}
183
184impl std::fmt::Debug for VisibleEdge {
185    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
186        match self {
187            VisibleEdge::Direct(_) => write!(f, "direct edge"),
188            VisibleEdge::Indirect { .. } => write!(f, "indirect edge"),
189        }
190    }
191}
192
193impl VisibleEdge {
194    pub fn last(&self) -> RawEdgeIndex {
195        match *self {
196            VisibleEdge::Direct(e) => e,
197            VisibleEdge::Indirect { to, .. } => to,
198        }
199    }
200
201    pub fn is_indirect(&self) -> bool {
202        matches!(self, VisibleEdge::Indirect { indirect: true, .. })
203    }
204    pub fn kind(&self, graph: &InstGraph) -> VisibleEdgeKind {
205        match *self {
206            VisibleEdge::Direct(e) => VisibleEdgeKind::Direct(e, graph.raw[e]),
207            VisibleEdge::Indirect {
208                from, to, ref path, ..
209            } => {
210                let Some(f) = from else {
211                    return VisibleEdgeKind::Unknown(from, to);
212                };
213                let Some(non_visible_between) = path else {
214                    return VisibleEdgeKind::Unknown(from, to);
215                };
216
217                let get_kind = |n| {
218                    let idx: RawNodeIndex = *non_visible_between.get(n)?;
219                    Some(graph.raw[idx].kind())
220                };
221
222                match (graph.raw[f], graph.raw[to]) {
223                    // Starting at Instantiation
224                    (EdgeKind::Yield, EdgeKind::Blame { pattern_term })
225                        if non_visible_between.len() == 1 =>
226                    {
227                        VisibleEdgeKind::YieldBlame {
228                            enode: get_kind(0).unwrap().enode().unwrap(),
229                            pattern_term,
230                        }
231                    }
232                    (EdgeKind::Yield, EdgeKind::TEqualitySimple { .. })
233                        if non_visible_between.len() == 2 =>
234                    {
235                        VisibleEdgeKind::YieldEq(get_kind(1).unwrap().eq_given().unwrap())
236                    }
237                    (
238                        EdgeKind::Yield,
239                        EdgeKind::BlameEq {
240                            pattern_term,
241                            eq_order,
242                        },
243                    ) if non_visible_between.len() == 3 => {
244                        let trans = graph
245                            .raw
246                            .graph
247                            .edges_directed(non_visible_between[2].0, Direction::Incoming)
248                            .count();
249                        let given_eq = get_kind(1).unwrap().eq_given().unwrap();
250                        if trans == 1 {
251                            VisibleEdgeKind::YieldBlameEq {
252                                given_eq,
253                                pattern_term,
254                                eq_order,
255                            }
256                        } else {
257                            VisibleEdgeKind::YieldEqOther(given_eq)
258                        }
259                    }
260
261                    // Starting at ENode
262                    (EdgeKind::EqualityFact, EdgeKind::TEqualitySimple { .. })
263                        if non_visible_between.len() == 1 =>
264                    {
265                        VisibleEdgeKind::ENodeEq(get_kind(0).unwrap().eq_given().unwrap())
266                    }
267                    (
268                        EdgeKind::EqualityFact,
269                        EdgeKind::BlameEq {
270                            pattern_term,
271                            eq_order,
272                        },
273                    ) if non_visible_between.len() == 2 => {
274                        let trans = graph
275                            .raw
276                            .graph
277                            .edges_directed(non_visible_between[1].0, Direction::Incoming)
278                            .count();
279                        let given_eq = get_kind(0).unwrap().eq_given().unwrap();
280                        if trans == 1 {
281                            VisibleEdgeKind::ENodeBlameEq {
282                                given_eq,
283                                pattern_term,
284                                eq_order,
285                            }
286                        } else {
287                            VisibleEdgeKind::ENodeEqOther(given_eq)
288                        }
289                    }
290
291                    _ => VisibleEdgeKind::Unknown(from, to),
292                }
293            }
294        }
295    }
296}
297
298#[derive(Clone)]
299pub enum VisibleEdgeKind {
300    Direct(RawEdgeIndex, EdgeKind),
301    /// `Instantiation` -> `ENode` -> `Instantiation`
302    YieldBlame {
303        enode: ENodeIdx,
304        pattern_term: u16,
305    },
306    /// `Instantiation` -> `ENode` -> `GivenEquality` -> `TransEquality`
307    YieldEq((EqGivenIdx, Option<NonMaxU32>)),
308    /// `Instantiation` -> `ENode` -> `GivenEquality` ->
309    /// `TransEquality` (only 1 parent) -> `Instantiation`
310    YieldBlameEq {
311        given_eq: (EqGivenIdx, Option<NonMaxU32>),
312        pattern_term: u16,
313        eq_order: u16,
314    },
315    /// `Instantiation` -> `ENode` -> `GivenEquality` -> ...
316    YieldEqOther((EqGivenIdx, Option<NonMaxU32>)),
317
318    /// `ENode` -> `GivenEquality` -> `TransEquality`
319    ENodeEq((EqGivenIdx, Option<NonMaxU32>)),
320    /// `ENode` -> `GivenEquality` -> `TransEquality` -> `Instantiation`
321    ENodeBlameEq {
322        given_eq: (EqGivenIdx, Option<NonMaxU32>),
323        pattern_term: u16,
324        eq_order: u16,
325    },
326    /// `ENode` -> `GivenEquality` -> ...
327    ENodeEqOther((EqGivenIdx, Option<NonMaxU32>)),
328
329    Unknown(Option<RawEdgeIndex>, RawEdgeIndex),
330}
331
332impl VisibleEdgeKind {
333    pub fn blame(&self, graph: &InstGraph) -> NodeKind {
334        use NodeKind::*;
335        match self {
336            VisibleEdgeKind::Direct(edge, _) | VisibleEdgeKind::Unknown(.., edge) => {
337                *graph.raw.graph[graph.raw.graph.edge_endpoints(edge.0).unwrap().0].kind()
338            }
339
340            VisibleEdgeKind::YieldEq(given_eq)
341            | VisibleEdgeKind::YieldBlameEq { given_eq, .. }
342            | VisibleEdgeKind::YieldEqOther(given_eq)
343            | VisibleEdgeKind::ENodeEq(given_eq)
344            | VisibleEdgeKind::ENodeBlameEq { given_eq, .. }
345            | VisibleEdgeKind::ENodeEqOther(given_eq) => GivenEquality(given_eq.0, given_eq.1),
346
347            VisibleEdgeKind::YieldBlame { enode, .. } => ENode(*enode),
348        }
349    }
350}