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)
    }
}