qualia_core_db/modalities/
paraconsistent.rs1use crate::{q_hash, NQuin};
2
3pub const OP_ISOLATE: u8 = 0x30;
4pub const OP_CONTRADICTION_SCORE: u8 = 0x31;
5pub const OP_PARACONSISTENT_MERGE: u8 = 0x32;
6
7pub const ISOLATED_CONTEXT_PREFIX: u64 = q_hash("q42:isolated");
8
9#[derive(Debug)]
10pub enum ParaconsistentError {
11 BufferOverflow,
12}
13
14pub enum ContradictionStatus {
15 Consistent,
16 Isolated {
17 severity: u8,
18 isolation_context: u64,
19 },
20}
21
22pub fn route_paraconsistent(
24 quins: &[NQuin],
25 out_consistent: &mut [NQuin],
26 out_isolated: &mut [NQuin],
27) -> Result<(usize, usize), ParaconsistentError> {
28 let mut consistent_count = 0;
29 let mut isolated_count = 0;
30
31 for q in quins {
32 if q.context == ISOLATED_CONTEXT_PREFIX {
35 if consistent_count >= out_consistent.len() {
36 return Err(ParaconsistentError::BufferOverflow);
37 }
38 out_consistent[consistent_count] = *q;
39 consistent_count += 1;
40 continue;
41 }
42
43 let mut is_contradiction = false;
44
45 for i in 0..consistent_count {
47 let prev = &out_consistent[i];
48 if prev.context == q.context
49 && prev.subject == q.subject
50 && prev.predicate == q.predicate
51 && prev.object != q.object
52 {
53 is_contradiction = true;
54 break;
55 }
56 }
57
58 if is_contradiction {
59 if isolated_count >= out_isolated.len() {
60 return Err(ParaconsistentError::BufferOverflow);
61 }
62 let mut isolated_q = *q;
63 isolated_q.context = ISOLATED_CONTEXT_PREFIX ^ q.context;
64 isolated_q.parity =
65 isolated_q.subject ^ isolated_q.predicate ^ isolated_q.object ^ isolated_q.context;
66 out_isolated[isolated_count] = isolated_q;
67 isolated_count += 1;
68 } else {
69 if consistent_count >= out_consistent.len() {
70 return Err(ParaconsistentError::BufferOverflow);
71 }
72 out_consistent[consistent_count] = *q;
73 consistent_count += 1;
74 }
75 }
76
77 Ok((consistent_count, isolated_count))
78}
79
80#[derive(Debug, Clone, Copy, PartialEq, Eq)]
90pub enum Belnap {
91 Neither,
93 True,
95 False,
97 Both,
99}
100
101impl Belnap {
102 #[inline]
104 pub fn from_evidence(told_true: bool, told_false: bool) -> Belnap {
105 match (told_true, told_false) {
106 (false, false) => Belnap::Neither,
107 (true, false) => Belnap::True,
108 (false, true) => Belnap::False,
109 (true, true) => Belnap::Both,
110 }
111 }
112
113 #[inline]
115 pub fn evidence(self) -> (bool, bool) {
116 match self {
117 Belnap::Neither => (false, false),
118 Belnap::True => (true, false),
119 Belnap::False => (false, true),
120 Belnap::Both => (true, true),
121 }
122 }
123
124 #[inline]
126 pub fn is_contradiction(self) -> bool {
127 matches!(self, Belnap::Both)
128 }
129
130 #[inline]
132 pub fn negate(self) -> Belnap {
133 let (t, f) = self.evidence();
134 Belnap::from_evidence(f, t)
135 }
136
137 #[inline]
139 pub fn and(self, other: Belnap) -> Belnap {
140 let (at, af) = self.evidence();
141 let (bt, bf) = other.evidence();
142 Belnap::from_evidence(at && bt, af || bf)
143 }
144
145 #[inline]
147 pub fn or(self, other: Belnap) -> Belnap {
148 let (at, af) = self.evidence();
149 let (bt, bf) = other.evidence();
150 Belnap::from_evidence(at || bt, af && bf)
151 }
152}
153
154pub fn global_saturation(consistent: usize, isolated: usize) -> f32 {
162 let total = consistent + isolated;
163 if total == 0 {
164 0.0
165 } else {
166 isolated as f32 / total as f32
167 }
168}
169
170pub fn local_saturation(quins: &[NQuin], context: u64) -> f32 {
175 let mut in_ctx = 0usize;
176 let mut contradictory = 0usize;
177 for (i, q) in quins.iter().enumerate() {
178 if q.context != context {
179 continue;
180 }
181 in_ctx += 1;
182 let conflicts = quins[..i].iter().any(|p| {
183 p.context == context
184 && p.subject == q.subject
185 && p.predicate == q.predicate
186 && p.object != q.object
187 });
188 if conflicts {
189 contradictory += 1;
190 }
191 }
192 if in_ctx == 0 {
193 0.0
194 } else {
195 contradictory as f32 / in_ctx as f32
196 }
197}
198
199#[inline]
202pub fn is_saturated(saturation: f32, threshold: f32) -> bool {
203 saturation >= threshold
204}
205
206#[cfg(test)]
207mod tests {
208 use super::*;
209
210 #[test]
211 fn belnap_four_valued_tables() {
212 assert_eq!(Belnap::True.negate(), Belnap::False);
214 assert_eq!(Belnap::False.negate(), Belnap::True);
215 assert_eq!(Belnap::Both.negate(), Belnap::Both);
216 assert_eq!(Belnap::Neither.negate(), Belnap::Neither);
217
218 assert_eq!(Belnap::True.and(Belnap::False), Belnap::False);
220 assert_eq!(Belnap::Both.and(Belnap::True), Belnap::Both);
221 assert_eq!(Belnap::Neither.and(Belnap::True), Belnap::Neither);
222 assert_eq!(Belnap::Both.and(Belnap::False), Belnap::False);
223
224 assert_eq!(Belnap::True.or(Belnap::False), Belnap::True);
226 assert_eq!(Belnap::Both.or(Belnap::False), Belnap::Both);
227 assert_eq!(Belnap::Neither.or(Belnap::False), Belnap::Neither);
228
229 assert!(Belnap::Both.is_contradiction());
231 assert_eq!(Belnap::Both.and(Belnap::Both.negate()), Belnap::Both);
232 assert!(!Belnap::True.is_contradiction());
233 }
234
235 #[test]
236 fn test_paraconsistent_routing() {
237 let mut out_c = [NQuin::default(); 10];
238 let mut out_i = [NQuin::default(); 10];
239
240 let q1 = NQuin {
242 subject: 1,
243 predicate: 2,
244 object: 3,
245 context: 42,
246 ..Default::default()
247 };
248 let q2 = NQuin {
249 subject: 1,
250 predicate: 3,
251 object: 3,
252 context: 42,
253 ..Default::default()
254 };
255 let (c, i) = route_paraconsistent(&[q1, q2], &mut out_c, &mut out_i).unwrap();
256 assert_eq!(c, 2);
257 assert_eq!(i, 0);
258
259 let q3 = NQuin {
261 subject: 1,
262 predicate: 2,
263 object: 99,
264 context: 42,
265 ..Default::default()
266 };
267 let (c, i) = route_paraconsistent(&[q1, q3], &mut out_c, &mut out_i).unwrap();
268 assert_eq!(c, 1);
269 assert_eq!(i, 1);
270 assert_eq!(out_i[0].context, ISOLATED_CONTEXT_PREFIX ^ 42);
271
272 let q4 = NQuin {
274 subject: 10,
275 predicate: 20,
276 object: 30,
277 context: 42,
278 ..Default::default()
279 };
280 let (c, i) = route_paraconsistent(&[q1, q3, q4], &mut out_c, &mut out_i).unwrap();
281 assert_eq!(c, 2);
282 assert_eq!(i, 1);
283
284 let mut q_iso = q3;
286 q_iso.context = ISOLATED_CONTEXT_PREFIX; let (c, i) = route_paraconsistent(&[q_iso], &mut out_c, &mut out_i).unwrap();
288 assert_eq!(c, 1);
289 assert_eq!(i, 0);
290 }
291
292 #[test]
293 fn saturation_metrics_local_and_global() {
294 assert!((global_saturation(3, 1) - 0.25).abs() < 1e-6);
296 assert_eq!(global_saturation(0, 0), 0.0);
297 assert_eq!(global_saturation(0, 5), 1.0);
298
299 let ctx = 42;
301 let q1 = NQuin {
302 subject: 1,
303 predicate: 2,
304 object: 3,
305 context: ctx,
306 ..Default::default()
307 };
308 let q2 = NQuin {
309 subject: 1,
310 predicate: 2,
311 object: 99,
312 context: ctx,
313 ..Default::default()
314 }; let q3 = NQuin {
316 subject: 5,
317 predicate: 6,
318 object: 7,
319 context: ctx,
320 ..Default::default()
321 };
322 let other = NQuin {
323 subject: 1,
324 predicate: 2,
325 object: 8,
326 context: 7,
327 ..Default::default()
328 }; let s = local_saturation(&[q1, q2, q3, other], ctx);
330 assert!(
331 (s - (1.0 / 3.0)).abs() < 1e-6,
332 "1 of 3 in-context quins is contradictory"
333 );
334
335 assert_eq!(local_saturation(&[q1, q3], ctx), 0.0);
337 assert!(is_saturated(0.6, 0.5));
338 assert!(!is_saturated(0.4, 0.5));
339 assert_eq!(local_saturation(&[], ctx), 0.0);
341 }
342}