Skip to main content

anomstream_core/forest/
mod.rs

1//! Forest aggregate root.
2//!
3//! - [`point_store::PointStore`] — a refcounted ring buffer that
4//!   holds the canonical copy of every point currently referenced by
5//!   any tree. Trees see it through the
6//!   [`crate::tree::PointAccessor`] trait.
7//! - [`random_cut_forest::RandomCutForest`] — orchestrates `N`
8//!   `(RandomCutTree, ReservoirSampler)` pairs sharing the
9//!   [`point_store::PointStore`].
10//! - [`ForestSnapshot`] — minimal read-only health + capacity view,
11//!   exposed so downstream crates (`anomstream-triage`, any external
12//!   calibrator / SOC dashboard) can consume forest state without
13//!   reaching into the reservoir-level internals.
14
15pub mod point_store;
16pub mod random_cut_forest;
17
18pub use point_store::PointStore;
19pub use random_cut_forest::RandomCutForest;
20
21/// Read-only snapshot view of a forest's capacity + health.
22///
23/// Lives in `anomstream-core` so any consumer — including the
24/// downstream `anomstream-triage` crate that hosts SAGE, Platt,
25/// `AlertClusterer`, `FeedbackStore` — can introspect a forest
26/// (`RandomCutForest` or `ThresholdedForest`) without needing
27/// access to the reservoir internals (`point_store()`, `trees()`).
28///
29/// The contract is intentionally tiny; calibration / triage
30/// pipelines typically need only sizing + progress information,
31/// not tree-level data.
32///
33/// Implemented by:
34/// - [`RandomCutForest<D>`]
35/// - [`crate::thresholded::ThresholdedForest<D>`] (delegates to
36///   its inner forest)
37///
38/// # Examples
39///
40/// ```
41/// use anomstream_core::{ForestBuilder, ForestSnapshot};
42///
43/// let forest = ForestBuilder::<4>::new()
44///     .num_trees(50)
45///     .sample_size(64)
46///     .seed(42)
47///     .build()
48///     .unwrap();
49/// assert_eq!(forest.snapshot_num_trees(), 50);
50/// assert_eq!(forest.snapshot_dimension(), 4);
51/// assert_eq!(forest.snapshot_updates_seen(), 0);
52/// ```
53pub trait ForestSnapshot {
54    /// Number of trees in the forest.
55    fn snapshot_num_trees(&self) -> usize;
56    /// Per-tree reservoir capacity.
57    fn snapshot_sample_size(&self) -> usize;
58    /// Per-point compile-time dimensionality.
59    fn snapshot_dimension(&self) -> usize;
60    /// Live points currently referenced by at least one tree.
61    fn snapshot_live_points(&self) -> usize;
62    /// Total `update` calls observed since construction.
63    fn snapshot_updates_seen(&self) -> u64;
64    /// Pessimistic upper bound on the forest's memory footprint in
65    /// bytes (point store + tree arenas + samplers + RNGs).
66    fn snapshot_memory_estimate(&self) -> usize;
67}
68
69impl<const D: usize> ForestSnapshot for RandomCutForest<D> {
70    fn snapshot_num_trees(&self) -> usize {
71        self.num_trees()
72    }
73    fn snapshot_sample_size(&self) -> usize {
74        self.sample_size()
75    }
76    fn snapshot_dimension(&self) -> usize {
77        self.dimension()
78    }
79    fn snapshot_live_points(&self) -> usize {
80        self.point_store().live_count()
81    }
82    fn snapshot_updates_seen(&self) -> u64 {
83        self.updates_seen()
84    }
85    fn snapshot_memory_estimate(&self) -> usize {
86        self.memory_estimate()
87    }
88}