Commit ·
7c57667
1
Parent(s): c85191b
Exorcise field ghosts: remove_region on free, dead allocations leave the field
Browse files63,000 cold regions in the Lenia field were freed allocations that never
got removed. The field was paying to cool corpses — stepping temperature
on memory that no longer exists.
Fix: add LeniaField::remove_region(), wire into Pipeline::process_free().
Freed allocations are removed from the field, their energy reclaimed,
and their neighbor links cleaned up. The field only contains live memory.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- rust_core/src/lenia.rs +18 -0
- rust_core/src/pipeline.rs +4 -1
rust_core/src/lenia.rs
CHANGED
|
@@ -184,6 +184,24 @@ impl LeniaField {
|
|
| 184 |
self.regions.insert(id, region);
|
| 185 |
}
|
| 186 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 187 |
/// Set neighborhood connections from graph edges
|
| 188 |
pub fn set_neighbors(&mut self, id: u32, neighbors: Vec<(u32, f64)>) {
|
| 189 |
self.neighbors.insert(id, neighbors);
|
|
|
|
| 184 |
self.regions.insert(id, region);
|
| 185 |
}
|
| 186 |
|
| 187 |
+
/// Remove a region from the field — called when an allocation is freed.
|
| 188 |
+
/// Reclaims the energy and removes from all tracking structures.
|
| 189 |
+
/// Dead allocations must not haunt the field.
|
| 190 |
+
pub fn remove_region(&mut self, id: u32) {
|
| 191 |
+
if let Some(region) = self.regions.remove(&id) {
|
| 192 |
+
let energy = region.temperature * (region.size_bytes as f64 / (1024.0 * 1024.0));
|
| 193 |
+
self.total_energy -= energy;
|
| 194 |
+
if self.total_energy < 0.0 {
|
| 195 |
+
self.total_energy = 0.0;
|
| 196 |
+
}
|
| 197 |
+
}
|
| 198 |
+
self.neighbors.remove(&id);
|
| 199 |
+
// Also remove this id from other regions' neighbor lists
|
| 200 |
+
for (_rid, nbrs) in self.neighbors.iter_mut() {
|
| 201 |
+
nbrs.retain(|(nid, _)| *nid != id);
|
| 202 |
+
}
|
| 203 |
+
}
|
| 204 |
+
|
| 205 |
/// Set neighborhood connections from graph edges
|
| 206 |
pub fn set_neighbors(&mut self, id: u32, neighbors: Vec<(u32, f64)>) {
|
| 207 |
self.neighbors.insert(id, neighbors);
|
rust_core/src/pipeline.rs
CHANGED
|
@@ -340,7 +340,10 @@ impl Pipeline {
|
|
| 340 |
pub fn process_free(&mut self, address: usize) {
|
| 341 |
self.condenser.unregister(address);
|
| 342 |
self.address_to_path.remove(&address);
|
| 343 |
-
|
|
|
|
|
|
|
|
|
|
| 344 |
}
|
| 345 |
|
| 346 |
/// Rebuild the graph from accumulated events and retrain the predictor.
|
|
|
|
| 340 |
pub fn process_free(&mut self, address: usize) {
|
| 341 |
self.condenser.unregister(address);
|
| 342 |
self.address_to_path.remove(&address);
|
| 343 |
+
// Remove from Lenia field — dead allocations don't get thermal cycles
|
| 344 |
+
if let Some(field_id) = self.address_to_field_id.remove(&address) {
|
| 345 |
+
self.field.remove_region(field_id);
|
| 346 |
+
}
|
| 347 |
}
|
| 348 |
|
| 349 |
/// Rebuild the graph from accumulated events and retrain the predictor.
|