holographic_memory/core/cognition/
hypothesis.rs1use crate::core::atom_memory::AtomMemory;
11use crate::core::cognition::gaps::KnowledgeGap;
12use crate::core::entangled::EntangledHVec;
13use crate::core::triple_store::TripleStore;
14
15#[derive(Clone, Debug)]
17pub struct Hypothesis {
18 pub entity: String,
20 pub relation: String,
22 pub proposed_filler: String,
24 pub confidence: f64,
26 pub evidence_count: usize,
28 pub confirmed: bool,
30}
31
32pub struct HypothesisEngine;
34
35impl HypothesisEngine {
36 pub fn propose(
42 gaps: &[KnowledgeGap],
43 triple_store: &TripleStore,
44 atom_memory: &AtomMemory,
45 beta: f64,
46 min_confidence: f64,
47 ) -> Vec<Hypothesis> {
48 let mut hypotheses = Vec::new();
49
50 for gap in gaps {
51 if let Some(hyp) = Self::propose_one(gap, triple_store, atom_memory, beta) {
52 if hyp.confidence >= min_confidence {
53 hypotheses.push(hyp);
54 }
55 }
56 }
57
58 hypotheses.sort_by(|a, b| {
59 b.confidence
60 .partial_cmp(&a.confidence)
61 .unwrap_or(std::cmp::Ordering::Equal)
62 });
63 hypotheses
64 }
65
66 fn propose_one(
67 gap: &KnowledgeGap,
68 triple_store: &TripleStore,
69 atom_memory: &AtomMemory,
70 beta: f64,
71 ) -> Option<Hypothesis> {
72 let mut filler_vecs: Vec<EntangledHVec> = Vec::new();
74 let mut evidence_count = 0;
75
76 for peer in &gap.peers_with_relation {
77 let triples = triple_store.query(Some(peer), Some(&gap.missing_relation), None);
78 for t in &triples {
79 if let Some(vec) = atom_memory.get(&t.object_id) {
80 filler_vecs.push(vec);
81 evidence_count += 1;
82 }
83 }
84 }
85
86 if filler_vecs.is_empty() {
87 return None;
88 }
89
90 let bundled = EntangledHVec::bundle(&filler_vecs);
94 let probe = if bundled.indices().is_empty() {
95 filler_vecs[0].clone()
96 } else {
97 bundled
98 };
99
100 let result = atom_memory.cleanup(&probe, beta, 64, 3);
102 if !result.found {
103 return None;
104 }
105
106 Some(Hypothesis {
107 entity: gap.entity.clone(),
108 relation: gap.missing_relation.clone(),
109 proposed_filler: result.id,
110 confidence: result.confidence,
111 evidence_count,
112 confirmed: false,
113 })
114 }
115}
116
117#[cfg(test)]
118mod tests {
119 use super::*;
120 use crate::core::cognition::gaps::GapDetector;
121
122 #[test]
123 fn test_propose_hypothesis() {
124 let dim = 16384;
125 let atom_mem = AtomMemory::new(dim, 3.0);
126 let triple_store = TripleStore::new();
127
128 atom_mem.get_or_insert("paris");
130 atom_mem.get_or_insert("berlin");
131 atom_mem.get_or_insert("tokyo");
132 atom_mem.get_or_insert("europe");
133 atom_mem.get_or_insert("asia");
134 atom_mem.get_or_insert("france");
135 atom_mem.get_or_insert("germany");
136 atom_mem.get_or_insert("japan");
137
138 triple_store.add("paris", "capital_of", "france", "c1");
139 triple_store.add("paris", "located_in", "europe", "c2");
140 triple_store.add("berlin", "capital_of", "germany", "c3");
141 triple_store.add("berlin", "located_in", "europe", "c4");
142 triple_store.add("tokyo", "capital_of", "japan", "c5");
143 let gaps = GapDetector::detect(&triple_store, 1, 0.5);
146 let tokyo_gaps: Vec<_> = gaps
147 .iter()
148 .filter(|g| g.entity == "tokyo" && g.missing_relation == "located_in")
149 .cloned()
150 .collect();
151
152 if !tokyo_gaps.is_empty() {
153 let hypotheses =
154 HypothesisEngine::propose(&tokyo_gaps, &triple_store, &atom_mem, 24.0, 0.0);
155 if !hypotheses.is_empty() {
158 assert_eq!(hypotheses[0].entity, "tokyo");
159 assert_eq!(hypotheses[0].relation, "located_in");
160 assert_eq!(hypotheses[0].proposed_filler, "europe");
161 assert!(hypotheses[0].evidence_count >= 1);
162 }
163 }
164 }
165
166 #[test]
167 fn test_propose_empty_gaps() {
168 let dim = 16384;
169 let atom_mem = AtomMemory::new(dim, 3.0);
170 let triple_store = TripleStore::new();
171 let hypotheses = HypothesisEngine::propose(&[], &triple_store, &atom_mem, 24.0, 0.0);
172 assert!(hypotheses.is_empty());
173 }
174
175 #[test]
176 fn test_propose_min_confidence_filter() {
177 let dim = 16384;
178 let atom_mem = AtomMemory::new(dim, 3.0);
179 let triple_store = TripleStore::new();
180 let gaps = vec![KnowledgeGap {
182 entity: "x".to_string(),
183 missing_relation: "r".to_string(),
184 peers_with_relation: vec![],
185 peer_coverage: 1.0,
186 }];
187 let hypotheses = HypothesisEngine::propose(&gaps, &triple_store, &atom_mem, 24.0, 0.99);
188 assert!(hypotheses.is_empty());
189 }
190}