1use crate::temporal_graph::{P_WAS_ATTRIBUTED_TO, P_WAS_GENERATED_BY};
45use crate::{q_hash, NQuin};
46
47pub const OBJECTIVE_CONTEXT: u64 = q_hash("urn:qualia:context:objective");
51
52pub const EPISTEMIC_CONTEXT: u64 = q_hash("urn:qualia:context:epistemic");
54
55#[inline]
61pub fn agent_epistemic_context(did_hash: u64) -> u64 {
62 q_hash("urn:qualia:context:epistemic:agent") ^ did_hash
63}
64
65pub const C_CRYPTO_SENSOR: u64 = q_hash("urn:qualia:ontology:epistemic:CryptoSensor");
69pub const C_SIGNED_OBJECTIVE_EVENT: u64 =
71 q_hash("urn:qualia:ontology:epistemic:SignedObjectiveEvent");
72pub const C_AGENT_QUERY: u64 = q_hash("urn:qualia:ontology:epistemic:AgentQuery");
74pub const C_HEARSAY_EVENT: u64 = q_hash("urn:qualia:ontology:epistemic:HearsayEvent");
76
77pub const P_KNOWS_DIRECTLY: u64 = q_hash("urn:qualia:epistemic:knowsDirectly");
81pub const P_INFERS_FROM: u64 = q_hash("urn:qualia:epistemic:infersFrom");
83pub const P_BELIEVES_VIA_HEARSAY: u64 = q_hash("urn:qualia:epistemic:believesViaHearsay");
85
86pub const P_WAS_DERIVED_FROM: u64 = q_hash("http://www.w3.org/ns/prov#wasDerivedFrom");
88
89pub const P_COG_BELIEVES: u64 = q_hash("https://www.w3.org/community/cogai/ont#believes");
91pub const P_COG_QUERIES: u64 = q_hash("https://www.w3.org/community/cogai/ont#queries");
93pub const P_COG_OBSERVES: u64 = q_hash("https://www.w3.org/community/cogai/ont#observes");
95pub const P_COG_INFERS: u64 = q_hash("https://www.w3.org/community/cogai/ont#infers");
97
98const P_RDF_TYPE: u64 = q_hash("http://www.w3.org/1999/02/22-rdf-syntax-ns#type");
100
101#[derive(Debug, Clone, Copy, PartialEq, Eq)]
111pub enum EpistemicState {
112 ObjectiveKnowledge,
114 InferredBelief,
116 HearsayBelief,
118 Unknown,
120}
121
122pub fn classify_epistemic_state(
131 quins: &[NQuin],
132 agent_did: u64,
133 proposition_hash: u64,
134) -> EpistemicState {
135 let agent_ctx = agent_epistemic_context(agent_did);
136 let mut best = EpistemicState::Unknown;
137
138 for q in quins {
139 if q.context != agent_ctx && q.context != OBJECTIVE_CONTEXT {
140 continue;
141 }
142 if q.subject != agent_did && q.subject != proposition_hash {
143 continue;
144 }
145 if q.object != proposition_hash
146 && q.predicate != P_WAS_DERIVED_FROM
147 && q.predicate != P_INFERS_FROM
148 {
149 continue;
150 }
151
152 let state = predicate_to_state(q.predicate);
153 best = higher(best, state);
154 if best == EpistemicState::ObjectiveKnowledge {
155 break;
156 }
157 }
158 best
159}
160
161#[inline]
163fn higher(a: EpistemicState, b: EpistemicState) -> EpistemicState {
164 use EpistemicState::*;
165 match (a, b) {
166 (ObjectiveKnowledge, _) | (_, ObjectiveKnowledge) => ObjectiveKnowledge,
167 (InferredBelief, _) | (_, InferredBelief) => InferredBelief,
168 (HearsayBelief, _) | (_, HearsayBelief) => HearsayBelief,
169 (Unknown, Unknown) => Unknown,
170 }
171}
172
173#[inline]
174fn predicate_to_state(predicate: u64) -> EpistemicState {
175 if predicate == P_KNOWS_DIRECTLY {
176 EpistemicState::ObjectiveKnowledge
177 } else if predicate == P_INFERS_FROM {
178 EpistemicState::InferredBelief
179 } else if predicate == P_BELIEVES_VIA_HEARSAY {
180 EpistemicState::HearsayBelief
181 } else {
182 EpistemicState::Unknown
183 }
184}
185
186pub fn assert_objective_knowledge(
196 agent_did: u64,
197 proposition_hash: u64,
198 proof_hash: u64,
199 ts: u64,
200) -> [NQuin; 3] {
201 let ctx = agent_epistemic_context(agent_did);
202 [
203 make_ep(agent_did, P_KNOWS_DIRECTLY, proposition_hash, ctx, ts),
204 make_ep(agent_did, P_COG_BELIEVES, proposition_hash, ctx, ts),
205 make_ep(
206 proposition_hash,
207 P_WAS_GENERATED_BY,
208 proof_hash,
209 OBJECTIVE_CONTEXT,
210 ts,
211 ),
212 ]
213}
214
215pub fn assert_inferred_belief(
223 agent_did: u64,
224 proposition_hash: u64,
225 source_hash: u64,
226 ts: u64,
227) -> [NQuin; 3] {
228 let ctx = agent_epistemic_context(agent_did);
229 [
230 make_ep(agent_did, P_INFERS_FROM, source_hash, ctx, ts),
231 make_ep(agent_did, P_COG_BELIEVES, proposition_hash, ctx, ts),
232 make_ep(proposition_hash, P_WAS_DERIVED_FROM, source_hash, ctx, ts),
233 ]
234}
235
236pub fn assert_hearsay_belief(
244 agent_did: u64,
245 proposition_hash: u64,
246 informant_did: u64,
247 ts: u64,
248) -> [NQuin; 3] {
249 let ctx = agent_epistemic_context(agent_did);
250 [
251 make_ep(agent_did, P_BELIEVES_VIA_HEARSAY, proposition_hash, ctx, ts),
252 make_ep(agent_did, P_COG_BELIEVES, proposition_hash, ctx, ts),
253 make_ep(proposition_hash, P_WAS_DERIVED_FROM, informant_did, ctx, ts),
254 ]
255}
256
257pub fn record_query_observation(observer_did: u64, query_subject_hash: u64, ts: u64) -> NQuin {
265 make_ep(
266 observer_did,
267 P_COG_QUERIES,
268 query_subject_hash,
269 EPISTEMIC_CONTEXT,
270 ts,
271 )
272}
273
274pub fn register_crypto_sensor_output(proof_hash: u64, ts: u64) -> NQuin {
277 make_ep(
278 proof_hash,
279 P_RDF_TYPE,
280 C_CRYPTO_SENSOR,
281 OBJECTIVE_CONTEXT,
282 ts,
283 )
284}
285
286pub fn register_query_event(query_event_hash: u64, querying_agent_did: u64, ts: u64) -> [NQuin; 2] {
289 [
290 make_ep(
291 query_event_hash,
292 P_RDF_TYPE,
293 C_AGENT_QUERY,
294 EPISTEMIC_CONTEXT,
295 ts,
296 ),
297 make_ep(
298 query_event_hash,
299 P_WAS_ATTRIBUTED_TO,
300 querying_agent_did,
301 EPISTEMIC_CONTEXT,
302 ts,
303 ),
304 ]
305}
306
307pub fn objective_knowledge_of(quins: &[NQuin], agent_did: u64, out: &mut [u64]) -> usize {
315 let ctx = agent_epistemic_context(agent_did);
316 let mut n = 0usize;
317 for q in quins {
318 if q.context == ctx && q.subject == agent_did && q.predicate == P_KNOWS_DIRECTLY {
319 if n >= out.len() {
320 break;
321 }
322 out[n] = q.object;
323 n += 1;
324 }
325 }
326 n
327}
328
329pub fn all_beliefs_of(quins: &[NQuin], agent_did: u64, out: &mut [u64]) -> usize {
333 let ctx = agent_epistemic_context(agent_did);
334 let mut n = 0usize;
335 for q in quins {
336 if q.context == ctx && q.subject == agent_did && q.predicate == P_COG_BELIEVES {
337 if n >= out.len() {
338 break;
339 }
340 out[n] = q.object;
341 n += 1;
342 }
343 }
344 n
345}
346
347pub fn has_objective_proof(quins: &[NQuin], proposition_hash: u64) -> bool {
350 quins.iter().any(|q| {
351 q.context == OBJECTIVE_CONTEXT
352 && q.subject == proposition_hash
353 && q.predicate == P_WAS_GENERATED_BY
354 })
355}
356
357#[inline]
360fn make_ep(subject: u64, predicate: u64, object: u64, context: u64, lamport: u64) -> NQuin {
361 NQuin {
362 subject,
363 predicate,
364 object,
365 context,
366 metadata: lamport & 0xFFFF_FFFF,
367 parity: 0,
368 }
369}
370
371#[cfg(test)]
374mod tests {
375 use super::*;
376
377 const JOHN: u64 = 0x111A_u64;
378 const FRANK: u64 = 0x222B_u64;
379 const JANE: u64 = 0x333C_u64;
380 const MATTER: u64 = 0xFACE_1234_u64;
381 const SENSOR: u64 = 0xDEAD_BEEF_u64;
382
383 #[test]
384 fn agent_epistemic_contexts_are_distinct() {
385 let ctx_john = agent_epistemic_context(JOHN);
386 let ctx_frank = agent_epistemic_context(FRANK);
387 let ctx_jane = agent_epistemic_context(JANE);
388 assert_ne!(ctx_john, ctx_frank);
389 assert_ne!(ctx_frank, ctx_jane);
390 assert_ne!(ctx_john, ctx_jane);
391 }
392
393 #[test]
394 fn objective_knowledge_produces_correct_contexts() {
395 let qs = assert_objective_knowledge(JOHN, MATTER, SENSOR, 1000);
396 let john_ctx = agent_epistemic_context(JOHN);
397 assert_eq!(qs[0].context, john_ctx);
399 assert_eq!(qs[1].context, john_ctx);
400 assert_eq!(qs[2].context, OBJECTIVE_CONTEXT);
402 assert_eq!(qs[2].predicate, P_WAS_GENERATED_BY);
403 assert_eq!(qs[2].object, SENSOR);
404 }
405
406 #[test]
407 fn inferred_belief_does_not_collide_with_objective() {
408 let frank_query = 0x9999_u64;
409 let obj_qs = assert_objective_knowledge(JOHN, MATTER, SENSOR, 1000);
410 let inf_qs = assert_inferred_belief(JANE, MATTER, frank_query, 2000);
411
412 let john_ctx = agent_epistemic_context(JOHN);
413 let jane_ctx = agent_epistemic_context(JANE);
414 assert_ne!(john_ctx, jane_ctx);
415
416 for q in &obj_qs {
418 assert_ne!(q.context, jane_ctx);
419 }
420 for q in &inf_qs {
422 assert_ne!(q.context, john_ctx);
423 }
424 }
425
426 #[test]
427 fn classify_detects_objective_knowledge() {
428 let qs = assert_objective_knowledge(JOHN, MATTER, SENSOR, 1000);
429 let state = classify_epistemic_state(&qs, JOHN, MATTER);
430 assert_eq!(state, EpistemicState::ObjectiveKnowledge);
431 }
432
433 #[test]
434 fn classify_detects_inferred_belief() {
435 let frank_query = 0x9999_u64;
436 let qs = assert_inferred_belief(JANE, MATTER, frank_query, 2000);
437 let state = classify_epistemic_state(&qs, JANE, MATTER);
438 assert_eq!(state, EpistemicState::InferredBelief);
439 }
440
441 #[test]
442 fn classify_detects_hearsay() {
443 let qs = assert_hearsay_belief(JANE, MATTER, FRANK, 3000);
444 let state = classify_epistemic_state(&qs, JANE, MATTER);
445 assert_eq!(state, EpistemicState::HearsayBelief);
446 }
447
448 #[test]
449 fn classify_returns_unknown_for_unrelated_quins() {
450 let qs = assert_objective_knowledge(JOHN, MATTER, SENSOR, 1000);
451 let state = classify_epistemic_state(&qs, JANE, MATTER);
453 assert_eq!(state, EpistemicState::Unknown);
454 }
455
456 #[test]
457 fn has_objective_proof_detects_crypto_link() {
458 let qs = assert_objective_knowledge(JOHN, MATTER, SENSOR, 1000);
459 assert!(has_objective_proof(&qs, MATTER));
460 assert!(!has_objective_proof(&qs, 0xFFFF_u64));
461 }
462
463 #[test]
464 fn objective_knowledge_and_beliefs_write_into_caller_buffer() {
465 let qs = assert_objective_knowledge(JOHN, MATTER, SENSOR, 1000);
466
467 let mut buf = [0u64; 8];
468 let n = objective_knowledge_of(&qs, JOHN, &mut buf);
469 assert_eq!(n, 1);
470 assert_eq!(buf[0], MATTER);
471
472 let mut bel = [0u64; 8];
473 let m = all_beliefs_of(&qs, JOHN, &mut bel);
474 assert_eq!(m, 1);
475 assert_eq!(bel[0], MATTER);
476
477 let mut empty: [u64; 0] = [];
479 assert_eq!(objective_knowledge_of(&qs, JOHN, &mut empty), 0);
480 }
481
482 #[test]
483 fn john_frank_jane_scenario() {
484 let mut all: Vec<NQuin> = Vec::new();
485
486 all.extend_from_slice(&assert_objective_knowledge(JOHN, MATTER, SENSOR, 1000));
488
489 all.push(record_query_observation(FRANK, MATTER, 2000));
491 let frank_query_hash = FRANK.wrapping_mul(0x9e37_79b9) ^ MATTER ^ 2000;
492
493 all.extend_from_slice(&assert_inferred_belief(
495 JANE,
496 MATTER,
497 frank_query_hash,
498 3000,
499 ));
500
501 assert_eq!(
503 classify_epistemic_state(&all, JOHN, MATTER),
504 EpistemicState::ObjectiveKnowledge,
505 );
506 assert_eq!(
508 classify_epistemic_state(&all, JANE, MATTER),
509 EpistemicState::InferredBelief,
510 );
511 assert_eq!(
513 classify_epistemic_state(&all, FRANK, MATTER),
514 EpistemicState::Unknown,
515 );
516 }
517}