mentedb_graph/
contradiction.rs1use std::collections::VecDeque;
4
5use ahash::HashSet;
6use mentedb_core::edge::EdgeType;
7use mentedb_core::types::MemoryId;
8
9use crate::csr::CsrGraph;
10
11pub fn find_contradictions(graph: &CsrGraph, id: MemoryId) -> Vec<MemoryId> {
14 let mut contradictions = HashSet::default();
15 let mut visited = HashSet::default();
16 let mut queue = VecDeque::new();
17
18 visited.insert(id);
19
20 for (neighbor, edge) in graph.outgoing(id) {
22 if edge.edge_type == EdgeType::Contradicts {
23 contradictions.insert(neighbor);
24 visited.insert(neighbor);
25 }
26 }
27 for (neighbor, edge) in graph.incoming(id) {
29 if edge.edge_type == EdgeType::Contradicts {
30 contradictions.insert(neighbor);
31 visited.insert(neighbor);
32 }
33 }
34
35 queue.push_back(id);
38 while let Some(node) = queue.pop_front() {
39 for (supporter, edge) in graph.incoming(node) {
41 if edge.edge_type == EdgeType::Supports && visited.insert(supporter) {
42 for (target, e2) in graph.outgoing(supporter) {
44 if e2.edge_type == EdgeType::Contradicts && target != id {
45 contradictions.insert(target);
46 }
47 }
48 }
49 }
50 for (supported, edge) in graph.outgoing(node) {
52 if edge.edge_type == EdgeType::Supports && visited.insert(supported) {
53 for (target, e2) in graph.outgoing(supported) {
54 if e2.edge_type == EdgeType::Contradicts {
55 contradictions.insert(target);
56 }
57 }
58 }
59 }
60 }
61
62 contradictions.into_iter().collect()
63}
64
65pub fn find_superseded(graph: &CsrGraph, id: MemoryId) -> Vec<MemoryId> {
67 let mut result = Vec::new();
68 let mut visited = HashSet::default();
69 let mut queue = VecDeque::new();
70
71 visited.insert(id);
72 queue.push_back(id);
73
74 while let Some(node) = queue.pop_front() {
75 for (neighbor, edge) in graph.outgoing(node) {
76 if edge.edge_type == EdgeType::Supersedes && visited.insert(neighbor) {
77 result.push(neighbor);
78 queue.push_back(neighbor);
79 }
80 }
81 }
82
83 result
84}
85
86pub fn detect_cycles(graph: &CsrGraph, edge_types: &[EdgeType]) -> Vec<Vec<MemoryId>> {
89 let filter: HashSet<EdgeType> = edge_types.iter().copied().collect();
90 let mut cycles = Vec::new();
91 let mut globally_visited = HashSet::default();
92
93 for &start_id in graph.node_ids() {
94 if globally_visited.contains(&start_id) {
95 continue;
96 }
97
98 let mut stack: Vec<(MemoryId, Vec<MemoryId>)> = vec![(start_id, vec![start_id])];
100 let mut in_stack = HashSet::default();
101 in_stack.insert(start_id);
102 let mut local_visited = HashSet::default();
103 local_visited.insert(start_id);
104
105 while let Some((node, path)) = stack.pop() {
106 in_stack.clear();
108 for &p in &path {
109 in_stack.insert(p);
110 }
111
112 for (neighbor, edge) in graph.outgoing(node) {
113 if !filter.contains(&edge.edge_type) {
114 continue;
115 }
116
117 if in_stack.contains(&neighbor) {
118 if let Some(pos) = path.iter().position(|&n| n == neighbor) {
120 let cycle: Vec<MemoryId> = path[pos..].to_vec();
121 if !cycles.iter().any(|c: &Vec<MemoryId>| {
123 c.len() == cycle.len() && cycle.iter().all(|n| c.contains(n))
124 }) {
125 cycles.push(cycle);
126 }
127 }
128 } else if local_visited.insert(neighbor) {
129 let mut new_path = path.clone();
130 new_path.push(neighbor);
131 stack.push((neighbor, new_path));
132 }
133 }
134
135 globally_visited.insert(node);
136 }
137 }
138
139 cycles
140}
141
142#[cfg(test)]
143mod tests {
144 use super::*;
145 use mentedb_core::edge::MemoryEdge;
146
147 fn make_edge(src: MemoryId, tgt: MemoryId, etype: EdgeType, weight: f32) -> MemoryEdge {
148 MemoryEdge {
149 source: src,
150 target: tgt,
151 edge_type: etype,
152 weight,
153 created_at: 1000,
154 }
155 }
156
157 #[test]
158 fn test_direct_contradictions() {
159 let mut g = CsrGraph::new();
160 let a = MemoryId::new();
161 let b = MemoryId::new();
162 let c = MemoryId::new();
163
164 g.add_edge(&make_edge(a, b, EdgeType::Contradicts, 1.0));
165 g.add_edge(&make_edge(c, a, EdgeType::Contradicts, 1.0));
166
167 let contras = find_contradictions(&g, a);
168 assert!(contras.contains(&b));
169 assert!(contras.contains(&c));
170 }
171
172 #[test]
173 fn test_transitive_contradictions() {
174 let mut g = CsrGraph::new();
175 let a = MemoryId::new();
176 let b = MemoryId::new();
177 let c = MemoryId::new();
178
179 g.add_edge(&make_edge(b, a, EdgeType::Supports, 1.0));
181 g.add_edge(&make_edge(b, c, EdgeType::Contradicts, 1.0));
182
183 let contras = find_contradictions(&g, a);
184 assert!(contras.contains(&c));
185 }
186
187 #[test]
188 fn test_find_superseded() {
189 let mut g = CsrGraph::new();
190 let a = MemoryId::new();
191 let b = MemoryId::new();
192 let c = MemoryId::new();
193
194 g.add_edge(&make_edge(a, b, EdgeType::Supersedes, 1.0));
195 g.add_edge(&make_edge(b, c, EdgeType::Supersedes, 1.0));
196
197 let superseded = find_superseded(&g, a);
198 assert_eq!(superseded.len(), 2);
199 assert!(superseded.contains(&b));
200 assert!(superseded.contains(&c));
201 }
202
203 #[test]
204 fn test_detect_cycle() {
205 let mut g = CsrGraph::new();
206 let a = MemoryId::new();
207 let b = MemoryId::new();
208 let c = MemoryId::new();
209
210 g.add_edge(&make_edge(a, b, EdgeType::Caused, 1.0));
211 g.add_edge(&make_edge(b, c, EdgeType::Caused, 1.0));
212 g.add_edge(&make_edge(c, a, EdgeType::Caused, 1.0));
213
214 let cycles = detect_cycles(&g, &[EdgeType::Caused]);
215 assert!(!cycles.is_empty());
216 assert_eq!(cycles[0].len(), 3);
217 }
218
219 #[test]
220 fn test_no_cycle() {
221 let mut g = CsrGraph::new();
222 let a = MemoryId::new();
223 let b = MemoryId::new();
224 let c = MemoryId::new();
225
226 g.add_edge(&make_edge(a, b, EdgeType::Caused, 1.0));
227 g.add_edge(&make_edge(b, c, EdgeType::Caused, 1.0));
228
229 let cycles = detect_cycles(&g, &[EdgeType::Caused]);
230 assert!(cycles.is_empty());
231 }
232}