microscope_memory/
emotional.rs1use crate::hebbian::HebbianState;
8use crate::reader::MicroscopeReader;
9use crate::LAYER_NAMES;
10
11const EMOTIONAL_LAYER_ID: u8 = 4;
13
14pub fn apply_emotional_bias(
19 qx: f32,
20 qy: f32,
21 qz: f32,
22 weight: f32,
23 reader: &MicroscopeReader,
24 hebb: &HebbianState,
25) -> (f32, f32, f32) {
26 if weight <= 0.0 {
27 return (qx, qy, qz);
28 }
29
30 let weight = weight.clamp(0.0, 1.0);
31
32 let mut sum_x = 0.0f32;
34 let mut sum_y = 0.0f32;
35 let mut sum_z = 0.0f32;
36 let mut total_energy = 0.0f32;
37
38 for i in hebb.hot_indices(0.01) {
44 let h = reader.header(i);
45 if h.layer_id != EMOTIONAL_LAYER_ID {
46 continue;
47 }
48 let energy = hebb.energy(i);
49
50 let hx = h.x;
52 let hy = h.y;
53 let hz = h.z;
54
55 sum_x += hx * energy;
56 sum_y += hy * energy;
57 sum_z += hz * energy;
58 total_energy += energy;
59 }
60
61 if total_energy < 0.01 {
62 return (qx, qy, qz); }
64
65 let cx = sum_x / total_energy;
67 let cy = sum_y / total_energy;
68 let cz = sum_z / total_energy;
69
70 let warped_x = qx + (cx - qx) * weight;
72 let warped_y = qy + (cy - qy) * weight;
73 let warped_z = qz + (cz - qz) * weight;
74
75 (warped_x, warped_y, warped_z)
76}
77
78pub fn emotional_field(reader: &MicroscopeReader, hebb: &HebbianState) -> Option<EmotionalField> {
81 let mut sum_x = 0.0f32;
82 let mut sum_y = 0.0f32;
83 let mut sum_z = 0.0f32;
84 let mut total_energy = 0.0f32;
85 let mut active_count = 0usize;
86 let mut hottest_idx: Option<(usize, f32)> = None;
87
88 for i in hebb.hot_indices(0.01) {
93 let h = reader.header(i);
94 if h.layer_id != EMOTIONAL_LAYER_ID {
95 continue;
96 }
97 let energy = hebb.energy(i);
98
99 let hx = h.x;
100 let hy = h.y;
101 let hz = h.z;
102
103 sum_x += hx * energy;
104 sum_y += hy * energy;
105 sum_z += hz * energy;
106 total_energy += energy;
107 active_count += 1;
108
109 if hottest_idx.is_none() || energy > hottest_idx.unwrap().1 {
110 hottest_idx = Some((i, energy));
111 }
112 }
113
114 if active_count == 0 {
115 return None;
116 }
117
118 Some(EmotionalField {
119 centroid: (
120 sum_x / total_energy,
121 sum_y / total_energy,
122 sum_z / total_energy,
123 ),
124 total_energy,
125 active_blocks: active_count,
126 hottest_block: hottest_idx,
127 })
128}
129
130pub struct EmotionalField {
131 pub centroid: (f32, f32, f32),
132 pub total_energy: f32,
133 pub active_blocks: usize,
134 pub hottest_block: Option<(usize, f32)>,
135}
136
137pub fn apply_emotional_bias_from_centroid(
141 qx: f32,
142 qy: f32,
143 qz: f32,
144 weight: f32,
145 centroid: Option<(f32, f32, f32)>,
146) -> (f32, f32, f32) {
147 if weight <= 0.0 {
148 return (qx, qy, qz);
149 }
150 match centroid {
151 None => (qx, qy, qz),
152 Some((cx, cy, cz)) => {
153 let w = weight.clamp(0.0, 1.0);
154 (qx + (cx - qx) * w, qy + (cy - qy) * w, qz + (cz - qz) * w)
155 }
156 }
157}
158
159pub fn emotional_layer_name() -> &'static str {
161 LAYER_NAMES
162 .get(EMOTIONAL_LAYER_ID as usize)
163 .unwrap_or(&"emotional")
164}
165
166#[cfg(test)]
167mod tests {
168 use super::*;
169 #[test]
170 fn test_no_warp_at_zero_weight() {
171 let (x, _y, _z) = apply_emotional_bias_from_centroid(0.5, 0.5, 0.5, 0.0, None);
172 assert!((x - 0.5).abs() < 0.001);
173 }
174
175 #[test]
176 fn test_warp_with_centroid() {
177 let centroid = Some((0.2, 0.3, 0.4));
179 let (x, y, z) = apply_emotional_bias_from_centroid(0.5, 0.5, 0.5, 0.5, centroid);
180 assert!((x - 0.35).abs() < 0.001);
182 assert!((y - 0.4).abs() < 0.001);
183 assert!((z - 0.45).abs() < 0.001);
184 }
185
186 #[test]
187 fn test_warp_full_weight() {
188 let centroid = Some((0.2, 0.3, 0.4));
189 let (x, y, z) = apply_emotional_bias_from_centroid(0.5, 0.5, 0.5, 1.0, centroid);
190 assert!((x - 0.2).abs() < 0.001);
192 assert!((y - 0.3).abs() < 0.001);
193 assert!((z - 0.4).abs() < 0.001);
194 }
195
196 #[test]
197 fn test_no_centroid() {
198 let (x, _y, _z) = apply_emotional_bias_from_centroid(0.5, 0.5, 0.5, 1.0, None);
199 assert!((x - 0.5).abs() < 0.001);
201 }
202
203 #[test]
204 fn test_emotional_layer_name() {
205 assert_eq!(emotional_layer_name(), "emotional");
206 }
207}