File size: 3,080 Bytes
1c59946 c475135 1c59946 c475135 1c59946 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | //! HTMRegion: compose SpatialPooler + TemporalMemory into a single step().
use crate::sp::{SpatialPooler, SpatialPoolerConfig};
use crate::tm::{TemporalMemory, TemporalMemoryConfig};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
pub struct HTMRegionCore {
pub sp: SpatialPooler,
pub tm: TemporalMemory,
}
impl HTMRegionCore {
pub fn new(
input_bits: usize,
n_columns: usize,
cells_per_column: usize,
seed: u64,
) -> Self {
let defaults = SpatialPoolerConfig::default();
let sp_cfg = SpatialPoolerConfig {
input_bits,
n_columns,
// Scale potential_radius to at most the input size.
potential_radius: defaults.potential_radius.min(input_bits),
..defaults
};
let tm_cfg = TemporalMemoryConfig {
n_columns,
cells_per_column,
..TemporalMemoryConfig::default()
};
Self {
sp: SpatialPooler::new(sp_cfg, seed),
tm: TemporalMemory::new(tm_cfg, seed.wrapping_add(0x9E3779B97F4A7C15)),
}
}
/// Process one timestep. Returns (active_columns_mask,
/// active_cells_mask, predicted_cells_mask, anomaly).
pub fn step(
&mut self,
input_sdr: &[bool],
learn: bool,
) -> (Vec<bool>, Vec<bool>, Vec<bool>, f32) {
let active_cols = self.sp.compute(input_sdr, learn);
let mut active_cols_mask = vec![false; self.sp.cfg.n_columns];
for &c in &active_cols {
active_cols_mask[c as usize] = true;
}
let anomaly = self.tm.compute(&active_cols, learn);
// active_cells and predictive_cells are stored as Vec<bool> already.
let active_cells_mask = self.tm.active_cells.clone();
let predicted_cells_mask = self.tm.predictive_cells.clone();
(active_cols_mask, active_cells_mask, predicted_cells_mask, anomaly)
}
pub fn reset(&mut self) {
self.tm.reset();
}
/// Process T timesteps in one call. Returns flat `(T*n_columns)` active-column
/// mask (u8 0/1) and `(T,)` anomaly scores.
///
/// Amortises the per-step Python round-trip for training: one GIL release,
/// one copy-out. Used by `HTMLayer.step_many`.
pub fn step_many(
&mut self,
inputs_flat: &[bool],
input_bits: usize,
t: usize,
learn: bool,
) -> (Vec<u8>, Vec<f32>) {
let n_cols = self.sp.cfg.n_columns;
debug_assert_eq!(inputs_flat.len(), t * input_bits);
let mut cols = vec![0u8; t * n_cols];
let mut anom = vec![0f32; t];
for ti in 0..t {
let off = ti * input_bits;
let input = &inputs_flat[off..off + input_bits];
let active_cols = self.sp.compute(input, learn);
let co = ti * n_cols;
for &c in &active_cols {
cols[co + c as usize] = 1;
}
anom[ti] = self.tm.compute(&active_cols, learn);
}
(cols, anom)
}
}
|