use super::config::*;
use super::optimizer::{Adaptation, AdaptationPriority, AdaptationType, StreamingDataPoint};
use super::resource_management::ResourceUsage;
use scirs2_core::numeric::Float;
use serde::{Deserialize, Serialize};
use std::collections::{HashMap, VecDeque};
use std::iter::Sum;
use std::time::{Duration, Instant};
#[derive(Debug, Clone)]
pub struct PerformanceSnapshot<A: Float + Send + Sync> {
pub timestamp: Instant,
pub loss: A,
pub accuracy: Option<A>,
pub convergence_rate: Option<A>,
pub gradient_norm: Option<A>,
pub parameter_update_magnitude: Option<A>,
pub data_statistics: DataStatistics<A>,
pub resource_usage: ResourceUsage,
pub custom_metrics: HashMap<String, A>,
}
#[derive(Debug, Clone)]
pub struct DataStatistics<A: Float + Send + Sync> {
pub sample_count: usize,
pub feature_means: scirs2_core::ndarray::Array1<A>,
pub feature_stds: scirs2_core::ndarray::Array1<A>,
pub average_quality: A,
pub timestamp: Instant,
}
impl<A: Float + Send + Sync> Default for DataStatistics<A> {
fn default() -> Self {
Self {
sample_count: 0,
feature_means: scirs2_core::ndarray::Array1::zeros(0),
feature_stds: scirs2_core::ndarray::Array1::zeros(0),
average_quality: A::zero(),
timestamp: Instant::now(),
}
}
}
#[derive(Debug, Clone)]
pub enum PerformanceMetric<A: Float + Send + Sync> {
Loss(A),
Accuracy(A),
ConvergenceRate(A),
GradientNorm(A),
LearningRateEffectiveness(A),
ResourceEfficiency(A),
DataQuality(A),
Custom(String, A),
}
#[derive(Debug, Clone)]
pub struct PerformanceContext<A: Float + Send + Sync> {
pub learning_rate: A,
pub batch_size: usize,
pub buffer_size: usize,
pub drift_detected: bool,
pub resource_constraints: ResourceUsage,
pub time_since_adaptation: Duration,
}
pub struct PerformanceTracker<A: Float + Send + Sync + std::iter::Sum> {
config: PerformanceConfig,
performance_history: VecDeque<PerformanceSnapshot<A>>,
trend_analyzer: PerformanceTrendAnalyzer<A>,
predictor: PerformancePredictor<A>,
baseline: Option<PerformanceSnapshot<A>>,
current_context: Option<PerformanceContext<A>>,
improvement_tracker: PerformanceImprovementTracker<A>,
performance_anomaly_detector: PerformanceAnomalyDetector<A>,
}
pub struct PerformanceTrendAnalyzer<A: Float + Send + Sync> {
window_size: usize,
trends: HashMap<String, TrendData<A>>,
trend_methods: Vec<TrendMethod>,
}
#[derive(Debug, Clone)]
pub struct TrendData<A: Float + Send + Sync> {
pub slope: A,
pub correlation: A,
pub volatility: A,
pub confidence: A,
pub recent_values: VecDeque<A>,
pub last_update: Instant,
}
#[derive(Debug, Clone)]
pub enum TrendMethod {
LinearRegression,
MovingAverage { window: usize },
ExponentialSmoothing { alpha: f64 },
SeasonalDecomposition,
}
pub struct PerformancePredictor<A: Float + Send + Sync> {
prediction_methods: Vec<PredictionMethod>,
prediction_history: VecDeque<PredictionResult<A>>,
model_accuracies: HashMap<String, A>,
ensemble_weights: HashMap<String, A>,
}
#[derive(Debug, Clone)]
pub enum PredictionMethod {
Linear,
Exponential { alpha: f64, beta: f64 },
ARIMA { p: usize, d: usize, q: usize },
NeuralNetwork { hidden_layers: Vec<usize> },
Ensemble,
}
#[derive(Debug, Clone)]
pub struct PredictionResult<A: Float + Send + Sync> {
pub predicted_value: A,
pub confidence_interval: (A, A),
pub method: String,
pub steps_ahead: usize,
pub timestamp: Instant,
pub actual_value: Option<A>,
}
pub struct PerformanceImprovementTracker<A: Float + Send + Sync> {
baseline_metrics: HashMap<String, A>,
improvement_rates: HashMap<String, A>,
improvement_history: VecDeque<ImprovementEvent<A>>,
plateau_detector: PlateauDetector<A>,
}
#[derive(Debug, Clone)]
pub struct ImprovementEvent<A: Float + Send + Sync> {
pub timestamp: Instant,
pub metric_name: String,
pub improvement: A,
pub improvement_rate: A,
pub context: String,
}
pub struct PlateauDetector<A: Float + Send + Sync> {
window_size: usize,
plateau_threshold: A,
recent_values: VecDeque<A>,
is_plateau: bool,
plateau_duration: Duration,
last_significant_change: Option<Instant>,
}
pub struct PerformanceAnomalyDetector<A: Float + Send + Sync> {
threshold: A,
historical_stats: HashMap<String, MetricStatistics<A>>,
recent_anomalies: VecDeque<PerformanceAnomaly<A>>,
adaptive_threshold: bool,
}
#[derive(Debug, Clone)]
pub struct MetricStatistics<A: Float + Send + Sync> {
pub mean: A,
pub variance: A,
pub min_value: A,
pub max_value: A,
pub count: usize,
pub last_update: Instant,
}
#[derive(Debug, Clone)]
pub struct PerformanceAnomaly<A: Float + Send + Sync> {
pub timestamp: Instant,
pub metric_name: String,
pub observed_value: A,
pub expected_range: (A, A),
pub severity: AnomalySeverity,
pub anomaly_type: AnomalyType,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum AnomalySeverity {
Minor,
Moderate,
Major,
Critical,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AnomalyType {
High,
Low,
TrendChange,
Oscillation,
Degradation,
Plateau,
}
impl<A: Float + Default + Clone + std::iter::Sum + Send + Sync + std::fmt::Debug>
PerformanceTracker<A>
{
pub fn new(config: &StreamingConfig) -> Result<Self, String> {
let performance_config = config.performance_config.clone();
let trend_analyzer = PerformanceTrendAnalyzer::new(performance_config.trend_window_size);
let predictor = PerformancePredictor::new();
let improvement_tracker = PerformanceImprovementTracker::new();
let performance_anomaly_detector = PerformanceAnomalyDetector::new(2.0);
Ok(Self {
config: performance_config,
performance_history: VecDeque::with_capacity(1000),
trend_analyzer,
predictor,
baseline: None,
current_context: None,
improvement_tracker,
performance_anomaly_detector,
})
}
pub fn add_performance(&mut self, snapshot: PerformanceSnapshot<A>) -> Result<(), String> {
if self.performance_history.len() >= self.config.history_size {
self.performance_history.pop_front();
}
self.performance_history.push_back(snapshot.clone());
if self.baseline.is_none() {
self.baseline = Some(snapshot.clone());
}
if self.config.enable_trend_analysis {
self.trend_analyzer.update(&snapshot)?;
}
self.improvement_tracker.update(&snapshot)?;
let anomalies = self
.performance_anomaly_detector
.check_for_anomalies(&snapshot)?;
if !anomalies.is_empty() {
self.handle_performance_anomalies(&anomalies)?;
}
if self.config.enable_prediction {
self.predictor.update_with_actual(&snapshot)?;
}
Ok(())
}
pub fn get_recent_performance(&self, count: usize) -> Vec<PerformanceSnapshot<A>> {
self.performance_history
.iter()
.rev()
.take(count)
.cloned()
.collect()
}
pub fn get_recent_losses(&self, count: usize) -> Vec<A> {
self.performance_history
.iter()
.rev()
.take(count)
.map(|snapshot| snapshot.loss)
.collect()
}
pub fn predict_performance(
&mut self,
steps_ahead: usize,
) -> Result<PredictionResult<A>, String> {
if !self.config.enable_prediction {
return Err("Performance prediction is disabled".to_string());
}
self.predictor
.predict(steps_ahead, &self.performance_history)
}
pub fn get_performance_trends(&self) -> HashMap<String, TrendData<A>> {
self.trend_analyzer.get_current_trends()
}
pub fn apply_threshold_adaptation(&mut self, adaptation: &Adaptation<A>) -> Result<(), String> {
if adaptation.adaptation_type == AdaptationType::PerformanceThreshold {
let new_threshold = self.performance_anomaly_detector.threshold + adaptation.magnitude;
self.performance_anomaly_detector
.update_threshold(new_threshold);
}
Ok(())
}
fn handle_performance_anomalies(
&mut self,
anomalies: &[PerformanceAnomaly<A>],
) -> Result<(), String> {
for anomaly in anomalies {
match anomaly.severity {
AnomalySeverity::Critical | AnomalySeverity::Major => {
println!("Critical performance anomaly detected: {:?}", anomaly);
}
_ => {
self.performance_anomaly_detector
.recent_anomalies
.push_back(anomaly.clone());
}
}
}
Ok(())
}
pub fn reset(&mut self) -> Result<(), String> {
self.performance_history.clear();
self.baseline = None;
self.current_context = None;
self.trend_analyzer.reset();
self.predictor.reset();
self.improvement_tracker.reset();
self.performance_anomaly_detector.reset();
Ok(())
}
pub fn get_diagnostics(&self) -> PerformanceDiagnostics {
PerformanceDiagnostics {
history_size: self.performance_history.len(),
baseline_set: self.baseline.is_some(),
trends_available: !self.trend_analyzer.trends.is_empty(),
anomalies_detected: self.performance_anomaly_detector.recent_anomalies.len(),
plateau_detected: self.improvement_tracker.plateau_detector.is_plateau,
prediction_accuracy: self.predictor.get_average_accuracy(),
}
}
}
impl<A: Float + Default + Clone + Send + Sync + std::iter::Sum> PerformanceTrendAnalyzer<A> {
fn new(window_size: usize) -> Self {
Self {
window_size,
trends: HashMap::new(),
trend_methods: vec![
TrendMethod::LinearRegression,
TrendMethod::MovingAverage {
window: window_size / 2,
},
TrendMethod::ExponentialSmoothing { alpha: 0.3 },
],
}
}
fn update(&mut self, snapshot: &PerformanceSnapshot<A>) -> Result<(), String> {
self.update_metric_trend("loss", snapshot.loss)?;
if let Some(accuracy) = snapshot.accuracy {
self.update_metric_trend("accuracy", accuracy)?;
}
if let Some(convergence) = snapshot.convergence_rate {
self.update_metric_trend("convergence", convergence)?;
}
self.compute_trends()?;
Ok(())
}
fn update_metric_trend(&mut self, metric_name: &str, value: A) -> Result<(), String> {
let trend_data = self
.trends
.entry(metric_name.to_string())
.or_insert_with(|| TrendData {
slope: A::zero(),
correlation: A::zero(),
volatility: A::zero(),
confidence: A::zero(),
recent_values: VecDeque::with_capacity(self.window_size),
last_update: Instant::now(),
});
if trend_data.recent_values.len() >= self.window_size {
trend_data.recent_values.pop_front();
}
trend_data.recent_values.push_back(value);
trend_data.last_update = Instant::now();
Ok(())
}
fn compute_trends(&mut self) -> Result<(), String> {
let keys: Vec<_> = self.trends.keys().cloned().collect();
let mut computed_values = Vec::new();
for metric_name in &keys {
if let Some(trend_data) = self.trends.get(metric_name) {
if trend_data.recent_values.len() >= 3 {
let values = trend_data.recent_values.clone();
let slope = self.compute_slope(&values)?;
let correlation = self.compute_correlation(&values)?;
let volatility = self.compute_volatility(&values)?;
let confidence = self.compute_confidence(&values)?;
computed_values.push((
metric_name.clone(),
slope,
correlation,
volatility,
confidence,
));
}
}
}
for (metric_name, slope, correlation, volatility, confidence) in computed_values {
if let Some(trend_data) = self.trends.get_mut(&metric_name) {
trend_data.slope = slope;
trend_data.correlation = correlation;
trend_data.volatility = volatility;
trend_data.confidence = confidence;
}
}
Ok(())
}
fn compute_slope(&self, values: &VecDeque<A>) -> Result<A, String> {
if values.len() < 2 {
return Ok(A::zero());
}
let n = A::from(values.len()).expect("unwrap failed");
let sum_x = n * (n + A::one()) / A::from(2.0).expect("unwrap failed");
let sum_y = values.iter().cloned().sum::<A>();
let sum_xy = values
.iter()
.enumerate()
.map(|(i, &y)| A::from(i + 1).expect("unwrap failed") * y)
.sum::<A>();
let two = A::from(2.0).expect("unwrap failed");
let six = A::from(6.0).expect("unwrap failed");
let sum_x_squared = n * (n + A::one()) * (two * n + A::one()) / six;
let denominator = n * sum_x_squared - sum_x * sum_x;
if denominator == A::zero() {
return Ok(A::zero());
}
let slope = (n * sum_xy - sum_x * sum_y) / denominator;
Ok(slope)
}
fn compute_correlation(&self, values: &VecDeque<A>) -> Result<A, String> {
if values.len() < 2 {
return Ok(A::zero());
}
let n = values.len();
let time_values: Vec<A> = (1..=n)
.map(|i| A::from(i).expect("unwrap failed"))
.collect();
let value_vec: Vec<A> = values.iter().cloned().collect();
let mean_time = time_values.iter().cloned().sum::<A>() / A::from(n).expect("unwrap failed");
let mean_value = value_vec.iter().cloned().sum::<A>() / A::from(n).expect("unwrap failed");
let numerator = time_values
.iter()
.zip(value_vec.iter())
.map(|(&t, &v)| (t - mean_time) * (v - mean_value))
.sum::<A>();
let time_variance = time_values
.iter()
.map(|&t| (t - mean_time) * (t - mean_time))
.sum::<A>();
let value_variance = value_vec
.iter()
.map(|&v| (v - mean_value) * (v - mean_value))
.sum::<A>();
let denominator = (time_variance * value_variance).sqrt();
if denominator == A::zero() {
return Ok(A::zero());
}
Ok(numerator / denominator)
}
fn compute_volatility(&self, values: &VecDeque<A>) -> Result<A, String> {
if values.len() < 2 {
return Ok(A::zero());
}
let mean =
values.iter().cloned().sum::<A>() / A::from(values.len()).expect("unwrap failed");
let variance = values.iter().map(|&v| (v - mean) * (v - mean)).sum::<A>()
/ A::from(values.len()).expect("unwrap failed");
Ok(variance.sqrt())
}
fn compute_confidence(&self, values: &VecDeque<A>) -> Result<A, String> {
if values.len() < 3 {
return Ok(A::zero());
}
let slope = self.compute_slope(values)?;
let correlation = self.compute_correlation(values)?;
let confidence = correlation.abs() * (A::one() - (slope.abs() / (slope.abs() + A::one())));
Ok(confidence)
}
fn get_current_trends(&self) -> HashMap<String, TrendData<A>> {
self.trends.clone()
}
fn reset(&mut self) {
self.trends.clear();
}
}
impl<A: Float + Default + Clone + Send + Sync + std::iter::Sum> PerformancePredictor<A> {
fn new() -> Self {
Self {
prediction_methods: vec![
PredictionMethod::Linear,
PredictionMethod::Exponential {
alpha: 0.3,
beta: 0.1,
},
],
prediction_history: VecDeque::with_capacity(1000),
model_accuracies: HashMap::new(),
ensemble_weights: HashMap::new(),
}
}
fn predict(
&mut self,
steps_ahead: usize,
history: &VecDeque<PerformanceSnapshot<A>>,
) -> Result<PredictionResult<A>, String> {
if history.len() < 2 {
return Err("Insufficient history for prediction".to_string());
}
let loss_values: Vec<A> = history.iter().map(|s| s.loss).collect();
let predicted_value = self.linear_prediction(&loss_values, steps_ahead)?;
let recent_volatility = self.compute_recent_volatility(&loss_values)?;
let confidence_interval = (
predicted_value - recent_volatility,
predicted_value + recent_volatility,
);
let prediction = PredictionResult {
predicted_value,
confidence_interval,
method: "linear".to_string(),
steps_ahead,
timestamp: Instant::now(),
actual_value: None,
};
if self.prediction_history.len() >= 1000 {
self.prediction_history.pop_front();
}
self.prediction_history.push_back(prediction.clone());
Ok(prediction)
}
fn linear_prediction(&self, values: &[A], steps_ahead: usize) -> Result<A, String> {
if values.len() < 2 {
return Ok(A::zero());
}
let n = values.len();
let x1 = A::from(n - 1).expect("unwrap failed");
let y1 = values[n - 1];
let x2 = A::from(n).expect("unwrap failed");
let y2 = values[n - 1];
if n >= 3 {
let slope = (values[n - 1] - values[n - 3]) / A::from(2).expect("unwrap failed");
let predicted = values[n - 1] + slope * A::from(steps_ahead).expect("unwrap failed");
Ok(predicted)
} else {
Ok(values[n - 1])
}
}
fn exponential_prediction(&self, values: &[A], steps_ahead: usize) -> Result<A, String> {
if values.is_empty() {
return Ok(A::zero());
}
let alpha = A::from(0.3).expect("unwrap failed");
let mut forecast = values[0];
for &value in values.iter().skip(1) {
forecast = alpha * value + (A::one() - alpha) * forecast;
}
for _ in 0..steps_ahead {
forecast = forecast * A::from(0.99).expect("unwrap failed"); }
Ok(forecast)
}
fn compute_recent_volatility(&self, values: &[A]) -> Result<A, String> {
if values.len() < 2 {
return Ok(A::zero());
}
let recent_count = values.len().min(10);
let recent_values = &values[values.len() - recent_count..];
let mean = recent_values.iter().cloned().sum::<A>()
/ A::from(recent_count).expect("unwrap failed");
let variance = recent_values
.iter()
.map(|&v| (v - mean) * (v - mean))
.sum::<A>()
/ A::from(recent_count).expect("unwrap failed");
Ok(variance.sqrt())
}
fn update_with_actual(&mut self, snapshot: &PerformanceSnapshot<A>) -> Result<(), String> {
let mut updated_predictions = Vec::new();
for prediction in &mut self.prediction_history {
if prediction.actual_value.is_none() {
let time_diff = snapshot.timestamp.duration_since(prediction.timestamp);
let expected_duration = Duration::from_secs(prediction.steps_ahead as u64 * 10);
if time_diff >= expected_duration {
prediction.actual_value = Some(snapshot.loss);
updated_predictions.push(prediction.clone());
}
}
}
for prediction in &updated_predictions {
self.update_accuracy_metrics(prediction)?;
}
Ok(())
}
fn update_accuracy_metrics(&mut self, prediction: &PredictionResult<A>) -> Result<(), String> {
if let Some(actual) = prediction.actual_value {
let error = (prediction.predicted_value - actual).abs();
let relative_error = error / actual.max(A::from(1e-8).expect("unwrap failed"));
let accuracy = A::one() - relative_error.min(A::one());
let method_name = &prediction.method;
self.model_accuracies.insert(method_name.clone(), accuracy);
}
Ok(())
}
fn get_average_accuracy(&self) -> f64 {
if self.model_accuracies.is_empty() {
return 0.0;
}
let sum: A = self.model_accuracies.values().cloned().sum();
let avg = sum / A::from(self.model_accuracies.len()).expect("unwrap failed");
avg.to_f64().unwrap_or(0.0)
}
fn reset(&mut self) {
self.prediction_history.clear();
self.model_accuracies.clear();
self.ensemble_weights.clear();
}
}
impl<A: Float + Default + Clone + Sum + Send + Sync + Send + Sync>
PerformanceImprovementTracker<A>
{
fn new() -> Self {
Self {
baseline_metrics: HashMap::new(),
improvement_rates: HashMap::new(),
improvement_history: VecDeque::with_capacity(1000),
plateau_detector: PlateauDetector::new(50, A::from(0.01).expect("unwrap failed")),
}
}
fn update(&mut self, snapshot: &PerformanceSnapshot<A>) -> Result<(), String> {
if self.baseline_metrics.is_empty() {
self.baseline_metrics
.insert("loss".to_string(), snapshot.loss);
if let Some(accuracy) = snapshot.accuracy {
self.baseline_metrics
.insert("accuracy".to_string(), accuracy);
}
}
if let Some(&baseline_loss) = self.baseline_metrics.get("loss") {
if snapshot.loss < baseline_loss {
let improvement = baseline_loss - snapshot.loss;
let improvement_event = ImprovementEvent {
timestamp: snapshot.timestamp,
metric_name: "loss".to_string(),
improvement,
improvement_rate: improvement / A::from(1.0).expect("unwrap failed"), context: "optimization_step".to_string(),
};
if self.improvement_history.len() >= 1000 {
self.improvement_history.pop_front();
}
self.improvement_history.push_back(improvement_event);
self.baseline_metrics
.insert("loss".to_string(), snapshot.loss);
}
}
self.plateau_detector.update(snapshot.loss);
Ok(())
}
fn reset(&mut self) {
self.baseline_metrics.clear();
self.improvement_rates.clear();
self.improvement_history.clear();
self.plateau_detector.reset();
}
}
impl<A: Float + Default + Clone + Send + Sync + std::iter::Sum> PlateauDetector<A> {
fn new(window_size: usize, threshold: A) -> Self {
Self {
window_size,
plateau_threshold: threshold,
recent_values: VecDeque::with_capacity(window_size),
is_plateau: false,
plateau_duration: Duration::ZERO,
last_significant_change: None,
}
}
fn update(&mut self, value: A) {
if self.recent_values.len() >= self.window_size {
self.recent_values.pop_front();
}
self.recent_values.push_back(value);
if self.recent_values.len() >= self.window_size {
self.detect_plateau();
}
}
fn detect_plateau(&mut self) {
if self.recent_values.len() < 2 {
return;
}
let max_val = self.recent_values.iter().cloned().fold(A::zero(), A::max);
let min_val = self.recent_values.iter().cloned().fold(A::zero(), A::min);
let range = max_val - min_val;
let was_plateau = self.is_plateau;
self.is_plateau = range < self.plateau_threshold;
if self.is_plateau && !was_plateau {
self.plateau_duration = Duration::ZERO;
} else if self.is_plateau {
self.plateau_duration += Duration::from_secs(1); } else if !self.is_plateau {
self.last_significant_change = Some(Instant::now());
self.plateau_duration = Duration::ZERO;
}
}
fn reset(&mut self) {
self.recent_values.clear();
self.is_plateau = false;
self.plateau_duration = Duration::ZERO;
self.last_significant_change = None;
}
}
impl<A: Float + Default + Clone + Sum + Send + Sync + Send + Sync> PerformanceAnomalyDetector<A> {
fn new(threshold: f64) -> Self {
Self {
threshold: A::from(threshold).expect("unwrap failed"),
historical_stats: HashMap::new(),
recent_anomalies: VecDeque::with_capacity(100),
adaptive_threshold: true,
}
}
fn check_for_anomalies(
&mut self,
snapshot: &PerformanceSnapshot<A>,
) -> Result<Vec<PerformanceAnomaly<A>>, String> {
let mut anomalies = Vec::new();
let loss_anomaly = self.check_metric_anomaly("loss", snapshot.loss, snapshot.timestamp)?;
if let Some(anomaly) = loss_anomaly {
anomalies.push(anomaly);
}
if let Some(accuracy) = snapshot.accuracy {
let accuracy_anomaly =
self.check_metric_anomaly("accuracy", accuracy, snapshot.timestamp)?;
if let Some(anomaly) = accuracy_anomaly {
anomalies.push(anomaly);
}
}
Ok(anomalies)
}
fn check_metric_anomaly(
&mut self,
metric_name: &str,
value: A,
timestamp: Instant,
) -> Result<Option<PerformanceAnomaly<A>>, String> {
let stats = self
.historical_stats
.entry(metric_name.to_string())
.or_insert_with(|| MetricStatistics {
mean: value,
variance: A::zero(),
min_value: value,
max_value: value,
count: 0,
last_update: timestamp,
});
stats.count += 1;
let delta = value - stats.mean;
stats.mean = stats.mean + delta / A::from(stats.count).expect("unwrap failed");
let delta2 = value - stats.mean;
stats.variance = stats.variance + delta * delta2;
stats.min_value = stats.min_value.min(value);
stats.max_value = stats.max_value.max(value);
stats.last_update = timestamp;
if stats.count >= 10 {
let std_dev =
(stats.variance / A::from(stats.count - 1).expect("unwrap failed")).sqrt();
let z_score = (value - stats.mean) / std_dev.max(A::from(1e-8).expect("unwrap failed"));
if z_score.abs() > self.threshold {
let severity = if z_score.abs() > A::from(3.0).expect("unwrap failed") {
AnomalySeverity::Critical
} else if z_score.abs() > A::from(2.5).expect("unwrap failed") {
AnomalySeverity::Major
} else {
AnomalySeverity::Moderate
};
let anomaly_type = if z_score > A::zero() {
AnomalyType::High
} else {
AnomalyType::Low
};
let expected_range = (
stats.mean - self.threshold * std_dev,
stats.mean + self.threshold * std_dev,
);
let anomaly = PerformanceAnomaly {
timestamp,
metric_name: metric_name.to_string(),
observed_value: value,
expected_range,
severity,
anomaly_type,
};
return Ok(Some(anomaly));
}
}
Ok(None)
}
fn update_threshold(&mut self, new_threshold: A) {
self.threshold = new_threshold;
}
fn reset(&mut self) {
self.historical_stats.clear();
self.recent_anomalies.clear();
}
}
#[derive(Debug, Clone)]
pub struct PerformanceDiagnostics {
pub history_size: usize,
pub baseline_set: bool,
pub trends_available: bool,
pub anomalies_detected: usize,
pub plateau_detected: bool,
pub prediction_accuracy: f64,
}