qualia_core_db/modalities/
epistemic.rs1use crate::NQuin;
2
3pub const OP_KNOWS: u8 = 0x20;
4pub const OP_BELIEVES: u8 = 0x21;
5pub const OP_COMMON_KNOWLEDGE: u8 = 0x22;
6pub const OP_INTENT_LOCK: u8 = 0x23;
7pub const OP_IS_LOCKED: u8 = 0x24;
8pub const OP_NAMESPACE_LOCK: u8 = 0x25;
9pub const OP_NAMESPACE_IS_LOCKED: u8 = 0x26;
10
11pub const CERTAINTY_BIT_SHIFT: u32 = 8;
12pub const NESTING_BIT_SHIFT: u32 = 16;
13
14pub const CERTAINTY_KNOWS: u8 = 255;
21pub const CERTAINTY_AFFIRMS: u8 = 230;
22pub const CERTAINTY_BELIEVES: u8 = 200;
23pub const CERTAINTY_RECOGNIZES: u8 = 200;
24pub const CERTAINTY_CONSIDERS: u8 = 128;
25pub const CERTAINTY_SUPPOSES: u8 = 100;
26pub const CERTAINTY_SUSPECTS: u8 = 80;
27pub const CERTAINTY_SPECULATES: u8 = 50;
28pub const CERTAINTY_DOUBTS: u8 = 20;
29
30#[derive(Debug, PartialEq, Clone, Copy)]
31pub enum EpistemicStatus {
32 Active,
33 Uncertain,
34 Skipped,
35}
36
37#[derive(Debug)]
38pub enum EpistemicError {
39 BufferOverflow,
40 NodeLocked(u64), NamespaceLocked(u64), }
43
44#[derive(Debug, Clone, Copy)]
45pub struct EpistemicVerdict {
46 pub claim: NQuin,
47 pub status: EpistemicStatus,
48 pub certainty: u8,
49}
50
51pub fn evaluate_epistemic_frame(
53 quins: &[NQuin],
54 agent_did_hash: u64, world_hash: u64, out: &mut [EpistemicVerdict],
57) -> Result<usize, EpistemicError> {
58 let mut count = 0;
59
60 for q in quins {
61 if world_hash != 0 && q.context != world_hash {
62 continue;
63 }
64 if agent_did_hash != 0 && q.subject != agent_did_hash {
65 continue;
66 }
67
68 let opcode = (q.predicate & 0xFF) as u8;
69 if opcode != OP_KNOWS && opcode != OP_BELIEVES && opcode != OP_COMMON_KNOWLEDGE {
70 continue;
71 }
72
73 let certainty = ((q.predicate >> CERTAINTY_BIT_SHIFT) & 0xFF) as u8;
74
75 let status = if certainty >= 128 || opcode == OP_KNOWS || opcode == OP_COMMON_KNOWLEDGE {
76 EpistemicStatus::Active
77 } else {
78 EpistemicStatus::Uncertain
79 };
80
81 if count >= out.len() {
82 return Err(EpistemicError::BufferOverflow);
83 }
84
85 out[count] = EpistemicVerdict {
86 claim: *q,
87 status,
88 certainty,
89 };
90 count += 1;
91 }
92
93 Ok(count)
94}
95
96pub fn check_node_locks(
98 intent_quins: &[NQuin],
99 current_graph: &[NQuin],
100 agent_did_hash: u64,
101) -> Result<(), EpistemicError> {
102 for i_quin in intent_quins {
103 let opcode = (i_quin.predicate & 0xFF) as u8;
104
105 if opcode == OP_NAMESPACE_LOCK {
107 let target_namespace = i_quin.object;
108 for c_quin in current_graph {
110 let c_opcode = (c_quin.predicate & 0xFF) as u8;
111 if c_opcode == OP_NAMESPACE_IS_LOCKED && c_quin.object == target_namespace {
112 if c_quin.subject != agent_did_hash {
113 return Err(EpistemicError::NamespaceLocked(target_namespace));
114 }
115 }
116 }
117 }
118
119 if opcode == OP_INTENT_LOCK {
121 let target_node = i_quin.object;
122 let target_namespace = i_quin.context; for c_quin in current_graph {
125 let c_opcode = (c_quin.predicate & 0xFF) as u8;
126
127 if c_opcode == OP_NAMESPACE_IS_LOCKED && c_quin.object == target_namespace {
129 if c_quin.subject != agent_did_hash {
130 return Err(EpistemicError::NamespaceLocked(target_namespace));
131 }
132 }
133
134 if c_opcode == OP_IS_LOCKED && c_quin.object == target_node {
136 if c_quin.subject != agent_did_hash {
137 return Err(EpistemicError::NodeLocked(target_node));
138 }
139 }
140 }
141 }
142 }
143 Ok(())
144}
145
146pub use crate::modalities::modal::{
151 contract as agm_contract, expand as agm_expand, is_consistent as belief_set_consistent,
152 revise as agm_revise, Belief,
153};
154
155pub fn everyone_knows(agent_knows: &[bool]) -> bool {
157 !agent_knows.is_empty() && agent_knows.iter().all(|&k| k)
158}
159
160pub fn distributed_knowledge(required: &[u64], known: &[u64]) -> bool {
164 !required.is_empty() && required.iter().all(|r| known.contains(r))
165}
166
167#[inline]
171pub fn common_knowledge_via_announcement(everyone_perceived: bool) -> bool {
172 everyone_perceived
173}
174
175#[inline]
177pub fn positive_introspection(knows_p: bool) -> bool {
178 knows_p
179}
180
181#[inline]
183pub fn negative_introspection(knows_p: bool) -> bool {
184 !knows_p
185}
186
187pub fn muddy_child_knows(num_muddy: u32, round: u32) -> bool {
192 num_muddy > 0 && round >= num_muddy
193}
194
195#[cfg(test)]
196mod tests {
197 use super::*;
198 use crate::q_hash;
199
200 #[test]
201 fn test_epistemic_evaluation() {
202 let mut out = Vec::with_capacity(10);
203 for _ in 0..10 {
204 out.push(EpistemicVerdict {
205 claim: NQuin::default(),
206 status: EpistemicStatus::Skipped,
207 certainty: 0,
208 });
209 }
210
211 let agent_a = q_hash("agent_a");
212 let agent_b = q_hash("agent_b");
213 let world_w = q_hash("world_w");
214
215 let mut q_knows = NQuin::default();
216 q_knows.subject = agent_a;
217 q_knows.predicate = (200u64 << CERTAINTY_BIT_SHIFT) | (OP_KNOWS as u64);
218 q_knows.context = world_w;
219
220 let mut q_believes_low = NQuin::default();
221 q_believes_low.subject = agent_a;
222 q_believes_low.predicate = (50u64 << CERTAINTY_BIT_SHIFT) | (OP_BELIEVES as u64);
223 q_believes_low.context = world_w;
224
225 let mut q_wrong_world = NQuin::default();
226 q_wrong_world.subject = agent_a;
227 q_wrong_world.predicate = (200u64 << CERTAINTY_BIT_SHIFT) | (OP_KNOWS as u64);
228 q_wrong_world.context = q_hash("other_world");
229
230 let quins = [q_knows, q_believes_low, q_wrong_world];
231
232 let count = evaluate_epistemic_frame(&quins, agent_a, world_w, &mut out).unwrap();
234 assert_eq!(count, 2);
235 assert_eq!(out[0].status, EpistemicStatus::Active); assert_eq!(out[1].status, EpistemicStatus::Uncertain); let mut out2 = Vec::with_capacity(10);
240 for _ in 0..10 {
241 out2.push(EpistemicVerdict {
242 claim: NQuin::default(),
243 status: EpistemicStatus::Skipped,
244 certainty: 0,
245 });
246 }
247 let count2 = evaluate_epistemic_frame(&quins, 0, q_hash("other_world"), &mut out2).unwrap();
248 assert_eq!(count2, 1);
249
250 let count3 = evaluate_epistemic_frame(&quins, agent_b, 0, &mut out2).unwrap();
252 assert_eq!(count3, 0);
253
254 let count4 = evaluate_epistemic_frame(&[], 0, 0, &mut out2).unwrap();
256 assert_eq!(count4, 0);
257 }
258
259 #[test]
260 fn epistemic_strength_bands_map_to_active_or_uncertain() {
261 let agent = q_hash("agent");
265 let world = q_hash("world");
266 let mk = |band: u8| {
267 let mut q = NQuin::default();
268 q.subject = agent;
269 q.predicate = ((band as u64) << CERTAINTY_BIT_SHIFT) | (OP_BELIEVES as u64);
270 q.context = world;
271 q
272 };
273 let mut out = vec![
274 EpistemicVerdict {
275 claim: NQuin::default(),
276 status: EpistemicStatus::Skipped,
277 certainty: 0,
278 };
279 4
280 ];
281
282 for b in [CERTAINTY_KNOWS, CERTAINTY_BELIEVES, CERTAINTY_CONSIDERS] {
283 evaluate_epistemic_frame(&[mk(b)], agent, world, &mut out).unwrap();
284 assert_eq!(
285 out[0].status,
286 EpistemicStatus::Active,
287 "band {b} should be Active"
288 );
289 }
290 for b in [CERTAINTY_SUPPOSES, CERTAINTY_SPECULATES, CERTAINTY_DOUBTS] {
291 evaluate_epistemic_frame(&[mk(b)], agent, world, &mut out).unwrap();
292 assert_eq!(
293 out[0].status,
294 EpistemicStatus::Uncertain,
295 "band {b} should be Uncertain"
296 );
297 }
298 }
299
300 #[test]
301 fn test_namespace_lock_blocks_node_lock() {
302 let agent_a = q_hash("agent_a");
303 let agent_b = q_hash("agent_b");
304 let target_namespace = q_hash("specialized_libs/");
305 let target_node = q_hash("specialized_libs/file.rs");
306
307 let mut namespace_locked = NQuin::default();
309 namespace_locked.subject = agent_a;
310 namespace_locked.predicate = OP_NAMESPACE_IS_LOCKED as u64;
311 namespace_locked.object = target_namespace;
312
313 let current_graph = vec![namespace_locked];
314
315 let mut intent_node_lock = NQuin::default();
317 intent_node_lock.subject = agent_b;
318 intent_node_lock.predicate = OP_INTENT_LOCK as u64;
319 intent_node_lock.object = target_node;
320 intent_node_lock.context = target_namespace;
321
322 let result = check_node_locks(&[intent_node_lock], ¤t_graph, agent_b);
323 assert!(
324 matches!(result, Err(EpistemicError::NamespaceLocked(ns)) if ns == target_namespace)
325 );
326
327 let mut intent_node_lock_a = NQuin::default();
329 intent_node_lock_a.subject = agent_a;
330 intent_node_lock_a.predicate = OP_INTENT_LOCK as u64;
331 intent_node_lock_a.object = target_node;
332 intent_node_lock_a.context = target_namespace;
333
334 let result_a = check_node_locks(&[intent_node_lock_a], ¤t_graph, agent_a);
335 assert!(result_a.is_ok());
336 }
337
338 #[test]
339 fn multi_agent_operators_and_introspection() {
340 assert!(everyone_knows(&[true, true, true]));
342 assert!(!everyone_knows(&[true, false, true]));
343 assert!(!everyone_knows(&[]));
344 let (a, b, c) = (q_hash("f:a"), q_hash("f:b"), q_hash("f:c"));
346 assert!(distributed_knowledge(&[a, b], &[a, b, c]));
347 assert!(
348 !distributed_knowledge(&[a, b], &[a, c]),
349 "missing fragment b"
350 );
351 assert!(common_knowledge_via_announcement(true));
353 assert!(!common_knowledge_via_announcement(false));
354 assert!(positive_introspection(true) && !positive_introspection(false));
356 assert!(negative_introspection(false) && !negative_introspection(true));
357 }
358
359 #[test]
360 fn muddy_children_deduction() {
361 assert!(!muddy_child_knows(2, 1));
363 assert!(muddy_child_knows(2, 2));
364 assert!(muddy_child_knows(1, 1));
366 }
367
368 #[test]
369 fn common_knowledge_propagation_across_two_agents() {
370 let agent_a = q_hash("agent_a");
375 let agent_b = q_hash("agent_b");
376 let world_w = q_hash("world_w");
377
378 let mk_knows = |agent: u64| {
379 let mut q = NQuin::default();
380 q.subject = agent;
381 q.predicate = (255u64 << CERTAINTY_BIT_SHIFT) | (OP_KNOWS as u64);
382 q.context = world_w;
383 q
384 };
385
386 let quins = [mk_knows(agent_a), mk_knows(agent_b)];
387
388 let mut out = [EpistemicVerdict {
390 claim: NQuin::default(),
391 status: EpistemicStatus::Skipped,
392 certainty: 0,
393 }; 4];
394 let count = evaluate_epistemic_frame(&quins, 0, world_w, &mut out).unwrap();
395 assert_eq!(count, 2, "both agents' claims must be evaluated");
396 assert_eq!(out[0].status, EpistemicStatus::Active);
397 assert_eq!(out[1].status, EpistemicStatus::Active);
398
399 let agent_knows = [
401 out[0].status == EpistemicStatus::Active,
402 out[1].status == EpistemicStatus::Active,
403 ];
404 assert!(everyone_knows(&agent_knows), "everyone knows φ");
405
406 assert!(
408 common_knowledge_via_announcement(everyone_knows(&agent_knows)),
409 "φ becomes common knowledge when everyone knows and it is publicly announced"
410 );
411 }
412
413 #[test]
414 fn agm_belief_revision_is_available_in_the_epistemic_namespace() {
415 let p = Belief {
417 atom: 1,
418 positive: true,
419 };
420 let not_p = Belief {
421 atom: 1,
422 positive: false,
423 };
424 let mut out = [Belief {
425 atom: 0,
426 positive: true,
427 }; 4];
428 let n = agm_revise(&[not_p], p, &mut out);
429 assert!(out[..n].contains(&p) && !out[..n].contains(¬_p));
430 assert!(belief_set_consistent(&out[..n]));
431 let _ = agm_expand;
432 let _ = agm_contract;
433 }
434}