holographic_memory/core/engine/
concepts.rs1use fxhash::{FxHashMap, FxHashSet};
5
6use super::HmsCore;
7use crate::core::entangled::EntangledHVec;
8use crate::core::types::ConceptCandidate;
9
10impl HmsCore {
11 pub fn synthesize_concepts(&self) -> Vec<ConceptCandidate> {
16 let cfg = &self.config.concepts;
17 let mut all_ids = Vec::new();
18 let mut all_vectors = Vec::new();
19
20 let shards = self.shards.read();
21 shards.for_each_shard(|shard| {
22 let (ids, vectors) = shard.load_all_vectors();
23 all_ids.extend(ids);
24 all_vectors.extend(vectors);
25 });
26
27 if all_ids.len() < cfg.min_cluster_size {
28 return vec![];
29 }
30
31 let n = all_vectors.len();
32 let mut used = vec![false; n];
33 let mut concepts = Vec::new();
34
35 let use_postings = n > 500;
38 let neighbors: Vec<FxHashSet<usize>> = if use_postings {
39 let mut postings: FxHashMap<u32, Vec<usize>> = FxHashMap::default();
40 for (i, vec) in all_vectors.iter().enumerate() {
41 for &idx in vec.indices() {
42 postings.entry(idx).or_default().push(i);
43 }
44 }
45 let mut nbrs = vec![FxHashSet::default(); n];
46 for list in postings.values() {
47 for (pos, &a) in list.iter().enumerate() {
48 for &b in &list[pos + 1..] {
49 nbrs[a].insert(b);
50 nbrs[b].insert(a);
51 }
52 }
53 }
54 nbrs
55 } else {
56 Vec::new()
57 };
58
59 for i in 0..n {
60 if used[i] {
61 continue;
62 }
63 let mut cluster = vec![i];
64 if use_postings {
65 for &j in &neighbors[i] {
66 if !used[j]
67 && all_vectors[i].similarity(&all_vectors[j]) > cfg.similarity_threshold
68 {
69 cluster.push(j);
70 }
71 }
72 } else {
73 for j in (i + 1)..n {
74 if !used[j]
75 && all_vectors[i].similarity(&all_vectors[j]) > cfg.similarity_threshold
76 {
77 cluster.push(j);
78 }
79 }
80 }
81 if cluster.len() >= cfg.min_cluster_size {
82 for &idx in &cluster {
83 used[idx] = true;
84 }
85 let cluster_vecs: Vec<&EntangledHVec> =
86 cluster.iter().map(|&idx| &all_vectors[idx]).collect();
87 let centroid = self.bundle(&cluster_vecs);
88 let coherence: f64 = cluster_vecs
89 .iter()
90 .map(|v| v.similarity(¢roid))
91 .sum::<f64>()
92 / cluster_vecs.len() as f64;
93 let member_ids: Vec<String> =
94 cluster.iter().map(|&idx| all_ids[idx].clone()).collect();
95
96 concepts.push(ConceptCandidate {
97 centroid_id: member_ids.first().cloned().unwrap_or_default(),
98 member_count: cluster.len() as u32,
99 coherence,
100 member_ids,
101 stable: false,
102 });
103 }
104 }
105
106 if concepts.len() >= 2 {
109 let cluster_indices: Vec<usize> = (0..n).filter(|&i| used[i]).collect();
110
111 let mut assignment: FxHashMap<usize, usize> = FxHashMap::default();
113 for (ci, concept) in concepts.iter().enumerate() {
114 for mid in &concept.member_ids {
115 if let Some(pos) = all_ids.iter().position(|id| id == mid) {
116 assignment.insert(pos, ci);
117 }
118 }
119 }
120
121 let mut centroids: Vec<EntangledHVec> = concepts
122 .iter()
123 .enumerate()
124 .map(|(ci, _)| {
125 let members: Vec<&EntangledHVec> = cluster_indices
126 .iter()
127 .filter(|&&vi| assignment.get(&vi) == Some(&ci))
128 .map(|&vi| &all_vectors[vi])
129 .collect();
130 self.bundle(&members)
131 })
132 .collect();
133
134 let mut stable = true;
135 for _iter in 0..3 {
136 let mut changed = false;
137 for &vi in &cluster_indices {
138 let mut best_ci = assignment[&vi];
139 let mut best_sim = all_vectors[vi].similarity(¢roids[best_ci]);
140 for (ci, centroid) in centroids.iter().enumerate() {
141 let sim = all_vectors[vi].similarity(centroid);
142 if sim > best_sim {
143 best_sim = sim;
144 best_ci = ci;
145 }
146 }
147 if best_ci != assignment[&vi] {
148 assignment.insert(vi, best_ci);
149 changed = true;
150 }
151 }
152 if !changed {
153 stable = true;
154 break;
155 }
156 stable = false;
157 for (ci, centroid) in centroids.iter_mut().enumerate() {
159 let members: Vec<&EntangledHVec> = cluster_indices
160 .iter()
161 .filter(|&&vi| assignment.get(&vi) == Some(&ci))
162 .map(|&vi| &all_vectors[vi])
163 .collect();
164 if !members.is_empty() {
165 *centroid = self.bundle(&members);
166 }
167 }
168 }
169
170 for (ci, concept) in concepts.iter_mut().enumerate() {
172 let members: Vec<usize> = cluster_indices
173 .iter()
174 .filter(|&&vi| assignment.get(&vi) == Some(&ci))
175 .copied()
176 .collect();
177 if members.is_empty() {
178 concept.member_count = 0;
179 concept.member_ids.clear();
180 concept.coherence = 0.0;
181 concept.stable = stable;
182 continue;
183 }
184 let member_vecs: Vec<&EntangledHVec> =
185 members.iter().map(|&vi| &all_vectors[vi]).collect();
186 let coherence: f64 = member_vecs
187 .iter()
188 .map(|v| v.similarity(¢roids[ci]))
189 .sum::<f64>()
190 / member_vecs.len() as f64;
191 concept.member_ids = members.iter().map(|&vi| all_ids[vi].clone()).collect();
192 concept.centroid_id = concept.member_ids.first().cloned().unwrap_or_default();
193 concept.member_count = members.len() as u32;
194 concept.coherence = coherence;
195 concept.stable = stable;
196 }
197
198 concepts.retain(|c| c.member_count > 0);
200 } else {
201 for concept in &mut concepts {
203 concept.stable = true;
204 }
205 }
206
207 concepts
208 }
209}