use super::config::*;
use super::performance::PerformanceSnapshot;
use crate::OptimizerError as OptimError;
use scirs2_core::ndarray::{Array1, Array2};
use scirs2_core::numeric::Float;
use std::collections::{HashMap, VecDeque};
use std::fmt::Debug;
use std::time::{Duration, SystemTime};
type Result<T> = std::result::Result<T, OptimError>;
#[derive(Debug)]
pub struct PatternDetector<T: Float + Debug + Send + Sync + Debug> {
config: PatternDetectionConfig,
analyzers: Vec<PatternAnalyzer<T>>,
pattern_history: VecDeque<DetectedPattern<T>>,
pattern_library: PatternLibrary<T>,
pattern_matcher: PatternMatcher<T>,
sequence_analyzer: SequenceAnalyzer<T>,
}
#[derive(Debug)]
pub struct PatternAnalyzer<T: Float + Debug + Send + Sync + Debug> {
pattern_type: PatternType,
analysis_window: VecDeque<T>,
pattern_candidates: Vec<PatternCandidate<T>>,
detection_threshold: T,
min_length: usize,
max_length: usize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum PatternType {
LossPattern,
GradientPattern,
PerformancePattern,
LearningRatePattern,
OscillationPattern,
ConvergencePattern,
PlateauPattern,
SpikePattern,
}
#[derive(Debug, Clone)]
pub struct DetectedPattern<T: Float + Debug + Send + Sync + 'static> {
pub pattern_id: String,
pub pattern_type: PatternType,
pub pattern_data: Vec<T>,
pub characteristics: PatternCharacteristics<T>,
pub confidence: T,
pub start_index: usize,
pub end_index: usize,
pub timestamp: SystemTime,
pub significance: PatternSignificance,
pub related_patterns: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct PatternCharacteristics<T: Float + Debug + Send + Sync + 'static> {
pub length: usize,
pub amplitude: T,
pub frequency: T,
pub trend: PatternTrend,
pub regularity: T,
pub stability: T,
pub complexity: T,
pub statistics: PatternStatistics<T>,
}
#[derive(Debug, Clone, Copy)]
pub enum PatternTrend {
Increasing,
Decreasing,
Oscillating,
Stable,
Random,
Periodic,
}
#[derive(Debug, Clone, Copy)]
pub enum PatternSignificance {
Low,
Medium,
High,
Critical,
}
#[derive(Debug, Clone)]
pub struct PatternStatistics<T: Float + Debug + Send + Sync + 'static> {
pub mean: T,
pub std_dev: T,
pub variance: T,
pub skewness: T,
pub kurtosis: T,
pub autocorrelation: Vec<T>,
pub spectral_density: Vec<T>,
}
#[derive(Debug, Clone)]
pub struct PatternCandidate<T: Float + Debug + Send + Sync + 'static> {
pub data: Vec<T>,
pub start_pos: usize,
pub confidence: T,
pub template_match: Option<PatternTemplate<T>>,
pub similarity_score: T,
}
#[derive(Debug, Clone)]
pub struct PatternTemplate<T: Float + Debug + Send + Sync + 'static> {
pub name: String,
pub template_data: Vec<T>,
pub characteristics: PatternCharacteristics<T>,
pub tolerance: T,
pub usage_count: usize,
pub success_rate: T,
}
#[derive(Debug)]
pub struct PatternLibrary<T: Float + Debug + Send + Sync + Debug> {
templates: HashMap<String, PatternTemplate<T>>,
categories: HashMap<PatternType, Vec<String>>,
relationships: HashMap<String, Vec<String>>,
metadata: HashMap<String, PatternMetadata>,
}
#[derive(Debug, Clone)]
pub struct PatternMetadata {
pub created: SystemTime,
pub last_used: SystemTime,
pub usage_frequency: f64,
pub description: String,
pub tags: Vec<String>,
pub source: PatternSource,
}
#[derive(Debug, Clone)]
pub enum PatternSource {
UserDefined,
MachineLearned,
HistoricalData,
Literature,
Heuristic,
}
#[derive(Debug)]
pub struct PatternMatcher<T: Float + Debug + Send + Sync + Debug> {
algorithms: Vec<MatchingAlgorithm>,
similarity_threshold: T,
match_cache: HashMap<String, Vec<PatternMatch<T>>>,
matching_stats: MatchingStatistics<T>,
}
#[derive(Debug, Clone)]
pub enum MatchingAlgorithm {
DTW,
Euclidean,
Correlation,
FrequencyDomain,
Statistical,
MLBased,
}
#[derive(Debug, Clone)]
pub struct PatternMatch<T: Float + Debug + Send + Sync + 'static> {
pub template_name: String,
pub similarity_score: T,
pub confidence: T,
pub alignment_offset: i32,
pub scale_factor: T,
pub match_quality: MatchQuality,
}
#[derive(Debug, Clone, Copy)]
pub enum MatchQuality {
Excellent,
Good,
Fair,
Poor,
}
#[derive(Debug, Clone)]
pub struct MatchingStatistics<T: Float + Debug + Send + Sync + 'static> {
pub total_matches: usize,
pub successful_matches: usize,
pub avg_matching_time: Duration,
pub avg_similarity_score: T,
pub match_accuracy: T,
}
#[derive(Debug)]
pub struct SequenceAnalyzer<T: Float + Debug + Send + Sync + Debug> {
sequence_buffer: VecDeque<SequenceElement<T>>,
sequence_patterns: Vec<SequencePattern<T>>,
temporal_windows: Vec<Duration>,
anomaly_detector: SequenceAnomalyDetector<T>,
}
#[derive(Debug, Clone)]
pub struct SequenceElement<T: Float + Debug + Send + Sync + 'static> {
pub timestamp: SystemTime,
pub value: T,
pub element_type: String,
pub context: HashMap<String, String>,
}
#[derive(Debug, Clone)]
pub struct SequencePattern<T: Float + Debug + Send + Sync + 'static> {
pub name: String,
pub elements: Vec<SequenceElement<T>>,
pub duration: Duration,
pub repetition_count: usize,
pub confidence: T,
pub predictive_power: T,
}
#[derive(Debug)]
pub struct SequenceAnomalyDetector<T: Float + Debug + Send + Sync + Debug> {
normal_patterns: Vec<SequencePattern<T>>,
anomaly_threshold: T,
detection_window: Duration,
anomaly_history: VecDeque<SequenceAnomaly<T>>,
}
#[derive(Debug, Clone)]
pub struct SequenceAnomaly<T: Float + Debug + Send + Sync + 'static> {
pub timestamp: SystemTime,
pub anomaly_score: T,
pub anomaly_type: AnomalyType,
pub context: SequenceElement<T>,
pub explanation: String,
}
#[derive(Debug, Clone, Copy)]
pub enum AnomalyType {
OutOfPattern,
UnexpectedValue,
TimingAnomaly,
FrequencyAnomaly,
StructuralAnomaly,
}
impl<T: Float + Debug + Send + Sync + 'static + Default + Clone> PatternDetector<T> {
pub fn new(config: PatternDetectionConfig) -> Result<Self> {
let mut analyzers = Vec::new();
if config.enable_loss_patterns {
analyzers.push(PatternAnalyzer::new(
PatternType::LossPattern,
config.detection_sensitivity,
config.min_pattern_length,
config.max_pattern_length,
)?);
}
if config.enable_gradient_patterns {
analyzers.push(PatternAnalyzer::new(
PatternType::GradientPattern,
config.detection_sensitivity,
config.min_pattern_length,
config.max_pattern_length,
)?);
}
if config.enable_performance_patterns {
analyzers.push(PatternAnalyzer::new(
PatternType::PerformancePattern,
config.detection_sensitivity,
config.min_pattern_length,
config.max_pattern_length,
)?);
}
Ok(Self {
config,
analyzers,
pattern_history: VecDeque::new(),
pattern_library: PatternLibrary::new()?,
pattern_matcher: PatternMatcher::new(scirs2_core::numeric::NumCast::from(config.pattern_tolerance).unwrap_or_else(|| T::zero()))?,
sequence_analyzer: SequenceAnalyzer::new()?,
})
}
pub fn detect_patterns(&mut self, snapshot: &PerformanceSnapshot<T>) -> Result<Vec<DetectedPattern<T>>> {
let mut detected_patterns = Vec::new();
for analyzer in &mut self.analyzers {
let value = match analyzer.pattern_type {
PatternType::LossPattern => snapshot.loss,
PatternType::GradientPattern => snapshot.gradient_norm.unwrap_or(T::zero()),
PatternType::PerformancePattern => snapshot.accuracy.unwrap_or(T::zero()),
_ => T::zero(),
};
if let Some(pattern) = analyzer.analyze_value(value, snapshot.step)? {
detected_patterns.push(pattern);
}
}
for pattern in &detected_patterns {
if self.pattern_history.len() >= 1000 {
self.pattern_history.pop_front();
}
self.pattern_history.push_back(pattern.clone());
self.pattern_library.update_with_pattern(pattern)?;
}
Ok(detected_patterns)
}
pub fn get_pattern_history(&self) -> &VecDeque<DetectedPattern<T>> {
&self.pattern_history
}
pub fn find_similar_patterns(&self, pattern: &DetectedPattern<T>) -> Result<Vec<PatternMatch<T>>> {
self.pattern_matcher.find_matches(&pattern.pattern_data)
}
pub fn predict_next_values(&self, steps_ahead: usize) -> Result<Vec<T>> {
if self.pattern_history.is_empty() {
return Ok(vec![T::zero(); steps_ahead]);
}
let recent_pattern = self.pattern_history.back().expect("unwrap failed");
let mut predictions = Vec::new();
for i in 0..steps_ahead {
let pattern_index = i % recent_pattern.pattern_data.len();
let predicted_value = recent_pattern.pattern_data[pattern_index];
predictions.push(predicted_value);
}
Ok(predictions)
}
pub fn analyze_sequence_patterns(&mut self, sequence: &[SequenceElement<T>]) -> Result<Vec<SequencePattern<T>>> {
self.sequence_analyzer.analyze_sequence(sequence)
}
}
impl<T: Float + Debug + Send + Sync + 'static + Default + Clone> PatternAnalyzer<T> {
pub fn new(
pattern_type: PatternType,
detection_threshold: f64,
min_length: usize,
max_length: usize,
) -> Result<Self> {
Ok(Self {
pattern_type,
analysis_window: VecDeque::new(),
pattern_candidates: Vec::new(),
detection_threshold: scirs2_core::numeric::NumCast::from(detection_threshold).unwrap_or_else(|| T::zero()),
min_length,
max_length,
})
}
pub fn analyze_value(&mut self, value: T, step: usize) -> Result<Option<DetectedPattern<T>>> {
self.analysis_window.push_back(value);
if self.analysis_window.len() > self.max_length * 2 {
self.analysis_window.pop_front();
}
if self.analysis_window.len() < self.min_length {
return Ok(None);
}
if let Some(pattern) = self.detect_pattern_in_window(step)? {
return Ok(Some(pattern));
}
Ok(None)
}
fn detect_pattern_in_window(&self, step: usize) -> Result<Option<DetectedPattern<T>>> {
let data: Vec<T> = self.analysis_window.iter().cloned().collect();
match self.pattern_type {
PatternType::LossPattern => self.detect_loss_pattern(&data, step),
PatternType::GradientPattern => self.detect_gradient_pattern(&data, step),
PatternType::PerformancePattern => self.detect_performance_pattern(&data, step),
PatternType::OscillationPattern => self.detect_oscillation_pattern(&data, step),
PatternType::PlateauPattern => self.detect_plateau_pattern(&data, step),
_ => Ok(None),
}
}
fn detect_loss_pattern(&self, data: &[T], step: usize) -> Result<Option<DetectedPattern<T>>> {
if data.len() < self.min_length {
return Ok(None);
}
let trend = self.calculate_trend(data)?;
if trend < -self.detection_threshold {
let characteristics = self.calculate_characteristics(data)?;
let confidence = (-trend).min(T::one());
return Ok(Some(DetectedPattern {
pattern_id: format!("loss_decreasing_{}", step),
pattern_type: self.pattern_type,
pattern_data: data.to_vec(),
characteristics,
confidence,
start_index: step.saturating_sub(data.len()),
end_index: step,
timestamp: SystemTime::now(),
significance: PatternSignificance::High,
related_patterns: Vec::new(),
}));
}
Ok(None)
}
fn detect_gradient_pattern(&self, data: &[T], step: usize) -> Result<Option<DetectedPattern<T>>> {
if data.len() < self.min_length {
return Ok(None);
}
let trend = self.calculate_trend(data)?;
if trend < -self.detection_threshold {
let characteristics = self.calculate_characteristics(data)?;
let confidence = (-trend).min(T::one());
return Ok(Some(DetectedPattern {
pattern_id: format!("gradient_decreasing_{}", step),
pattern_type: self.pattern_type,
pattern_data: data.to_vec(),
characteristics,
confidence,
start_index: step.saturating_sub(data.len()),
end_index: step,
timestamp: SystemTime::now(),
significance: PatternSignificance::Medium,
related_patterns: Vec::new(),
}));
}
Ok(None)
}
fn detect_performance_pattern(&self, data: &[T], step: usize) -> Result<Option<DetectedPattern<T>>> {
if data.len() < self.min_length {
return Ok(None);
}
let trend = self.calculate_trend(data)?;
if trend > self.detection_threshold {
let characteristics = self.calculate_characteristics(data)?;
let confidence = trend.min(T::one());
return Ok(Some(DetectedPattern {
pattern_id: format!("performance_improving_{}", step),
pattern_type: self.pattern_type,
pattern_data: data.to_vec(),
characteristics,
confidence,
start_index: step.saturating_sub(data.len()),
end_index: step,
timestamp: SystemTime::now(),
significance: PatternSignificance::High,
related_patterns: Vec::new(),
}));
}
Ok(None)
}
fn detect_oscillation_pattern(&self, data: &[T], step: usize) -> Result<Option<DetectedPattern<T>>> {
if data.len() < self.min_length {
return Ok(None);
}
let mut direction_changes = 0;
for window in data.windows(3) {
let trend1 = window[1] - window[0];
let trend2 = window[2] - window[1];
if trend1 * trend2 < T::zero() {
direction_changes += 1;
}
}
let oscillation_ratio = scirs2_core::numeric::NumCast::from(direction_changes).unwrap_or_else(|| T::zero()) / T::from(data.len().saturating_sub(2)).expect("unwrap failed");
if oscillation_ratio > self.detection_threshold {
let characteristics = self.calculate_characteristics(data)?;
let confidence = oscillation_ratio.min(T::one());
return Ok(Some(DetectedPattern {
pattern_id: format!("oscillation_{}", step),
pattern_type: PatternType::OscillationPattern,
pattern_data: data.to_vec(),
characteristics,
confidence,
start_index: step.saturating_sub(data.len()),
end_index: step,
timestamp: SystemTime::now(),
significance: PatternSignificance::Medium,
related_patterns: Vec::new(),
}));
}
Ok(None)
}
fn detect_plateau_pattern(&self, data: &[T], step: usize) -> Result<Option<DetectedPattern<T>>> {
if data.len() < self.min_length {
return Ok(None);
}
let mean = data.iter().fold(T::zero(), |acc, &x| acc + x) / T::from(data.len()).expect("unwrap failed");
let variance = data.iter()
.map(|&x| (x - mean) * (x - mean))
.fold(T::zero(), |acc, x| acc + x) / T::from(data.len()).expect("unwrap failed");
let stability = T::one() / (T::one() + variance.sqrt());
if stability > self.detection_threshold {
let characteristics = self.calculate_characteristics(data)?;
let confidence = stability;
return Ok(Some(DetectedPattern {
pattern_id: format!("plateau_{}", step),
pattern_type: PatternType::PlateauPattern,
pattern_data: data.to_vec(),
characteristics,
confidence,
start_index: step.saturating_sub(data.len()),
end_index: step,
timestamp: SystemTime::now(),
significance: PatternSignificance::Low,
related_patterns: Vec::new(),
}));
}
Ok(None)
}
fn calculate_trend(&self, data: &[T]) -> Result<T> {
if data.len() < 2 {
return Ok(T::zero());
}
let n = T::from(data.len()).expect("unwrap failed");
let sum_x = (0..data.len()).map(|i| scirs2_core::numeric::NumCast::from(i).unwrap_or_else(|| T::zero())).fold(T::zero(), |acc, x| acc + x);
let sum_y = data.iter().fold(T::zero(), |acc, &y| acc + y);
let sum_xy = data.iter().enumerate()
.map(|(i, &y)| scirs2_core::numeric::NumCast::from(i).unwrap_or_else(|| T::zero()) * y)
.fold(T::zero(), |acc, xy| acc + xy);
let sum_x2 = (0..data.len())
.map(|i| scirs2_core::numeric::NumCast::from(i).unwrap_or_else(|| T::zero()) * scirs2_core::numeric::NumCast::from(i).unwrap_or_else(|| T::zero()))
.fold(T::zero(), |acc, x2| acc + x2);
let numerator = n * sum_xy - sum_x * sum_y;
let denominator = n * sum_x2 - sum_x * sum_x;
if denominator == T::zero() {
Ok(T::zero())
} else {
Ok(numerator / denominator)
}
}
fn calculate_characteristics(&self, data: &[T]) -> Result<PatternCharacteristics<T>> {
let length = data.len();
let mean = data.iter().fold(T::zero(), |acc, &x| acc + x) / scirs2_core::numeric::NumCast::from(length).unwrap_or_else(|| T::zero());
let variance = data.iter()
.map(|&x| (x - mean) * (x - mean))
.fold(T::zero(), |acc, x| acc + x) / scirs2_core::numeric::NumCast::from(length).unwrap_or_else(|| T::zero());
let std_dev = variance.sqrt();
let max_val = data.iter().fold(T::zero(), |acc, &x| if x > acc { x } else { acc });
let min_val = data.iter().fold(T::one(), |acc, &x| if x < acc { x } else { acc });
let amplitude = max_val - min_val;
let autocorr = self.calculate_autocorrelation(data, 5)?;
let frequency = T::one() / scirs2_core::numeric::NumCast::from(length).unwrap_or_else(|| T::zero());
let trend_val = self.calculate_trend(data)?;
let trend = if trend_val > scirs2_core::numeric::NumCast::from(0.01).unwrap_or_else(|| T::zero()) {
PatternTrend::Increasing
} else if trend_val < scirs2_core::numeric::NumCast::from(-0.01).unwrap_or_else(|| T::zero()) {
PatternTrend::Decreasing
} else {
PatternTrend::Stable
};
let regularity = T::one() / (T::one() + variance);
let stability = regularity;
let mut direction_changes = 0;
for window in data.windows(2) {
if (window[1] - window[0]).abs() > std_dev / scirs2_core::numeric::NumCast::from(2.0).unwrap_or_else(|| T::zero()) {
direction_changes += 1;
}
}
let complexity = scirs2_core::numeric::NumCast::from(direction_changes).unwrap_or_else(|| T::zero()) / scirs2_core::numeric::NumCast::from(length).unwrap_or_else(|| T::zero());
Ok(PatternCharacteristics {
length,
amplitude,
frequency,
trend,
regularity,
stability,
complexity,
statistics: PatternStatistics {
mean,
std_dev,
variance,
skewness: T::zero(), kurtosis: T::zero(), autocorrelation: autocorr,
spectral_density: Vec::new(), },
})
}
fn calculate_autocorrelation(&self, data: &[T], max_lag: usize) -> Result<Vec<T>> {
let mut autocorr = Vec::new();
for lag in 0..=max_lag.min(data.len() / 2) {
if lag >= data.len() {
autocorr.push(T::zero());
continue;
}
let mut sum = T::zero();
let mut count = 0;
for i in 0..(data.len() - lag) {
sum = sum + data[i] * data[i + lag];
count += 1;
}
autocorr.push(if count > 0 { sum / scirs2_core::numeric::NumCast::from(count).unwrap_or_else(|| T::zero()) } else { T::zero() });
}
Ok(autocorr)
}
}
impl<T: Float + Debug + Send + Sync + 'static + Default + Clone> PatternLibrary<T> {
pub fn new() -> Result<Self> {
Ok(Self {
templates: HashMap::new(),
categories: HashMap::new(),
relationships: HashMap::new(),
metadata: HashMap::new(),
})
}
pub fn add_template(&mut self, template: PatternTemplate<T>) -> Result<()> {
let template_name = template.name.clone();
self.templates.insert(template_name.clone(), template.clone());
let metadata = PatternMetadata {
created: SystemTime::now(),
last_used: SystemTime::now(),
usage_frequency: 0.0,
description: format!("Pattern template: {}", template_name),
tags: Vec::new(),
source: PatternSource::UserDefined,
};
self.metadata.insert(template_name, metadata);
Ok(())
}
pub fn update_with_pattern(&mut self, pattern: &DetectedPattern<T>) -> Result<()> {
for (template_name, template) in &mut self.templates {
if self.patterns_similar(&pattern.pattern_data, &template.template_data)? {
template.usage_count += 1;
if let Some(metadata) = self.metadata.get_mut(template_name) {
metadata.last_used = SystemTime::now();
metadata.usage_frequency += 1.0;
}
}
}
Ok(())
}
fn patterns_similar(&self, pattern1: &[T], pattern2: &[T]) -> Result<bool> {
if pattern1.len() != pattern2.len() {
return Ok(false);
}
let mut sum_squared_diff = T::zero();
for (a, b) in pattern1.iter().zip(pattern2.iter()) {
let diff = *a - *b;
sum_squared_diff = sum_squared_diff + diff * diff;
}
let rmse = (sum_squared_diff / T::from(pattern1.len()).expect("unwrap failed")).sqrt();
Ok(rmse < scirs2_core::numeric::NumCast::from(0.1).unwrap_or_else(|| T::zero())) }
}
impl<T: Float + Debug + Send + Sync + 'static + Default + Clone> PatternMatcher<T> {
pub fn new(similarity_threshold: T) -> Result<Self> {
Ok(Self {
algorithms: vec![
MatchingAlgorithm::Correlation,
MatchingAlgorithm::Euclidean,
],
similarity_threshold,
match_cache: HashMap::new(),
matching_stats: MatchingStatistics::default(),
})
}
pub fn find_matches(&self, pattern_data: &[T]) -> Result<Vec<PatternMatch<T>>> {
let mut matches = Vec::new();
let match_result = PatternMatch {
template_name: "example_template".to_string(),
similarity_score: scirs2_core::numeric::NumCast::from(0.8).unwrap_or_else(|| T::zero()),
confidence: scirs2_core::numeric::NumCast::from(0.75).unwrap_or_else(|| T::zero()),
alignment_offset: 0,
scale_factor: T::one(),
match_quality: MatchQuality::Good,
};
matches.push(match_result);
Ok(matches)
}
}
impl<T: Float + Debug + Send + Sync + 'static + Default + Clone> SequenceAnalyzer<T> {
pub fn new() -> Result<Self> {
Ok(Self {
sequence_buffer: VecDeque::new(),
sequence_patterns: Vec::new(),
temporal_windows: vec![
Duration::from_secs(60),
Duration::from_secs(300),
Duration::from_secs(900),
],
anomaly_detector: SequenceAnomalyDetector::new()?,
})
}
pub fn analyze_sequence(&mut self, sequence: &[SequenceElement<T>]) -> Result<Vec<SequencePattern<T>>> {
let mut patterns = Vec::new();
if sequence.len() >= 3 {
let pattern = SequencePattern {
name: format!("sequence_pattern_{}", SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).expect("unwrap failed").as_secs()),
elements: sequence.to_vec(),
duration: Duration::from_secs(sequence.len() as u64),
repetition_count: 1,
confidence: scirs2_core::numeric::NumCast::from(0.7).unwrap_or_else(|| T::zero()),
predictive_power: scirs2_core::numeric::NumCast::from(0.6).unwrap_or_else(|| T::zero()),
};
patterns.push(pattern);
}
Ok(patterns)
}
}
impl<T: Float + Debug + Send + Sync + 'static + Default + Clone> SequenceAnomalyDetector<T> {
pub fn new() -> Result<Self> {
Ok(Self {
normal_patterns: Vec::new(),
anomaly_threshold: scirs2_core::numeric::NumCast::from(0.8).unwrap_or_else(|| T::zero()),
detection_window: Duration::from_secs(300),
anomaly_history: VecDeque::new(),
})
}
pub fn detect_anomalies(&mut self, element: &SequenceElement<T>) -> Result<Option<SequenceAnomaly<T>>> {
if element.value > scirs2_core::numeric::NumCast::from(10.0).unwrap_or_else(|| T::zero()) {
let anomaly = SequenceAnomaly {
timestamp: SystemTime::now(),
anomaly_score: scirs2_core::numeric::NumCast::from(0.9).unwrap_or_else(|| T::zero()),
anomaly_type: AnomalyType::UnexpectedValue,
context: element.clone(),
explanation: "Value exceeds expected range".to_string(),
};
self.anomaly_history.push_back(anomaly.clone());
if self.anomaly_history.len() > 1000 {
self.anomaly_history.pop_front();
}
return Ok(Some(anomaly));
}
Ok(None)
}
}
impl<T: Float + Debug + Send + Sync + 'static> Default for MatchingStatistics<T> {
fn default() -> Self {
Self {
total_matches: 0,
successful_matches: 0,
avg_matching_time: Duration::from_millis(10),
avg_similarity_score: T::from(0.5).unwrap_or(T::zero()),
match_accuracy: T::from(0.8).unwrap_or(T::zero()),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use super::super::performance::*;
#[test]
fn test_pattern_detector_creation() {
let config = PatternDetectionConfig::default();
let detector = PatternDetector::<f32>::new(config);
assert!(detector.is_ok());
}
#[test]
fn test_pattern_analyzer_creation() {
let analyzer = PatternAnalyzer::<f32>::new(
PatternType::LossPattern,
0.5,
5,
20
);
assert!(analyzer.is_ok());
}
#[test]
fn test_trend_calculation() {
let analyzer = PatternAnalyzer::<f32>::new(
PatternType::LossPattern,
0.5,
5,
20
).expect("unwrap failed");
let data = vec![5.0, 4.0, 3.0, 2.0, 1.0]; let trend = analyzer.calculate_trend(&data).expect("unwrap failed");
assert!(trend < 0.0); }
#[test]
fn test_pattern_characteristics_calculation() {
let analyzer = PatternAnalyzer::<f32>::new(
PatternType::LossPattern,
0.5,
5,
20
).expect("unwrap failed");
let data = vec![1.0, 2.0, 3.0, 2.0, 1.0];
let characteristics = analyzer.calculate_characteristics(&data).expect("unwrap failed");
assert_eq!(characteristics.length, 5);
assert!(characteristics.amplitude > 0.0);
assert!(characteristics.regularity > 0.0);
}
#[test]
fn test_autocorrelation_calculation() {
let analyzer = PatternAnalyzer::<f32>::new(
PatternType::LossPattern,
0.5,
5,
20
).expect("unwrap failed");
let data = vec![1.0, 2.0, 1.0, 2.0, 1.0]; let autocorr = analyzer.calculate_autocorrelation(&data, 3).expect("unwrap failed");
assert!(!autocorr.is_empty());
}
#[test]
fn test_pattern_library() {
let mut library = PatternLibrary::<f32>::new().expect("unwrap failed");
let template = PatternTemplate {
name: "test_template".to_string(),
template_data: vec![1.0, 2.0, 3.0],
characteristics: PatternCharacteristics {
length: 3,
amplitude: 2.0,
frequency: 0.5,
trend: PatternTrend::Increasing,
regularity: 0.8,
stability: 0.9,
complexity: 0.3,
statistics: PatternStatistics {
mean: 2.0,
std_dev: 0.8,
variance: 0.67,
skewness: 0.0,
kurtosis: 0.0,
autocorrelation: vec![1.0, 0.5, 0.0],
spectral_density: Vec::new(),
},
},
tolerance: 0.1,
usage_count: 0,
success_rate: 0.0,
};
let result = library.add_template(template);
assert!(result.is_ok());
assert_eq!(library.templates.len(), 1);
}
#[test]
fn test_sequence_analyzer() {
let mut analyzer = SequenceAnalyzer::<f32>::new().expect("unwrap failed");
let sequence = vec![
SequenceElement {
timestamp: SystemTime::now(),
value: 1.0,
element_type: "test".to_string(),
context: HashMap::new(),
},
SequenceElement {
timestamp: SystemTime::now(),
value: 2.0,
element_type: "test".to_string(),
context: HashMap::new(),
},
SequenceElement {
timestamp: SystemTime::now(),
value: 3.0,
element_type: "test".to_string(),
context: HashMap::new(),
},
];
let patterns = analyzer.analyze_sequence(&sequence).expect("unwrap failed");
assert!(!patterns.is_empty());
}
}