#[allow(dead_code)]
use scirs2_core::ndarray::{Array1, Array2};
use scirs2_core::numeric::Float;
use std::collections::{HashMap, VecDeque};
use std::fmt::Debug;
use crate::error::{OptimError, Result};
#[derive(Debug, Clone, Copy)]
pub enum CurriculumStrategy {
None,
DifficultyProgression,
DiversityBased,
SelfPaced,
TeacherStudent,
Adversarial,
MultiTask,
Adaptive,
}
#[derive(Debug, Clone)]
pub struct CurriculumLearner<T: Float + Debug + Send + Sync + 'static> {
strategy: CurriculumStrategy,
curriculum_params: CurriculumParams<T>,
difficulty_estimator: TaskDifficultyEstimator<T>,
progress_tracker: LearningProgressTracker<T>,
curriculum_state: CurriculumState<T>,
task_scheduler: TaskScheduler<T>,
performance_history: VecDeque<PerformanceRecord<T>>,
}
#[derive(Debug, Clone)]
pub struct CurriculumParams<T: Float + Debug + Send + Sync + 'static> {
initial_difficulty: T,
max_difficulty: T,
difficulty_increment: T,
progression_threshold: T,
patience: usize,
self_pacing_factor: T,
diversity_weight: T,
teacher_confidence: T,
}
#[derive(Debug, Clone)]
pub struct TaskDifficultyEstimator<T: Float + Debug + Send + Sync + 'static> {
difficulty_predictor: DifficultyPredictor<T>,
task_features: HashMap<String, Array1<T>>,
difficulty_history: HashMap<String, Vec<T>>,
estimation_method: DifficultyEstimationMethod,
}
#[derive(Debug, Clone)]
pub struct LearningProgressTracker<T: Float + Debug + Send + Sync + 'static> {
performance_timeline: VecDeque<T>,
learning_rates: VecDeque<T>,
competency_levels: HashMap<String, T>,
milestones: Vec<ProgressMilestone<T>>,
}
#[derive(Debug, Clone)]
pub struct CurriculumState<T: Float + Debug + Send + Sync + 'static> {
current_difficulty: T,
active_tasks: Vec<String>,
recent_performance: T,
epochs_since_increase: usize,
learning_phase: LearningPhase,
adaptive_params: HashMap<String, T>,
}
#[derive(Debug, Clone)]
pub struct TaskScheduler<T: Float + Debug + Send + Sync + 'static> {
task_queue: VecDeque<ScheduledTask<T>>,
scheduling_policy: SchedulingPolicy,
task_weights: HashMap<String, T>,
load_balancing: HashMap<String, T>,
}
#[derive(Debug, Clone)]
pub struct PerformanceRecord<T: Float + Debug + Send + Sync + 'static> {
task_id: String,
performance: T,
difficulty_level: T,
training_steps: usize,
timestamp: usize,
metrics: HashMap<String, T>,
}
#[derive(Debug, Clone)]
pub struct DifficultyPredictor<T: Float + Debug + Send + Sync + 'static> {
input_dim: usize,
hidden_layers: Vec<Array2<T>>,
output_layer: Array1<T>,
training_history: Vec<(Array1<T>, T)>,
}
#[derive(Debug, Clone)]
pub struct ProgressMilestone<T: Float + Debug + Send + Sync + 'static> {
name: String,
threshold: T,
achieved: bool,
achieved_at: Option<usize>,
}
#[derive(Debug, Clone)]
pub struct ScheduledTask<T: Float + Debug + Send + Sync + 'static> {
task_id: String,
priority: T,
difficulty: T,
required_competency: T,
parameters: HashMap<String, T>,
}
#[derive(Debug, Clone, Copy)]
pub enum LearningPhase {
Exploration,
SkillBuilding,
Mastery,
Transfer,
Generalization,
}
#[derive(Debug, Clone, Copy)]
pub enum DifficultyEstimationMethod {
PerformanceBased,
FeatureBased,
GradientBased,
UncertaintyBased,
MultiModal,
}
#[derive(Debug, Clone, Copy)]
pub enum SchedulingPolicy {
FIFO,
Priority,
WeightedRandom,
Balanced,
Adaptive,
}
impl<T: Float + Debug + Send + Sync + 'static + Default + Clone> CurriculumLearner<T> {
pub fn new(strategy: CurriculumStrategy) -> Result<Self> {
Ok(Self {
strategy,
curriculum_params: CurriculumParams::default(),
difficulty_estimator: TaskDifficultyEstimator::new()?,
progress_tracker: LearningProgressTracker::new(),
curriculum_state: CurriculumState::new()?,
task_scheduler: TaskScheduler::new()?,
performance_history: VecDeque::new(),
})
}
pub fn update_curriculum(
&mut self,
task_id: &str,
performance: T,
training_steps: usize,
) -> Result<()> {
let record = PerformanceRecord {
task_id: task_id.to_string(),
performance,
difficulty_level: self.curriculum_state.current_difficulty,
training_steps,
timestamp: self.performance_history.len(),
metrics: HashMap::new(),
};
self.performance_history.push_back(record);
if self.performance_history.len() > 1000 {
self.performance_history.pop_front();
}
self.progress_tracker.update_performance(performance);
match self.strategy {
CurriculumStrategy::None => Ok(()),
CurriculumStrategy::DifficultyProgression => {
self.update_difficulty_progression(performance)
}
CurriculumStrategy::SelfPaced => self.update_self_paced_curriculum(performance),
CurriculumStrategy::Adaptive => self.update_adaptive_curriculum(task_id, performance),
_ => self.update_generic_curriculum(performance),
}
}
pub fn get_next_task(&mut self) -> Result<Option<String>> {
match self.strategy {
CurriculumStrategy::None => Ok(None),
_ => Ok(self.task_scheduler.schedule_next_task()),
}
}
fn update_difficulty_progression(&mut self, performance: T) -> Result<()> {
self.curriculum_state.recent_performance = performance;
if performance > self.curriculum_params.progression_threshold {
self.curriculum_state.epochs_since_increase += 1;
if self.curriculum_state.epochs_since_increase >= self.curriculum_params.patience {
let new_difficulty = (self.curriculum_state.current_difficulty
+ self.curriculum_params.difficulty_increment)
.min(self.curriculum_params.max_difficulty);
self.curriculum_state.current_difficulty = new_difficulty;
self.curriculum_state.epochs_since_increase = 0;
self.update_learning_phase();
}
} else {
self.curriculum_state.epochs_since_increase = 0;
}
Ok(())
}
fn update_self_paced_curriculum(&mut self, performance: T) -> Result<()> {
let pacing_factor = self.curriculum_params.self_pacing_factor;
let performance_ratio = performance / self.get_expected_performance();
let difficulty_adjustment = (performance_ratio - T::one()) * pacing_factor;
let new_difficulty = (self.curriculum_state.current_difficulty + difficulty_adjustment)
.max(self.curriculum_params.initial_difficulty)
.min(self.curriculum_params.max_difficulty);
self.curriculum_state.current_difficulty = new_difficulty;
Ok(())
}
fn update_adaptive_curriculum(&mut self, task_id: &str, performance: T) -> Result<()> {
let competency = self
.progress_tracker
.competency_levels
.get(task_id)
.copied()
.unwrap_or(T::zero());
let alpha = scirs2_core::numeric::NumCast::from(0.1).unwrap_or_else(|| T::zero());
let new_competency = competency * (T::one() - alpha) + performance * alpha;
self.progress_tracker
.competency_levels
.insert(task_id.to_string(), new_competency);
self.adapt_curriculum_parameters(task_id, performance)?;
Ok(())
}
fn update_generic_curriculum(&mut self, performance: T) -> Result<()> {
if performance > scirs2_core::numeric::NumCast::from(0.8).unwrap_or_else(|| T::zero()) {
let increment = self.curriculum_params.difficulty_increment
* scirs2_core::numeric::NumCast::from(0.5).unwrap_or_else(|| T::zero());
self.curriculum_state.current_difficulty = (self.curriculum_state.current_difficulty
+ increment)
.min(self.curriculum_params.max_difficulty);
}
Ok(())
}
fn update_learning_phase(&mut self) {
let difficulty_ratio =
self.curriculum_state.current_difficulty / self.curriculum_params.max_difficulty;
self.curriculum_state.learning_phase = match difficulty_ratio {
x if x < scirs2_core::numeric::NumCast::from(0.2).unwrap_or_else(|| T::zero()) => {
LearningPhase::Exploration
}
x if x < scirs2_core::numeric::NumCast::from(0.4).unwrap_or_else(|| T::zero()) => {
LearningPhase::SkillBuilding
}
x if x < scirs2_core::numeric::NumCast::from(0.7).unwrap_or_else(|| T::zero()) => {
LearningPhase::Mastery
}
x if x < scirs2_core::numeric::NumCast::from(0.9).unwrap_or_else(|| T::zero()) => {
LearningPhase::Transfer
}
_ => LearningPhase::Generalization,
};
}
fn adapt_curriculum_parameters(&mut self, task_id: &str, performance: T) -> Result<()> {
let performance_variance = self.calculate_performance_variance(task_id);
if performance_variance
> scirs2_core::numeric::NumCast::from(0.1).unwrap_or_else(|| T::zero())
{
self.curriculum_params.patience = self.curriculum_params.patience.max(5);
} else {
self.curriculum_params.patience =
(self.curriculum_params.patience.saturating_sub(1)).max(1);
}
let trend = self.calculate_performance_trend();
if trend > T::zero() {
self.curriculum_params.progression_threshold =
(self.curriculum_params.progression_threshold
* scirs2_core::numeric::NumCast::from(0.95).unwrap_or_else(|| T::zero()))
.max(scirs2_core::numeric::NumCast::from(0.5).unwrap_or_else(|| T::zero()));
} else {
self.curriculum_params.progression_threshold =
(self.curriculum_params.progression_threshold
* scirs2_core::numeric::NumCast::from(1.05).unwrap_or_else(|| T::zero()))
.min(scirs2_core::numeric::NumCast::from(0.95).unwrap_or_else(|| T::zero()));
}
Ok(())
}
fn calculate_performance_variance(&self, task_id: &str) -> T {
let task_performances: Vec<T> = self
.performance_history
.iter()
.filter(|record| record.task_id == task_id)
.map(|record| record.performance)
.collect();
if task_performances.len() < 2 {
return T::zero();
}
let mean = task_performances
.iter()
.cloned()
.fold(T::zero(), |a, b| a + b)
/ T::from(task_performances.len() as f64).expect("unwrap failed");
let variance = task_performances
.iter()
.map(|&x| (x - mean) * (x - mean))
.fold(T::zero(), |a, b| a + b)
/ T::from((task_performances.len() - 1) as f64).expect("unwrap failed");
variance
}
fn calculate_performance_trend(&self) -> T {
if self.performance_history.len() < 10 {
return T::zero();
}
let recent: Vec<T> = self
.performance_history
.iter()
.rev()
.take(10)
.map(|record| record.performance)
.collect();
let first_half_avg = recent[5..].iter().cloned().fold(T::zero(), |a, b| a + b)
/ scirs2_core::numeric::NumCast::from(5.0).unwrap_or_else(|| T::zero());
let second_half_avg = recent[..5].iter().cloned().fold(T::zero(), |a, b| a + b)
/ scirs2_core::numeric::NumCast::from(5.0).unwrap_or_else(|| T::zero());
second_half_avg - first_half_avg
}
fn get_expected_performance(&self) -> T {
let difficulty_factor =
self.curriculum_state.current_difficulty / self.curriculum_params.max_difficulty;
T::one()
- difficulty_factor
* scirs2_core::numeric::NumCast::from(0.5).unwrap_or_else(|| T::zero())
}
pub fn add_task(
&mut self,
task_id: String,
estimated_difficulty: T,
required_competency: T,
) -> Result<()> {
let scheduled_task = ScheduledTask {
task_id: task_id.clone(),
priority: T::one() / estimated_difficulty, difficulty: estimated_difficulty,
required_competency,
parameters: HashMap::new(),
};
self.task_scheduler.add_task(scheduled_task);
self.progress_tracker
.competency_levels
.insert(task_id, T::zero());
Ok(())
}
pub fn get_curriculum_statistics(&self) -> HashMap<String, T> {
let mut stats = HashMap::new();
stats.insert(
"current_difficulty".to_string(),
self.curriculum_state.current_difficulty,
);
stats.insert(
"recent_performance".to_string(),
self.curriculum_state.recent_performance,
);
stats.insert(
"epochs_since_increase".to_string(),
scirs2_core::numeric::NumCast::from(self.curriculum_state.epochs_since_increase as f64)
.unwrap_or_else(|| T::zero()),
);
stats.insert(
"active_tasks_count".to_string(),
T::from(self.curriculum_state.active_tasks.len() as f64).expect("unwrap failed"),
);
if !self.progress_tracker.competency_levels.is_empty() {
let avg_competency = self
.progress_tracker
.competency_levels
.values()
.cloned()
.fold(T::zero(), |a, b| a + b)
/ T::from(self.progress_tracker.competency_levels.len() as f64)
.expect("unwrap failed");
stats.insert("average_competency".to_string(), avg_competency);
}
stats
}
pub fn reset(&mut self) {
self.curriculum_state = CurriculumState::new().expect("unwrap failed");
self.progress_tracker.reset();
self.performance_history.clear();
self.task_scheduler.reset();
}
}
impl<T: Float + Debug + Send + Sync + 'static + Default + Clone> TaskDifficultyEstimator<T> {
fn new() -> Result<Self> {
Ok(Self {
difficulty_predictor: DifficultyPredictor::new(10)?, task_features: HashMap::new(),
difficulty_history: HashMap::new(),
estimation_method: DifficultyEstimationMethod::PerformanceBased,
})
}
}
impl<T: Float + Debug + Send + Sync + 'static + Default + Clone> LearningProgressTracker<T> {
fn new() -> Self {
Self {
performance_timeline: VecDeque::new(),
learning_rates: VecDeque::new(),
competency_levels: HashMap::new(),
milestones: Vec::new(),
}
}
fn update_performance(&mut self, performance: T) {
self.performance_timeline.push_back(performance);
if self.performance_timeline.len() > 1000 {
self.performance_timeline.pop_front();
}
}
fn reset(&mut self) {
self.performance_timeline.clear();
self.learning_rates.clear();
self.competency_levels.clear();
self.milestones.clear();
}
}
impl<T: Float + Debug + Send + Sync + 'static + Default + Clone> CurriculumState<T> {
fn new() -> Result<Self> {
Ok(Self {
current_difficulty: scirs2_core::numeric::NumCast::from(0.1)
.unwrap_or_else(|| T::zero()),
active_tasks: Vec::new(),
recent_performance: T::zero(),
epochs_since_increase: 0,
learning_phase: LearningPhase::Exploration,
adaptive_params: HashMap::new(),
})
}
}
impl<T: Float + Debug + Send + Sync + 'static + Default + Clone> TaskScheduler<T> {
fn new() -> Result<Self> {
Ok(Self {
task_queue: VecDeque::new(),
scheduling_policy: SchedulingPolicy::Priority,
task_weights: HashMap::new(),
load_balancing: HashMap::new(),
})
}
fn add_task(&mut self, task: ScheduledTask<T>) {
self.task_queue.push_back(task);
}
fn schedule_next_task(&mut self) -> Option<String> {
if let Some(task) = self.task_queue.pop_front() {
Some(task.task_id)
} else {
None
}
}
fn reset(&mut self) {
self.task_queue.clear();
self.task_weights.clear();
self.load_balancing.clear();
}
}
impl<T: Float + Debug + Send + Sync + 'static + Default + Clone> DifficultyPredictor<T> {
fn new(input_dim: usize) -> Result<Self> {
Ok(Self {
input_dim,
hidden_layers: vec![Array2::eye(input_dim)],
output_layer: Array1::ones(input_dim),
training_history: Vec::new(),
})
}
}
impl<T: Float + Debug + Send + Sync + 'static + Default + Clone> Default for CurriculumParams<T> {
fn default() -> Self {
Self {
initial_difficulty: scirs2_core::numeric::NumCast::from(0.1)
.unwrap_or_else(|| T::zero()),
max_difficulty: scirs2_core::numeric::NumCast::from(1.0).unwrap_or_else(|| T::zero()),
difficulty_increment: scirs2_core::numeric::NumCast::from(0.05)
.unwrap_or_else(|| T::zero()),
progression_threshold: scirs2_core::numeric::NumCast::from(0.8)
.unwrap_or_else(|| T::zero()),
patience: 5,
self_pacing_factor: scirs2_core::numeric::NumCast::from(0.1)
.unwrap_or_else(|| T::zero()),
diversity_weight: scirs2_core::numeric::NumCast::from(0.2).unwrap_or_else(|| T::zero()),
teacher_confidence: scirs2_core::numeric::NumCast::from(0.9)
.unwrap_or_else(|| T::zero()),
}
}
}