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(
20 qx: f32,
21 qy: f32,
22 qz: f32,
23 weight: f32,
24 reader: &MicroscopeReader,
25 hebb: &HebbianState,
26) -> (f32, f32, f32) {
27 if weight <= 0.0 {
28 return (qx, qy, qz);
29 }
30
31 let weight = weight.clamp(0.0, 1.0);
32
33 let mut sum_x = 0.0f32;
35 let mut sum_y = 0.0f32;
36 let mut sum_z = 0.0f32;
37 let mut total_energy = 0.0f32;
38
39 for i in 0..reader.block_count {
40 let h = reader.header(i);
41 if h.layer_id != EMOTIONAL_LAYER_ID {
42 continue;
43 }
44
45 let energy = hebb.energy(i);
46 if energy < 0.01 {
47 continue;
48 }
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 0..reader.block_count {
89 let h = reader.header(i);
90 if h.layer_id != EMOTIONAL_LAYER_ID {
91 continue;
92 }
93
94 let energy = hebb.energy(i);
95 if energy < 0.01 {
96 continue;
97 }
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 emotional_layer_name() -> &'static str {
139 LAYER_NAMES
140 .get(EMOTIONAL_LAYER_ID as usize)
141 .unwrap_or(&"emotional")
142}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147 #[test]
148 fn test_no_warp_at_zero_weight() {
149 let (x, _y, _z) = apply_emotional_bias_pure(0.5, 0.5, 0.5, 0.0, None);
150 assert!((x - 0.5).abs() < 0.001);
151 }
152
153 #[test]
154 fn test_warp_with_centroid() {
155 let centroid = Some((0.2, 0.3, 0.4));
157 let (x, y, z) = apply_emotional_bias_pure(0.5, 0.5, 0.5, 0.5, centroid);
158 assert!((x - 0.35).abs() < 0.001);
160 assert!((y - 0.4).abs() < 0.001);
161 assert!((z - 0.45).abs() < 0.001);
162 }
163
164 #[test]
165 fn test_warp_full_weight() {
166 let centroid = Some((0.2, 0.3, 0.4));
167 let (x, y, z) = apply_emotional_bias_pure(0.5, 0.5, 0.5, 1.0, centroid);
168 assert!((x - 0.2).abs() < 0.001);
170 assert!((y - 0.3).abs() < 0.001);
171 assert!((z - 0.4).abs() < 0.001);
172 }
173
174 #[test]
175 fn test_no_centroid() {
176 let (x, _y, _z) = apply_emotional_bias_pure(0.5, 0.5, 0.5, 1.0, None);
177 assert!((x - 0.5).abs() < 0.001);
179 }
180
181 #[test]
182 fn test_emotional_layer_name() {
183 assert_eq!(emotional_layer_name(), "emotional");
184 }
185
186 fn apply_emotional_bias_pure(
188 qx: f32,
189 qy: f32,
190 qz: f32,
191 weight: f32,
192 centroid: Option<(f32, f32, f32)>,
193 ) -> (f32, f32, f32) {
194 if weight <= 0.0 {
195 return (qx, qy, qz);
196 }
197 match centroid {
198 None => (qx, qy, qz),
199 Some((cx, cy, cz)) => {
200 let w = weight.clamp(0.0, 1.0);
201 (qx + (cx - qx) * w, qy + (cy - qy) * w, qz + (cz - qz) * w)
202 }
203 }
204 }
205}