1use crate::ast::{Atomic, Formula, FormulaArena, Var};
2use std::collections::{HashMap, HashSet};
3
4#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
5pub struct ScopedVar(pub Var, pub usize);
6
7#[derive(Debug, Clone, PartialEq, Eq, Hash)]
8pub struct Constraint {
9 pub v1: ScopedVar,
10 pub v2: ScopedVar,
11 pub weight: i32,
12 pub in_comp: bool,
13}
14
15#[derive(Debug, Clone, PartialEq, Eq, Hash)]
16pub struct Edge {
17 pub source: ScopedVar,
18 pub target: ScopedVar,
19 pub weight: i32,
20 pub in_comp: bool,
21}
22
23impl From<Constraint> for Edge {
24 fn from(c: Constraint) -> Self {
25 Edge {
26 source: c.v1,
27 target: c.v2,
28 weight: c.weight,
29 in_comp: c.in_comp,
30 }
31 }
32}
33
34pub fn extract_constraints_aux(
35 arena: &FormulaArena,
36 formula_idx: usize,
37 depth: usize,
38 in_comp: bool,
39) -> Vec<Constraint> {
40 let mut constraints = Vec::new();
41
42 let formula = match arena.get(formula_idx) {
43 Some(f) => f,
44 None => return constraints,
45 };
46
47 match formula {
48 Formula::Atom(atomic) => match atomic {
49 Atomic::Eq(x, y) => {
50 let sx = ScopedVar(x.clone(), depth);
51 let sy = ScopedVar(y.clone(), depth);
52 constraints.push(Constraint {
53 v1: sx.clone(),
54 v2: sy.clone(),
55 weight: 0,
56 in_comp,
57 });
58 constraints.push(Constraint {
59 v1: sy,
60 v2: sx,
61 weight: 0,
62 in_comp,
63 });
64 }
65 Atomic::Mem(x, y) => {
66 let sx = ScopedVar(x.clone(), depth);
67 let sy = ScopedVar(y.clone(), depth);
68 constraints.push(Constraint {
69 v1: sx.clone(),
70 v2: sy.clone(),
71 weight: 1,
72 in_comp,
73 });
74 constraints.push(Constraint {
75 v1: sy,
76 v2: sx,
77 weight: -1,
78 in_comp,
79 });
80 }
81 Atomic::Lt(x, y) => {
82 let sx = ScopedVar(x.clone(), depth);
83 let sy = ScopedVar(y.clone(), depth);
84 constraints.push(Constraint {
85 v1: sy.clone(),
86 v2: sx.clone(),
87 weight: -1,
88 in_comp,
89 });
90 }
91 _ => {}
92 },
93 Formula::Neg(f_idx) => {
94 constraints.extend(extract_constraints_aux(arena, *f_idx, depth, in_comp));
95 }
96 Formula::Conj(f1_idx, f2_idx)
97 | Formula::Disj(f1_idx, f2_idx)
98 | Formula::Impl(f1_idx, f2_idx) => {
99 constraints.extend(extract_constraints_aux(arena, *f1_idx, depth, in_comp));
100 constraints.extend(extract_constraints_aux(arena, *f2_idx, depth, in_comp));
101 }
102 Formula::Univ(_, _, f_idx) | Formula::Exist(_, _, f_idx) => {
103 constraints.extend(extract_constraints_aux(arena, *f_idx, depth + 1, in_comp));
104 }
105 Formula::Comp(_, _, f_idx) => {
106 constraints.extend(extract_constraints_aux(arena, *f_idx, depth + 1, true));
107 }
108 }
109 constraints
110}
111
112#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
116pub struct GraphArena {
117 pub vars: Vec<ScopedVar>,
118 pub var_to_idx: HashMap<ScopedVar, usize>,
119 pub edges: Vec<(usize, usize, i32, bool)>, }
121
122impl GraphArena {
123 pub fn new() -> Self {
124 Self {
125 vars: Vec::new(),
126 var_to_idx: HashMap::new(),
127 edges: Vec::new(),
128 }
129 }
130
131 pub fn add_var(&mut self, var: ScopedVar) -> usize {
132 if let Some(&idx) = self.var_to_idx.get(&var) {
133 idx
134 } else {
135 let idx = self.vars.len();
136 self.vars.push(var.clone());
137 self.var_to_idx.insert(var, idx);
138 idx
139 }
140 }
141
142 pub fn from_constraints(constraints: &[Constraint]) -> Self {
143 let mut arena = Self::new();
144 for c in constraints {
145 let u = arena.add_var(c.v1.clone());
146 let v = arena.add_var(c.v2.clone());
147 arena.edges.push((u, v, c.weight, c.in_comp));
148 }
149 arena
150 }
151
152 pub fn collapse_scc_0_weight(&mut self) {
154 let n = self.vars.len();
155 if n == 0 {
156 return;
157 }
158
159 let mut adj = vec![Vec::new(); n];
160 let mut rev_adj = vec![Vec::new(); n];
161
162 for &(u, v, w, _) in &self.edges {
163 if w == 0 {
164 adj[u].push(v);
165 rev_adj[v].push(u);
166 }
167 }
168
169 let mut visited = vec![false; n];
170 let mut order = Vec::new();
171
172 for i in 0..n {
173 if !visited[i] {
174 self.dfs1(i, &adj, &mut visited, &mut order);
175 }
176 }
177
178 visited.fill(false);
179 let mut component = vec![0; n];
180 let mut scc_count = 0;
181
182 for &i in order.iter().rev() {
183 if !visited[i] {
184 self.dfs2(i, &rev_adj, &mut visited, &mut component, scc_count);
185 scc_count += 1;
186 }
187 }
188
189 let mut reps = vec![n; scc_count];
191 for i in 0..n {
192 let comp = component[i];
193 if i < reps[comp] {
194 reps[comp] = i;
195 }
196 }
197
198 let mut new_edges = HashSet::new();
200
201 for i in 0..n {
204 let rep = reps[component[i]];
205 if i != rep {
206 new_edges.insert((rep, i, 0, false));
207 new_edges.insert((i, rep, 0, false));
208 }
209 }
210
211 for &(u, v, w, in_comp) in &self.edges {
212 let rep_u = reps[component[u]];
213 let rep_v = reps[component[v]];
214 if rep_u != rep_v || w != 0 {
215 new_edges.insert((rep_u, rep_v, w, in_comp));
216 }
217 }
218 self.edges = new_edges.into_iter().collect();
219 }
220
221 pub fn isolate_sc_bedrock(&mut self) -> Vec<String> {
225 let mut sc_nodes = HashSet::new();
226
227 for &(u, v, w, in_comp) in &self.edges {
232 if u == v && w != 0 && !in_comp {
233 sc_nodes.insert(u);
234 }
235 }
236
237 if sc_nodes.is_empty() {
238 return Vec::new();
239 }
240
241 let mut actions = Vec::new();
242 let mut new_edges = HashSet::new();
243
244 for &(u, v, w, in_comp) in &self.edges {
245 if u == v && w != 0 && !in_comp {
246 actions.push(format!(
247 "Neutralized SC defining self-loop on {}",
248 self.var_name(u)
249 ));
250 continue;
251 }
252 if sc_nodes.contains(&u) && w == 1 && !in_comp {
254 actions.push(format!(
255 "Severed outgoing +1 offset edge from SC bedrock node {} to {}",
256 self.var_name(u),
257 self.var_name(v)
258 ));
259 continue;
260 }
261 if sc_nodes.contains(&v) && w == -1 && !in_comp {
262 actions.push(format!(
263 "Severed incoming -1 offset edge to SC bedrock node {} from {}",
264 self.var_name(v),
265 self.var_name(u)
266 ));
267 continue;
268 }
269 new_edges.insert((u, v, w, in_comp));
270 }
271 self.edges = new_edges.into_iter().collect();
272
273 let mut unique_actions: Vec<String> = actions
275 .into_iter()
276 .collect::<HashSet<_>>()
277 .into_iter()
278 .collect();
279 unique_actions.sort();
280 unique_actions
281 }
282
283 fn var_name(&self, u: usize) -> String {
284 let var = &self.vars[u];
285 let name = match &var.0 {
286 crate::ast::Var::Free(n) => n.clone(),
287 crate::ast::Var::Bound(idx) => format!("b{}", idx),
288 };
289 format!("{}_{}", name, var.1)
290 }
291
292 fn dfs1(&self, u: usize, adj: &[Vec<usize>], visited: &mut [bool], order: &mut Vec<usize>) {
293 visited[u] = true;
294 for &v in &adj[u] {
295 if !visited[v] {
296 self.dfs1(v, adj, visited, order);
297 }
298 }
299 order.push(u);
300 }
301
302 fn dfs2(
303 &self,
304 u: usize,
305 rev_adj: &[Vec<usize>],
306 visited: &mut [bool],
307 component: &mut [usize],
308 comp_id: usize,
309 ) {
310 visited[u] = true;
311 component[u] = comp_id;
312 for &v in &rev_adj[u] {
313 if !visited[v] {
314 self.dfs2(v, rev_adj, visited, component, comp_id);
315 }
316 }
317 }
318
319 pub fn bellman_ford(&mut self) -> Result<(Vec<i32>, Vec<String>), String> {
323 let sc_actions = self.isolate_sc_bedrock();
325
326 let n = self.vars.len();
327 if n == 0 {
328 return Ok((Vec::new(), sc_actions));
329 }
330
331 let mut d = vec![0; n];
332 let mut p: Vec<Option<(usize, i32)>> = vec![None; n];
333
334 for _ in 0..n {
336 let mut changed = false;
337 for &(u, v, w, _) in &self.edges {
338 if d[u] + w < d[v] {
339 d[v] = d[u] + w;
340 p[v] = Some((u, w));
341 changed = true;
342 }
343 }
344 if !changed {
345 break;
346 }
347 }
348
349 let mut collision_vertex = None;
351 for &(u, v, w, _) in &self.edges {
352 if d[u] + w < d[v] {
353 collision_vertex = Some(v);
354 p[v] = Some((u, w));
355 break;
356 }
357 }
358
359 if let Some(mut curr) = collision_vertex {
360 for _ in 0..n {
361 curr = p[curr].unwrap().0;
362 }
363
364 let cycle_start = curr;
365 let mut cycle = Vec::new();
366
367 loop {
368 let (prev, w) = p[curr].unwrap();
369 cycle.push((prev, curr, w));
370 curr = prev;
371 if curr == cycle_start {
372 break;
373 }
374 }
375
376 cycle.reverse();
377
378 let mut result = String::new();
379 result.push_str("Extensionality Collision: Negative-weight cycle detected!\n");
380 result.push_str("Summation: ");
381
382 let mut sum_str = Vec::new();
383 let mut total_weight = 0;
384 for (u, v, w) in &cycle {
385 let u_var = &self.vars[*u];
386 let v_var = &self.vars[*v];
387
388 let u_name = match &u_var.0 {
389 crate::ast::Var::Free(name) => name.clone(),
390 crate::ast::Var::Bound(idx) => format!("b{}", idx),
391 };
392 let v_name = match &v_var.0 {
393 crate::ast::Var::Free(name) => name.clone(),
394 crate::ast::Var::Bound(idx) => format!("b{}", idx),
395 };
396
397 let u_str = format!("{}_{}", u_name, u_var.1);
398 let v_str = format!("{}_{}", v_name, v_var.1);
399
400 sum_str.push(format!("{} -> {} ({})", u_str, v_str, w));
401 total_weight += w;
402 }
403
404 result.push_str(&sum_str.join(" + "));
405 result.push_str(&format!(" = {}", total_weight));
406
407 return Err(result);
408 }
409
410 Ok((d, sc_actions))
411 }
412
413 pub fn extract_conflict_clauses(&mut self) -> Vec<Vec<usize>> {
418 let n = self.vars.len();
419 let mut d = vec![0; n];
420 let mut p: Vec<Option<(usize, i32)>> = vec![None; n];
421
422 for _ in 0..n {
424 for &(u, v, w, _) in &self.edges {
425 if d[u] + w < d[v] {
426 d[v] = d[u] + w;
427 p[v] = Some((u, w));
428 }
429 }
430 }
431
432 let mut conflict_clauses = Vec::new();
433 for &(u, v, w, _) in &self.edges {
435 if d[u] + w < d[v] {
436 let mut curr = v;
438 for _ in 0..n {
439 if let Some((prev, _)) = p[curr] {
440 curr = prev;
441 }
442 }
443
444 let cycle_start = curr;
445 let mut cycle = Vec::new();
446
447 loop {
448 if let Some((prev, _)) = p[curr] {
449 cycle.push(curr);
450 curr = prev;
451 if curr == cycle_start {
452 break;
453 }
454 } else {
455 break;
456 }
457 }
458 cycle.reverse();
459
460 let mut sorted_cycle = cycle.clone();
462 sorted_cycle.sort();
463
464 let is_duplicate = conflict_clauses.iter().any(|c: &Vec<usize>| {
465 let mut sc = c.clone();
466 sc.sort();
467 sc == sorted_cycle
468 });
469
470 if !is_duplicate {
471 conflict_clauses.push(cycle);
472 }
473 }
474 }
475
476 conflict_clauses
477 }
478}