Skip to main content

monist_core/
graph.rs

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/// The GraphArena represents the CPU Geometry Layer in the hybrid pipeline.
113/// It translates the semantic interactions (from the `FormulaArena`) into a weighted directed graph
114/// using De Bruijn indexing and lexical depths, enabling purely structural verification.
115#[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)>, // Added in_comp
120}
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    /// Implement Kosaraju's SCC algorithm to locate and safely collapse 0-weight semantic cycles
153    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        // Map components to the smallest representative variable in that component
190        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        // Update edges to use representatives
199        let mut new_edges = HashSet::new();
200
201        // Ensure that collapsed variables are not orphaned in the SMT matrix.
202        // We mathematically assert their equivalence to the Strongly Cantorian representative.
203        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    /// Continuous daemon that isolates Strongly Cantorian (ZFC-compliant) bedrock
222    /// by scanning for subgraphs satisfying the x = T(x) constraint (topological self-loops)
223    /// and severing their outgoing +1 offset edges to reduce computational load.
224    pub fn isolate_sc_bedrock(&mut self) -> Vec<String> {
225        let mut sc_nodes = HashSet::new();
226
227        // Detect x = T(x) constraints: nodes that have a +1 or -1 weight self-loop.
228        // We strictly enforce that Comprehension boundaries are respected:
229        // if the self-loop is part of a Comprehension (in_comp == true), it is an
230        // unstratifiable paradox and MUST NOT be isolated as Strongly Cantorian bedrock.
231        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            // Only sever connections if they are NOT inside a Comprehension
253            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        // Remove duplicates and return
274        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    /// Executes the Bellman-Ford algorithm to detect negative-weight cycles in the graph.
320    /// In the Monist Engine, a negative-weight cycle corresponds to an "Extensionality Collision" 
321    /// (an unstratifiable paradox or contradiction) in the underlying set theory formulas.
322    pub fn bellman_ford(&mut self) -> Result<(Vec<i32>, Vec<String>), String> {
323        // Run the continuous daemon to dynamically sever outgoing +1 offset edges from SC bedrock
324        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        // Relax edges n-1 times
335        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        // Final pass for negative weight cycles
350        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    /// Extract Minimal Conflict Clauses for Vector Superposition (IDL Masking)
414    /// When Bellman-Ford flags a negative-weight cycle, this identifies the nodes
415    /// involved so the upper ingestion layer can translate them into a hyperdimensional 
416    /// destructive interference mask.
417    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        // Relax edges
423        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        // Detect cycle
434        for &(u, v, w, _) in &self.edges {
435            if d[u] + w < d[v] {
436                // We found a node 'v' in a negative weight cycle
437                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                // Only add if not already present
461                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}