Core Traits#
The Rust core defines several traits that form the extension points of the system.
CostModel#
Defined in cpd-costs. The shared contract for segment cost computation.
pub trait CostModel {
type Cache: Send + Sync;
fn name(&self) -> &'static str;
fn validate(&self, x: &TimeSeriesView<'_>) -> Result<(), CpdError>;
fn missing_support(&self) -> MissingSupport { MissingSupport::Reject }
fn precompute(
&self,
x: &TimeSeriesView<'_>,
policy: &CachePolicy,
) -> Result<Self::Cache, CpdError>;
fn worst_case_cache_bytes(&self, x: &TimeSeriesView<'_>) -> usize;
fn supports_approx_cache(&self) -> bool { false }
fn penalty_params_per_segment(&self) -> usize { 2 }
fn penalty_effective_params(&self, d: usize) -> Option<usize> {
d.checked_mul(self.penalty_params_per_segment())
}
fn segment_cost(&self, cache: &Self::Cache, start: usize, end: usize) -> f64;
fn segment_cost_batch(
&self,
cache: &Self::Cache,
queries: &[(usize, usize)],
out_costs: &mut [f64],
) { /* default: loops over segment_cost */ }
}
Key design points:
Two-phase pattern:
precompute()builds a cache once;segment_cost()queries it O(1) per segmentHalf-open intervals: segments are
[start, end)by conventionCache + Send + Sync: enables safe sharing across threads when using Rayon
Penalty integration:
penalty_params_per_segment()drives BIC/AIC penalty computation; models with non-linear complexity (e.g.,CostNormalFullCov) overridepenalty_effective_params()Batch path:
segment_cost_batch()allows vectorized implementations; the default loops oversegment_cost()
CachedCost<C>#
A convenience wrapper that bundles a CostModel with its precomputed cache:
pub struct CachedCost<C: CostModel> {
model: C,
cache: C::Cache,
}
impl<C: CostModel> CachedCost<C> {
pub fn new(model: C, x: &TimeSeriesView<'_>, policy: &CachePolicy) -> Result<Self, CpdError>;
pub fn from_parts(model: C, cache: C::Cache) -> Self;
pub fn segment_cost(&self, start: usize, end: usize) -> f64;
pub fn segment_cost_batch(&self, queries: &[(usize, usize)], out_costs: &mut [f64]);
pub fn model(&self) -> &C;
pub fn cache(&self) -> &C::Cache;
pub fn into_parts(self) -> (C, C::Cache);
}
CachedCost::new() validates data compatibility (including missing-policy checks) and materializes the cache.
OfflineDetector#
Defined in cpd-core. Contract for batch detectors.
pub trait OfflineDetector {
fn detect(
&self,
x: &TimeSeriesView<'_>,
ctx: &ExecutionContext<'_>,
) -> Result<OfflineChangePointResult, CpdError>;
}
All offline algorithms (PELT, BinSeg, FPOP, SegNeigh, WBS) implement this trait. The ExecutionContext carries constraints, cancellation, budget mode, and reproducibility settings.
OnlineDetector#
Defined in cpd-core. Contract for streaming detectors.
pub trait OnlineDetector {
type State: Clone + std::fmt::Debug;
fn reset(&mut self);
fn update(
&mut self,
x_t: &[f64],
t_ns: Option<i64>,
ctx: &ExecutionContext<'_>,
) -> Result<OnlineStepResult, CpdError>;
fn save_state(&self) -> Self::State;
fn load_state(&mut self, state: &Self::State);
fn update_many(
&mut self,
x: &TimeSeriesView<'_>,
ctx: &ExecutionContext<'_>,
) -> Result<Vec<OnlineStepResult>, CpdError> { /* default impl */ }
}
Key design points:
Stateful: detectors maintain internal state (run-length distribution for BOCPD, cumulative sums for CUSUM)
Checkpoint/restore:
save_state()/load_state()enable fault toleranceDefault
update_many: iteratesupdate()with cancellation checks; implementations can override for batched optimizationMultivariate via
x_t: &[f64]: single observation is a slice of dimension d
ExecutionContext#
Unified execution context threaded through all detector calls:
pub struct ExecutionContext<'a> {
pub constraints: &'a Constraints,
pub cancel: Option<&'a CancelToken>,
pub budget_mode: BudgetMode,
pub repro_mode: ReproMode,
pub progress: Option<&'a dyn ProgressSink>,
pub telemetry: Option<&'a dyn TelemetrySink>,
}
Builder pattern:
let ctx = ExecutionContext::new(&constraints)
.with_cancel(&cancel)
.with_budget_mode(BudgetMode::SoftDegrade)
.with_repro_mode(ReproMode::Strict)
.with_progress_sink(&progress)
.with_telemetry_sink(&telemetry);
Methods for cooperative cancellation and budget enforcement:
check_cancelled()/check_cancelled_every(iter, every)check_cost_eval_budget(evals)check_time_budget(started_at)report_progress(fraction)/record_scalar(key, value)
TimeSeriesView<'a>#
Zero-copy borrowed view over signal data:
pub struct TimeSeriesView<'a> {
pub values: DTypeView<'a>, // F32 or F64 slice
pub n: usize, // number of observations
pub d: usize, // number of dimensions
pub layout: MemoryLayout, // CContiguous, FContiguous, or Strided
pub mask: Option<&'a [u8]>, // optional NaN/missing mask
pub time: TimeIndex<'a>, // None, Uniform{t0,dt}, or Explicit
pub missing: MissingPolicy, // Error, Ignore, or Impute
}
This design avoids data copying when converting from NumPy arrays via PyO3.
CpdError#
Unified error type with categorized variants:
InvalidInput(String)– bad parameters, shape mismatches, unsupported configurationsResourceLimit(String)– budget exceeded (cost evals, time, memory)Cancelled– cooperative cancellation triggeredAlgorithmFailure(String)– numerical failures, convergence issues