1use crate::transforms::Transform;
16use torsh_core::error::Result;
17use torsh_core::error::TorshError;
18
19#[cfg(not(feature = "std"))]
20use alloc::{boxed::Box, string::String, vec::Vec};
21
22use std::collections::HashMap;
23use std::sync::{Arc, RwLock};
24use std::time::Instant;
25
26#[cfg(feature = "std")]
27use scirs2_core::random::thread_rng;
28
29#[cfg(not(feature = "std"))]
30use scirs2_core::random::thread_rng;
31
32pub struct OnlineAugmentationEngine<T> {
34 transform_pipeline: Arc<dyn Transform<T, Output = T> + Send + Sync>,
35 cache: Arc<RwLock<HashMap<String, T>>>,
36 cache_enabled: bool,
37 max_cache_size: usize,
38 stats: Arc<RwLock<AugmentationStats>>,
39}
40
41#[derive(Debug, Clone)]
43pub struct AugmentationStats {
44 pub total_transforms: u64,
45 pub cache_hits: u64,
46 pub cache_misses: u64,
47 pub total_time_ms: f64,
48 pub average_time_ms: f64,
49}
50
51impl Default for AugmentationStats {
52 fn default() -> Self {
53 Self {
54 total_transforms: 0,
55 cache_hits: 0,
56 cache_misses: 0,
57 total_time_ms: 0.0,
58 average_time_ms: 0.0,
59 }
60 }
61}
62
63impl<T: Clone + Send + Sync + 'static> OnlineAugmentationEngine<T> {
64 pub fn new<P: Transform<T, Output = T> + Send + Sync + 'static>(pipeline: P) -> Self {
66 Self {
67 transform_pipeline: Arc::new(pipeline),
68 cache: Arc::new(RwLock::new(HashMap::new())),
69 cache_enabled: false,
70 max_cache_size: 1000,
71 stats: Arc::new(RwLock::new(AugmentationStats::default())),
72 }
73 }
74
75 pub fn with_cache(mut self, max_cache_size: usize) -> Self {
77 self.cache_enabled = true;
78 self.max_cache_size = max_cache_size;
79 self
80 }
81
82 pub fn apply(&self, input: T, cache_key: Option<&str>) -> Result<T> {
84 let start_time = Instant::now();
85
86 if self.cache_enabled {
88 if let Some(key) = cache_key {
89 let cache = self.cache.read().expect("lock should not be poisoned");
90 if let Some(cached_result) = cache.get(key) {
91 self.update_stats(start_time, true);
92 return Ok(cached_result.clone());
93 }
94 }
95 }
96
97 let result = self.transform_pipeline.transform(input)?;
99
100 if self.cache_enabled {
102 if let Some(key) = cache_key {
103 let mut cache = self.cache.write().expect("lock should not be poisoned");
104 if cache.len() < self.max_cache_size {
105 cache.insert(key.to_string(), result.clone());
106 }
107 }
108 }
109
110 self.update_stats(start_time, false);
111 Ok(result)
112 }
113
114 pub fn apply_uncached(&self, input: T) -> Result<T> {
116 let start_time = Instant::now();
117 let result = self.transform_pipeline.transform(input)?;
118 self.update_stats(start_time, false);
119 Ok(result)
120 }
121
122 pub fn clear_cache(&self) {
124 if self.cache_enabled {
125 self.cache
126 .write()
127 .expect("lock should not be poisoned")
128 .clear();
129 }
130 }
131
132 pub fn cache_size(&self) -> usize {
134 if self.cache_enabled {
135 self.cache
136 .read()
137 .expect("lock should not be poisoned")
138 .len()
139 } else {
140 0
141 }
142 }
143
144 pub fn stats(&self) -> AugmentationStats {
146 self.stats
147 .read()
148 .expect("lock should not be poisoned")
149 .clone()
150 }
151
152 pub fn reset_stats(&self) {
154 *self.stats.write().expect("lock should not be poisoned") = AugmentationStats::default();
155 }
156
157 fn update_stats(&self, start_time: Instant, was_cache_hit: bool) {
158 let duration = start_time.elapsed().as_secs_f64() * 1000.0;
159 let mut stats = self.stats.write().expect("lock should not be poisoned");
160
161 stats.total_transforms += 1;
162 stats.total_time_ms += duration;
163 stats.average_time_ms = stats.total_time_ms / stats.total_transforms as f64;
164
165 if was_cache_hit {
166 stats.cache_hits += 1;
167 } else {
168 stats.cache_misses += 1;
169 }
170 }
171}
172
173pub struct DynamicAugmentationStrategy<T> {
175 strategies: Vec<StrategyConfig<T>>,
176 current_epoch: usize,
177 total_epochs: usize,
178}
179
180pub struct StrategyConfig<T> {
181 epoch_range: (usize, usize),
182 pipeline: Arc<dyn Transform<T, Output = T> + Send + Sync>,
183 weight: f32,
184}
185
186impl<T: Clone + Send + Sync + 'static> DynamicAugmentationStrategy<T> {
187 pub fn new(total_epochs: usize) -> Self {
189 Self {
190 strategies: Vec::new(),
191 current_epoch: 0,
192 total_epochs,
193 }
194 }
195
196 pub fn add_strategy<P: Transform<T, Output = T> + Send + Sync + 'static>(
198 mut self,
199 epoch_start: usize,
200 epoch_end: usize,
201 pipeline: P,
202 weight: f32,
203 ) -> Self {
204 self.strategies.push(StrategyConfig {
205 epoch_range: (epoch_start, epoch_end),
206 pipeline: Arc::new(pipeline),
207 weight,
208 });
209 self
210 }
211
212 pub fn set_epoch(&mut self, epoch: usize) {
214 self.current_epoch = epoch;
215 }
216
217 fn get_active_strategies(&self) -> Vec<&StrategyConfig<T>> {
219 self.strategies
220 .iter()
221 .filter(|config| {
222 self.current_epoch >= config.epoch_range.0
223 && self.current_epoch <= config.epoch_range.1
224 })
225 .collect()
226 }
227
228 pub fn get_progress(&self) -> f32 {
230 if self.total_epochs == 0 {
231 0.0
232 } else {
233 (self.current_epoch as f32 / self.total_epochs as f32).min(1.0)
234 }
235 }
236
237 pub fn apply(&self, input: T) -> Result<T> {
239 let active_strategies = self.get_active_strategies();
240
241 if active_strategies.is_empty() {
242 return Ok(input);
243 }
244
245 let total_weight: f32 = active_strategies.iter().map(|s| s.weight).sum();
247 if total_weight == 0.0 {
248 return Ok(input);
249 }
250
251 let mut rng = thread_rng();
252 let random_value = rng.random::<f32>() * total_weight;
253 let mut cumulative_weight = 0.0;
254
255 for strategy in &active_strategies {
256 cumulative_weight += strategy.weight;
257 if random_value <= cumulative_weight {
258 return strategy.pipeline.transform(input);
259 }
260 }
261
262 if let Some(last_strategy) = active_strategies.last() {
264 last_strategy.pipeline.transform(input)
265 } else {
266 Ok(input)
267 }
268 }
269}
270
271#[derive(Clone)]
273pub struct ProgressiveAugmentation<T> {
274 light_pipeline: Arc<dyn Transform<T, Output = T> + Send + Sync>,
275 medium_pipeline: Arc<dyn Transform<T, Output = T> + Send + Sync>,
276 heavy_pipeline: Arc<dyn Transform<T, Output = T> + Send + Sync>,
277 current_epoch: usize,
278 total_epochs: usize,
279 progression_mode: ProgressionMode,
280}
281
282#[derive(Clone, Copy)]
283pub enum ProgressionMode {
284 Linear,
285 Exponential,
286 StepWise,
287}
288
289impl<T: Clone + Send + Sync + 'static> ProgressiveAugmentation<T> {
290 pub fn new<L, M, H>(light: L, medium: M, heavy: H, total_epochs: usize) -> Self
292 where
293 L: Transform<T, Output = T> + Send + Sync + 'static,
294 M: Transform<T, Output = T> + Send + Sync + 'static,
295 H: Transform<T, Output = T> + Send + Sync + 'static,
296 {
297 Self {
298 light_pipeline: Arc::new(light),
299 medium_pipeline: Arc::new(medium),
300 heavy_pipeline: Arc::new(heavy),
301 current_epoch: 0,
302 total_epochs,
303 progression_mode: ProgressionMode::Linear,
304 }
305 }
306
307 pub fn with_progression_mode(mut self, mode: ProgressionMode) -> Self {
309 self.progression_mode = mode;
310 self
311 }
312
313 pub fn set_epoch(&mut self, epoch: usize) {
315 self.current_epoch = epoch;
316 }
317
318 fn calculate_intensity(&self) -> f32 {
320 if self.total_epochs == 0 {
321 return 0.0;
322 }
323
324 let progress = self.current_epoch as f32 / self.total_epochs as f32;
325 let progress = progress.min(1.0);
326
327 match self.progression_mode {
328 ProgressionMode::Linear => progress,
329 ProgressionMode::Exponential => progress * progress,
330 ProgressionMode::StepWise => {
331 if progress < 0.33 {
332 0.0
333 } else if progress < 0.66 {
334 0.5
335 } else {
336 1.0
337 }
338 }
339 }
340 }
341
342 pub fn apply(&self, input: T) -> Result<T> {
344 let intensity = self.calculate_intensity();
345
346 if intensity < 0.33 {
347 self.light_pipeline.transform(input)
348 } else if intensity < 0.66 {
349 self.medium_pipeline.transform(input)
350 } else {
351 self.heavy_pipeline.transform(input)
352 }
353 }
354}
355
356pub struct AdaptiveAugmentation<T> {
358 pipelines: Vec<(Arc<dyn Transform<T, Output = T> + Send + Sync>, f32)>, performance_history: Vec<f32>,
360 target_performance: f32,
361 adaptation_rate: f32,
362 current_intensity: f32,
363 min_intensity: f32,
364 max_intensity: f32,
365}
366
367impl<T: Clone + Send + Sync + 'static> AdaptiveAugmentation<T> {
368 pub fn new(target_performance: f32) -> Self {
370 Self {
371 pipelines: Vec::new(),
372 performance_history: Vec::new(),
373 target_performance,
374 adaptation_rate: 0.1,
375 current_intensity: 0.5,
376 min_intensity: 0.0,
377 max_intensity: 1.0,
378 }
379 }
380
381 pub fn add_pipeline<P: Transform<T, Output = T> + Send + Sync + 'static>(
383 mut self,
384 pipeline: P,
385 intensity: f32,
386 ) -> Self {
387 self.pipelines.push((Arc::new(pipeline), intensity));
388 self
389 }
390
391 pub fn with_adaptation_params(
393 mut self,
394 adaptation_rate: f32,
395 min_intensity: f32,
396 max_intensity: f32,
397 ) -> Self {
398 self.adaptation_rate = adaptation_rate;
399 self.min_intensity = min_intensity;
400 self.max_intensity = max_intensity;
401 self
402 }
403
404 pub fn update_performance(&mut self, performance: f32) {
406 self.performance_history.push(performance);
407
408 if self.performance_history.len() > 10 {
410 self.performance_history.remove(0);
411 }
412
413 if performance < self.target_performance {
415 self.current_intensity -= self.adaptation_rate;
417 } else {
418 self.current_intensity += self.adaptation_rate;
420 }
421
422 self.current_intensity = self
424 .current_intensity
425 .max(self.min_intensity)
426 .min(self.max_intensity);
427 }
428
429 pub fn apply(&self, input: T) -> Result<T> {
431 if self.pipelines.is_empty() {
432 return Ok(input);
433 }
434
435 let mut best_pipeline = &self.pipelines[0].0;
437 let mut best_distance = (self.pipelines[0].1 - self.current_intensity).abs();
438
439 for (pipeline, intensity) in &self.pipelines {
440 let distance = (intensity - self.current_intensity).abs();
441 if distance < best_distance {
442 best_distance = distance;
443 best_pipeline = pipeline;
444 }
445 }
446
447 best_pipeline.transform(input)
448 }
449
450 pub fn current_intensity(&self) -> f32 {
452 self.current_intensity
453 }
454
455 pub fn recent_performance(&self) -> Option<f32> {
457 if self.performance_history.is_empty() {
458 None
459 } else {
460 let sum: f32 = self.performance_history.iter().sum();
461 Some(sum / self.performance_history.len() as f32)
462 }
463 }
464}
465
466pub struct AugmentationQueue<T> {
469 tasks: Arc<RwLock<Vec<AugmentationTask<T>>>>,
470 engine: Arc<OnlineAugmentationEngine<T>>,
471 max_queue_size: usize,
472}
473
474#[allow(dead_code)]
476struct AugmentationTask<T> {
477 input: T,
478 cache_key: Option<String>,
479 task_id: usize,
480}
481
482impl<T: Clone + Send + Sync + 'static> AugmentationQueue<T> {
483 pub fn new(engine: OnlineAugmentationEngine<T>, max_queue_size: usize) -> Self {
485 Self {
486 tasks: Arc::new(RwLock::new(Vec::new())),
487 engine: Arc::new(engine),
488 max_queue_size,
489 }
490 }
491
492 pub fn submit(&self, input: T, cache_key: Option<String>) -> Result<T> {
494 let tasks = self.tasks.read().expect("lock should not be poisoned");
495 if tasks.len() >= self.max_queue_size {
496 return Err(TorshError::InvalidArgument(
497 "Augmentation queue is full".to_string(),
498 ));
499 }
500 drop(tasks);
501
502 self.engine.apply(input, cache_key.as_deref())
504 }
505
506 pub fn process_tasks(&self) -> usize {
508 let mut tasks = self.tasks.write().expect("lock should not be poisoned");
509 let processed_count = tasks.len();
510
511 tasks.clear();
514
515 processed_count
516 }
517
518 pub fn queue_length(&self) -> usize {
520 self.tasks
521 .read()
522 .expect("lock should not be poisoned")
523 .len()
524 }
525}
526
527#[cfg(test)]
528mod tests {
529 use super::*;
530 use crate::transforms::lambda;
531
532 #[test]
533 fn test_augmentation_stats_default() {
534 let stats = AugmentationStats::default();
535 assert_eq!(stats.total_transforms, 0);
536 assert_eq!(stats.cache_hits, 0);
537 assert_eq!(stats.cache_misses, 0);
538 assert_eq!(stats.total_time_ms, 0.0);
539 assert_eq!(stats.average_time_ms, 0.0);
540 }
541
542 #[test]
543 fn test_online_augmentation_engine_creation() {
544 let transform = lambda(|x: i32| Ok(x * 2));
545 let engine = OnlineAugmentationEngine::new(transform);
546
547 assert!(!engine.cache_enabled);
548 assert_eq!(engine.max_cache_size, 1000);
549 assert_eq!(engine.cache_size(), 0);
550 }
551
552 #[test]
553 fn test_online_augmentation_engine_with_cache() {
554 let transform = lambda(|x: i32| Ok(x * 2));
555 let engine = OnlineAugmentationEngine::new(transform).with_cache(500);
556
557 assert!(engine.cache_enabled);
558 assert_eq!(engine.max_cache_size, 500);
559 }
560
561 #[test]
562 fn test_dynamic_augmentation_strategy() {
563 let strategy = DynamicAugmentationStrategy::<i32>::new(100);
564 assert_eq!(strategy.current_epoch, 0);
565 assert_eq!(strategy.total_epochs, 100);
566 assert_eq!(strategy.get_progress(), 0.0);
567 }
568
569 #[test]
570 fn test_dynamic_strategy_progress() {
571 let mut strategy = DynamicAugmentationStrategy::<i32>::new(100);
572 strategy.set_epoch(25);
573 assert_eq!(strategy.get_progress(), 0.25);
574
575 strategy.set_epoch(50);
576 assert_eq!(strategy.get_progress(), 0.5);
577
578 strategy.set_epoch(100);
579 assert_eq!(strategy.get_progress(), 1.0);
580 }
581
582 #[test]
583 fn test_progressive_augmentation_intensity() {
584 let light = lambda(|x: i32| Ok(x + 1));
585 let medium = lambda(|x: i32| Ok(x + 2));
586 let heavy = lambda(|x: i32| Ok(x + 3));
587
588 let mut progressive = ProgressiveAugmentation::new(light, medium, heavy, 100);
589
590 progressive.set_epoch(0);
592 assert_eq!(progressive.calculate_intensity(), 0.0);
593
594 progressive.set_epoch(25);
595 assert_eq!(progressive.calculate_intensity(), 0.25);
596
597 progressive.set_epoch(50);
598 assert_eq!(progressive.calculate_intensity(), 0.5);
599
600 progressive.set_epoch(100);
601 assert_eq!(progressive.calculate_intensity(), 1.0);
602 }
603
604 #[test]
605 fn test_progressive_augmentation_step_wise() {
606 let light = lambda(|x: i32| Ok(x + 1));
607 let medium = lambda(|x: i32| Ok(x + 2));
608 let heavy = lambda(|x: i32| Ok(x + 3));
609
610 let mut progressive = ProgressiveAugmentation::new(light, medium, heavy, 100)
611 .with_progression_mode(ProgressionMode::StepWise);
612
613 progressive.set_epoch(10);
614 assert_eq!(progressive.calculate_intensity(), 0.0);
615
616 progressive.set_epoch(40);
617 assert_eq!(progressive.calculate_intensity(), 0.5);
618
619 progressive.set_epoch(80);
620 assert_eq!(progressive.calculate_intensity(), 1.0);
621 }
622
623 #[test]
624 fn test_adaptive_augmentation() {
625 let mut adaptive = AdaptiveAugmentation::<i32>::new(0.85);
626 assert_eq!(adaptive.current_intensity(), 0.5);
627 assert_eq!(adaptive.recent_performance(), None);
628
629 adaptive.update_performance(0.9);
631 assert!(adaptive.current_intensity() > 0.5);
632 assert_eq!(adaptive.recent_performance(), Some(0.9));
633
634 adaptive.update_performance(0.7);
636 assert!(adaptive.current_intensity() < 0.6);
637 }
638
639 #[test]
640 fn test_augmentation_queue() {
641 let transform = lambda(|x: i32| Ok(x * 2));
642 let engine = OnlineAugmentationEngine::new(transform);
643 let queue = AugmentationQueue::new(engine, 10);
644
645 assert_eq!(queue.queue_length(), 0);
646
647 let result = queue.submit(5, None).unwrap();
648 assert_eq!(result, 10);
649 }
650}