use scirs2_core::ndarray::{Array1, Array2};
use scirs2_core::numeric::Float;
use std::collections::{HashMap, VecDeque};
use std::fmt::Debug;
use std::time::{Duration, Instant};
use super::OptimizerState;
use crate::error::{OptimError, Result};
pub mod prototypical;
pub use prototypical::{activate, xavier_encoder_layer};
pub struct FewShotLearningSystem<T: Float + Debug + Send + Sync + 'static> {
base_optimizer: Box<dyn FewShotOptimizer<T>>,
prototype_network: PrototypicalNetwork<T>,
memory_bank: EpisodicMemoryBank<T>,
fast_adaptation: FastAdaptationEngine<T>,
performance_tracker: FewShotPerformanceTracker<T>,
}
pub trait FewShotOptimizer<T: Float + Debug + Send + Sync + 'static>: Send + Sync {
fn adapt_few_shot(
&mut self,
support_set: &SupportSet<T>,
query_set: &QuerySet<T>,
adaptation_config: &AdaptationConfig,
) -> Result<AdaptationResult<T>>;
fn get_task_representation(&self, taskdata: &TaskData<T>) -> Result<Array1<T>>;
fn compute_adaptation_loss(
&self,
support_set: &SupportSet<T>,
query_set: &QuerySet<T>,
) -> Result<T>;
fn update_meta_parameters(&mut self, metagradients: &MetaGradients<T>) -> Result<()>;
fn get_transfer_state(&self) -> TransferState<T>;
fn load_transfer_state(&mut self, state: TransferState<T>) -> Result<()>;
}
#[derive(Debug, Clone)]
pub struct SupportSet<T: Float + Debug + Send + Sync + 'static> {
pub examples: Vec<SupportExample<T>>,
pub task_metadata: TaskMetadata,
pub statistics: SupportSetStatistics<T>,
pub temporal_order: Option<Vec<usize>>,
}
#[derive(Debug, Clone)]
pub struct SupportExample<T: Float + Debug + Send + Sync + 'static> {
pub features: Array1<T>,
pub target: T,
pub weight: T,
pub context: HashMap<String, T>,
pub metadata: ExampleMetadata,
}
#[derive(Debug, Clone)]
pub struct QuerySet<T: Float + Debug + Send + Sync + 'static> {
pub examples: Vec<QueryExample<T>>,
pub statistics: QuerySetStatistics<T>,
pub eval_metrics: Vec<EvaluationMetric>,
}
#[derive(Debug, Clone)]
pub struct QueryExample<T: Float + Debug + Send + Sync + 'static> {
pub features: Array1<T>,
pub true_target: Option<T>,
pub weight: T,
pub context: HashMap<String, T>,
}
#[derive(Debug, Clone)]
pub struct TaskData<T: Float + Debug + Send + Sync + 'static> {
pub task_id: String,
pub support_set: SupportSet<T>,
pub query_set: QuerySet<T>,
pub task_params: HashMap<String, T>,
pub domain_info: DomainInfo,
}
#[derive(Debug, Clone)]
pub struct DomainInfo {
pub domain_type: DomainType,
pub characteristics: DomainCharacteristics,
pub difficulty_level: DifficultyLevel,
pub constraints: Vec<DomainConstraint>,
}
#[derive(Debug, Clone, Copy)]
pub enum DomainType {
ComputerVision,
NaturalLanguageProcessing,
ReinforcementLearning,
TimeSeriesForecasting,
ScientificComputing,
Optimization,
ControlSystems,
GamePlaying,
Robotics,
Healthcare,
}
#[derive(Debug, Clone)]
pub struct DomainCharacteristics {
pub input_dim: usize,
pub output_dim: usize,
pub temporal: bool,
pub stochasticity: f64,
pub noise_level: f64,
pub sparsity: f64,
}
#[derive(Debug, Clone, Copy)]
pub enum DifficultyLevel {
Trivial,
Easy,
Medium,
Hard,
Expert,
Extreme,
}
#[derive(Debug, Clone)]
pub struct DomainConstraint {
pub constraint_type: ConstraintType,
pub description: String,
pub enforcement: ConstraintEnforcement,
}
#[derive(Debug, Clone, Copy)]
pub enum ConstraintType {
ResourceLimit,
TemporalConstraint,
AccuracyRequirement,
LatencyRequirement,
MemoryConstraint,
EnergyConstraint,
SafetyConstraint,
}
#[derive(Debug, Clone, Copy)]
pub enum ConstraintEnforcement {
Hard,
Soft,
Advisory,
}
#[derive(Debug, Clone)]
pub struct AdaptationConfig {
pub adaptation_steps: usize,
pub adaptation_lr: f64,
pub strategy: AdaptationStrategyType,
pub early_stopping: Option<EarlyStoppingConfig>,
pub regularization: RegularizationConfig,
pub resource_constraints: ResourceConstraints,
}
#[derive(Debug, Clone, Copy)]
pub enum AdaptationStrategyType {
MAML,
FOMAML,
Prototypical,
Matching,
Relation,
MetaSGD,
LearnedOptimizer,
GradientBased,
MemoryAugmented,
}
#[derive(Debug, Clone)]
pub struct EarlyStoppingConfig {
pub patience: usize,
pub min_improvement: f64,
pub validation_frequency: usize,
}
#[derive(Debug, Clone)]
pub struct RegularizationConfig {
pub l2_strength: f64,
pub dropout_rate: f64,
pub gradient_clip: Option<f64>,
pub task_regularization: HashMap<String, f64>,
}
#[derive(Debug, Clone)]
pub struct ResourceConstraints {
pub max_time: Duration,
pub max_memory_mb: usize,
pub max_compute_budget: f64,
}
#[derive(Debug, Clone)]
pub struct AdaptationResult<T: Float + Debug + Send + Sync + 'static> {
pub adapted_state: OptimizerState<T>,
pub performance: AdaptationPerformance<T>,
pub task_representation: Array1<T>,
pub adaptation_trajectory: Vec<AdaptationStep<T>>,
pub resource_usage: ResourceUsage<T>,
}
#[derive(Debug, Clone)]
pub struct AdaptationPerformance<T: Float + Debug + Send + Sync + 'static> {
pub query_performance: T,
pub support_performance: T,
pub adaptation_speed: usize,
pub final_loss: T,
pub improvement: T,
pub stability: T,
}
#[derive(Debug, Clone)]
pub struct AdaptationStep<T: Float + Debug + Send + Sync + 'static> {
pub step: usize,
pub loss: T,
pub performance: T,
pub gradient_norm: T,
pub step_time: Duration,
}
#[derive(Debug, Clone)]
pub struct ResourceUsage<T: Float + Debug + Send + Sync + 'static> {
pub total_time: Duration,
pub peak_memory_mb: T,
pub compute_cost: T,
pub energy_consumption: T,
}
#[derive(Debug, Clone)]
pub struct MetaGradients<T: Float + Debug + Send + Sync + 'static> {
pub param_gradients: HashMap<String, Array1<T>>,
pub lr_gradients: HashMap<String, T>,
pub arch_gradients: HashMap<String, Array1<T>>,
pub gradient_norm: T,
}
#[derive(Debug, Clone)]
pub struct TransferState<T: Float + Debug + Send + Sync + 'static> {
pub representations: HashMap<String, Array1<T>>,
pub meta_parameters: HashMap<String, Array1<T>>,
pub task_embeddings: Array2<T>,
pub transfer_stats: TransferStatistics<T>,
}
#[derive(Debug, Clone)]
pub struct TransferStatistics<T: Float + Debug + Send + Sync + 'static> {
pub source_performance: T,
pub target_performance: T,
pub transfer_efficiency: T,
pub steps_saved: usize,
}
pub struct PrototypicalNetwork<T: Float + Debug + Send + Sync + 'static> {
pub(super) encoder: EncoderNetwork<T>,
prototypes: HashMap<String, Prototype<T>>,
distance_metric: DistanceMetric,
parameters: PrototypicalNetworkParams<T>,
}
#[derive(Debug)]
pub struct EncoderNetwork<T: Float + Debug + Send + Sync + 'static> {
layers: Vec<EncoderLayer<T>>,
activation: ActivationFunction,
}
#[derive(Debug)]
pub struct EncoderLayer<T: Float + Debug + Send + Sync + 'static> {
pub weights: Array2<T>,
pub bias: Array1<T>,
pub layer_type: LayerType,
}
#[derive(Debug, Clone, Copy)]
pub enum LayerType {
Linear,
Convolutional,
Recurrent,
Attention,
Residual,
}
#[derive(Debug, Clone, Copy)]
pub enum ActivationFunction {
ReLU,
Tanh,
Sigmoid,
Swish,
GELU,
Mish,
}
#[derive(Debug, Clone)]
pub struct Prototype<T: Float + Debug + Send + Sync + 'static> {
pub vector: Array1<T>,
pub confidence: T,
pub example_count: usize,
pub last_updated: std::time::SystemTime,
pub metadata: PrototypeMetadata,
}
#[derive(Debug, Clone)]
pub struct PrototypeMetadata {
pub task_category: String,
pub domain: DomainType,
pub created_at: std::time::SystemTime,
pub update_count: usize,
}
#[derive(Debug, Clone, Copy)]
pub enum DistanceMetric {
Euclidean,
Cosine,
Manhattan,
Mahalanobis,
Learned,
}
#[derive(Debug, Clone)]
pub struct PrototypicalNetworkParams<T: Float + Debug + Send + Sync + 'static> {
pub embedding_dim: usize,
pub learning_rate: T,
pub temperature: T,
pub prototype_update_rate: T,
}
pub struct SupportSetManager<T: Float + Debug + Send + Sync + 'static> {
config: SupportSetManagerConfig,
_element: std::marker::PhantomData<T>,
}
#[derive(Debug, Clone)]
pub struct SupportSetManagerConfig {
pub min_support_size: usize,
pub max_support_size: usize,
pub quality_threshold: f64,
pub enable_augmentation: bool,
pub cache_support_sets: bool,
}
pub trait AdaptationStrategy<T: Float + Debug + Send + Sync + 'static>: Send + Sync {
fn adapt(
&mut self,
optimizer: &mut dyn FewShotOptimizer<T>,
task_data: &TaskData<T>,
config: &AdaptationConfig,
) -> Result<AdaptationResult<T>>;
fn name(&self) -> &str;
fn parameters(&self) -> HashMap<String, f64>;
fn update_parameters(&mut self, params: HashMap<String, f64>) -> Result<()>;
}
pub struct TaskSimilarityCalculator<T: Float + Debug + Send + Sync + 'static> {
similarity_cache: HashMap<(String, String), T>,
config: SimilarityCalculatorConfig<T>,
}
pub trait SimilarityMetric<T: Float + Debug + Send + Sync + 'static>: Send + Sync {
fn calculate_similarity(&self, task1: &TaskData<T>, task2: &TaskData<T>) -> Result<T>;
fn name(&self) -> &str;
fn weight(&self) -> T;
}
#[derive(Debug, Clone)]
pub struct SimilarityCalculatorConfig<T: Float + Debug + Send + Sync + 'static> {
pub enable_caching: bool,
pub cache_size_limit: usize,
pub similarity_threshold: T,
pub use_metadata: bool,
}
pub struct EpisodicMemoryBank<T: Float + Debug + Send + Sync + 'static> {
episodes: VecDeque<MemoryEpisode<T>>,
config: MemoryBankConfig<T>,
usage_stats: MemoryUsageStats,
}
#[derive(Debug, Clone)]
pub struct MemoryEpisode<T: Float + Debug + Send + Sync + 'static> {
pub episode_id: String,
pub task_data: TaskData<T>,
pub adaptation_result: AdaptationResult<T>,
pub timestamp: std::time::SystemTime,
pub metadata: EpisodeMetadata,
pub access_count: usize,
}
#[derive(Debug, Clone)]
pub struct EpisodeMetadata {
pub difficulty: DifficultyLevel,
pub domain: DomainType,
pub success_rate: f64,
pub tags: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct MemoryBankConfig<T: Float + Debug + Send + Sync + 'static> {
pub max_memory_size: usize,
pub eviction_policy: EvictionPolicy,
pub similarity_threshold: T,
pub enable_compression: bool,
}
#[derive(Debug, Clone, Copy)]
pub enum EvictionPolicy {
LRU, LFU, Performance, Age, Random,
}
#[derive(Debug, Clone)]
pub struct MemoryBankStats<T: Float + Debug + Send + Sync + 'static> {
pub count: usize,
pub avg_performance: T,
pub capacity_used: f64,
pub total_capacity: usize,
}
#[derive(Debug, Clone)]
pub struct MemoryUsageStats {
pub total_episodes: usize,
pub memory_utilization: f64,
pub hit_rate: f64,
pub avg_retrieval_time: Duration,
}
pub struct FastAdaptationEngine<T: Float + Debug + Send + Sync + 'static> {
algorithms: Vec<Box<dyn FastAdaptationAlgorithm<T>>>,
config: FastAdaptationConfig,
}
pub trait FastAdaptationAlgorithm<T: Float + Debug + Send + Sync + 'static>: Send + Sync {
fn adapt_fast(
&mut self,
optimizer: &mut dyn FewShotOptimizer<T>,
task_data: &TaskData<T>,
target_performance: Option<T>,
) -> Result<AdaptationResult<T>>;
fn estimate_adaptation_time(&self, taskdata: &TaskData<T>) -> Duration;
fn name(&self) -> &str;
}
#[derive(Debug, Clone)]
pub struct FastAdaptationConfig {
pub enable_caching: bool,
pub enable_prediction: bool,
pub max_adaptation_time: Duration,
pub _performance_threshold: f64,
}
pub struct FewShotPerformanceTracker<T: Float + Debug + Send + Sync + 'static> {
performance_history: VecDeque<PerformanceRecord<T>>,
config: TrackingConfig,
stats: PerformanceStats<T>,
}
#[derive(Debug, Clone)]
pub struct PerformanceRecord<T: Float + Debug + Send + Sync + 'static> {
pub task_id: String,
pub performance: T,
pub adaptation_time: Duration,
pub strategy_used: String,
pub timestamp: std::time::SystemTime,
pub additional_metrics: HashMap<String, T>,
}
pub trait PerformanceMetric<T: Float + Debug + Send + Sync + 'static>: Send + Sync {
fn calculate(&self, records: &[PerformanceRecord<T>]) -> Result<T>;
fn name(&self) -> &str;
fn higher_is_better(&self) -> bool;
}
#[derive(Debug, Clone)]
pub struct TrackingConfig {
pub max_history_size: usize,
pub update_frequency: Duration,
pub detailed_tracking: bool,
pub export_results: bool,
}
#[derive(Debug)]
pub struct PerformanceStats<T: Float + Debug + Send + Sync + 'static> {
pub best_performance: T,
pub average_performance: T,
pub performance_variance: T,
pub improvement_rate: T,
pub success_rate: T,
}
impl<T: Float + Debug + Send + Sync + 'static> FewShotLearningSystem<T> {
pub fn new(
base_optimizer: Box<dyn FewShotOptimizer<T>>,
config: FewShotConfig<T>,
) -> Result<Self> {
Ok(Self {
base_optimizer,
prototype_network: PrototypicalNetwork::new(config.prototype_config)?,
memory_bank: EpisodicMemoryBank::new(config.memory_config)?,
fast_adaptation: FastAdaptationEngine::new(config.adaptation_config)?,
performance_tracker: FewShotPerformanceTracker::new(config.tracking_config)?,
})
}
pub fn learn_few_shot(
&mut self,
task_data: TaskData<T>,
adaptation_config: AdaptationConfig,
) -> Result<AdaptationResult<T>> {
let _start_time = Instant::now();
let task_representation = self.prototype_network.encode_task(&task_data)?;
let similar_tasks = self.memory_bank.retrieve_similar(&task_data, 5)?;
let strategy = self.select_adaptation_strategy(&task_data, &similar_tasks)?;
let mut adaptation_result = self.fast_adaptation.adapt_fast(
&mut *self.base_optimizer,
&task_data,
strategy,
&adaptation_config,
)?;
adaptation_result.task_representation = task_representation;
self.memory_bank
.store_episode(task_data.clone(), adaptation_result.clone())?;
self.performance_tracker
.record_performance(&adaptation_result)?;
self.prototype_network
.update_prototypes(&task_data, &adaptation_result)?;
Ok(adaptation_result)
}
fn select_adaptation_strategy(
&self,
task_data: &TaskData<T>,
_similar_tasks: &[MemoryEpisode<T>],
) -> Result<AdaptationStrategyType> {
match task_data.domain_info.difficulty_level {
DifficultyLevel::Trivial | DifficultyLevel::Easy => Ok(AdaptationStrategyType::FOMAML),
DifficultyLevel::Medium => Ok(AdaptationStrategyType::MAML),
DifficultyLevel::Hard | DifficultyLevel::Expert => {
Ok(AdaptationStrategyType::Prototypical)
}
DifficultyLevel::Extreme => Ok(AdaptationStrategyType::MemoryAugmented),
}
}
}
#[derive(Debug, Clone)]
pub struct FewShotConfig<T: Float + Debug + Send + Sync + 'static> {
pub prototype_config: PrototypicalNetworkConfig<T>,
pub support_set_config: SupportSetManagerConfig,
pub similarity_config: SimilarityCalculatorConfig<T>,
pub memory_config: MemoryBankConfig<T>,
pub adaptation_config: FastAdaptationConfig,
pub tracking_config: TrackingConfig,
}
#[derive(Debug, Clone)]
pub struct PrototypicalNetworkConfig<T: Float + Debug + Send + Sync + 'static> {
pub embedding_dim: usize,
pub learning_rate: T,
pub num_layers: usize,
pub hidden_dim: usize,
}
impl<T: Float + Debug + Send + Sync + 'static> PrototypicalNetwork<T> {
pub fn new(config: PrototypicalNetworkConfig<T>) -> Result<Self> {
if config.embedding_dim == 0 {
return Err(OptimError::InvalidConfig(
"embedding_dim must be > 0".to_string(),
));
}
let input_dim = config.hidden_dim.max(config.embedding_dim);
let layer = xavier_encoder_layer::<T>(input_dim, config.embedding_dim, LayerType::Linear)?;
Ok(Self {
encoder: EncoderNetwork {
layers: vec![layer],
activation: ActivationFunction::ReLU,
},
prototypes: HashMap::new(),
distance_metric: DistanceMetric::Euclidean,
parameters: PrototypicalNetworkParams {
embedding_dim: config.embedding_dim,
learning_rate: config.learning_rate,
temperature: T::one(),
prototype_update_rate: scirs2_core::numeric::NumCast::from(0.1)
.unwrap_or_else(|| T::one()),
},
})
}
pub fn from_dims(embedding_dim: usize, _num_classes: usize) -> Result<Self> {
if embedding_dim == 0 {
return Err(OptimError::InvalidConfig(
"embedding_dim must be > 0".to_string(),
));
}
let layer = xavier_encoder_layer::<T>(embedding_dim, embedding_dim, LayerType::Linear)?;
Ok(Self {
encoder: EncoderNetwork {
layers: vec![layer],
activation: ActivationFunction::ReLU,
},
prototypes: HashMap::new(),
distance_metric: DistanceMetric::Euclidean,
parameters: PrototypicalNetworkParams {
embedding_dim,
learning_rate: scirs2_core::numeric::NumCast::from(0.01)
.unwrap_or_else(|| T::one()),
temperature: T::one(),
prototype_update_rate: scirs2_core::numeric::NumCast::from(0.1)
.unwrap_or_else(|| T::one()),
},
})
}
pub fn embedding_dim(&self) -> usize {
self.parameters.embedding_dim
}
pub fn encoder_layers(&self) -> &[EncoderLayer<T>] {
&self.encoder.layers
}
pub fn prototypes_mut(&mut self) -> &mut HashMap<String, Prototype<T>> {
&mut self.prototypes
}
pub fn prototypes(&self) -> &HashMap<String, Prototype<T>> {
&self.prototypes
}
pub fn distance_metric(&self) -> &DistanceMetric {
&self.distance_metric
}
pub fn encode_task(&self, task_data: &TaskData<T>) -> Result<Array1<T>> {
if task_data.support_set.examples.is_empty() {
return Err(OptimError::InsufficientData(
"No support examples for encoding".to_string(),
));
}
let width = self.encoder.embedding_width()?;
let mut sum = Array1::<T>::zeros(width);
let count = task_data.support_set.examples.len();
for ex in &task_data.support_set.examples {
let embedded = self.encoder.forward(&ex.features)?;
for i in 0..width {
sum[i] = sum[i] + embedded[i];
}
}
let count_t = scirs2_core::numeric::NumCast::from(count).unwrap_or_else(|| T::one());
for slot in sum.iter_mut() {
*slot = *slot / count_t;
}
Ok(sum)
}
pub fn embedding_width(&self) -> Result<usize> {
self.encoder.embedding_width()
}
pub fn update_prototypes(
&mut self,
task_data: &TaskData<T>,
_result: &AdaptationResult<T>,
) -> Result<()> {
let task_repr = self.encode_task(task_data)?;
let task_id = task_data.task_id.clone();
let update_rate = self.parameters.prototype_update_rate;
let one_minus = T::one() - update_rate;
if let Some(proto) = self.prototypes.get_mut(&task_id) {
let dim = proto.vector.len().min(task_repr.len());
for i in 0..dim {
proto.vector[i] = one_minus * proto.vector[i] + update_rate * task_repr[i];
}
proto.example_count += task_data.support_set.examples.len();
proto.last_updated = std::time::SystemTime::now();
proto.metadata.update_count += 1;
} else {
let proto = Prototype {
vector: task_repr,
confidence: T::one(),
example_count: task_data.support_set.examples.len(),
last_updated: std::time::SystemTime::now(),
metadata: PrototypeMetadata {
task_category: task_id.clone(),
domain: task_data.domain_info.domain_type,
created_at: std::time::SystemTime::now(),
update_count: 1,
},
};
self.prototypes.insert(task_id, proto);
}
Ok(())
}
}
impl<T: Float + Debug + Send + Sync + 'static> SupportSetManager<T> {
pub fn new(config: SupportSetManagerConfig) -> Result<Self> {
if config.max_support_size == 0 {
return Err(OptimError::InvalidConfig(
"max_support_size must be > 0".to_string(),
));
}
Ok(Self {
config,
_element: std::marker::PhantomData,
})
}
pub fn from_max_size(max_support_size: usize) -> Result<Self> {
Self::new(SupportSetManagerConfig {
min_support_size: 1,
max_support_size,
quality_threshold: 0.5,
enable_augmentation: true,
cache_support_sets: true,
})
}
pub fn max_support_size(&self) -> usize {
self.config.max_support_size
}
pub fn config(&self) -> &SupportSetManagerConfig {
&self.config
}
}
impl<T: Float + Debug + Send + Sync + 'static> TaskSimilarityCalculator<T> {
pub fn new(config: SimilarityCalculatorConfig<T>) -> Result<Self> {
Ok(Self {
similarity_cache: HashMap::new(),
config,
})
}
pub fn default_new() -> Result<Self> {
Self::new(SimilarityCalculatorConfig {
enable_caching: true,
cache_size_limit: 1000,
similarity_threshold: scirs2_core::numeric::NumCast::from(0.5)
.unwrap_or_else(|| T::zero()),
use_metadata: true,
})
}
pub fn similarity_cache_mut(&mut self) -> &mut HashMap<(String, String), T> {
&mut self.similarity_cache
}
pub fn caching_enabled(&self) -> bool {
self.config.enable_caching
}
}
impl<T: Float + Debug + Send + Sync + 'static> EpisodicMemoryBank<T> {
pub fn new(config: MemoryBankConfig<T>) -> Result<Self> {
if config.max_memory_size == 0 {
return Err(OptimError::InvalidConfig(
"max_memory_size must be > 0".to_string(),
));
}
Ok(Self {
episodes: VecDeque::new(),
config,
usage_stats: MemoryUsageStats {
total_episodes: 0,
memory_utilization: 0.0,
hit_rate: 0.0,
avg_retrieval_time: Duration::from_secs(0),
},
})
}
pub fn from_capacity(capacity: usize) -> Result<Self> {
Self::new(MemoryBankConfig {
max_memory_size: capacity,
eviction_policy: EvictionPolicy::Performance,
similarity_threshold: scirs2_core::numeric::NumCast::from(0.3)
.unwrap_or_else(|| T::zero()),
enable_compression: false,
})
}
pub fn capacity(&self) -> usize {
self.config.max_memory_size
}
pub fn len(&self) -> usize {
self.episodes.len()
}
pub fn is_empty(&self) -> bool {
self.episodes.is_empty()
}
pub fn episodes_mut(&mut self) -> &mut VecDeque<MemoryEpisode<T>> {
&mut self.episodes
}
pub fn episodes(&self) -> &VecDeque<MemoryEpisode<T>> {
&self.episodes
}
pub fn usage_stats_mut(&mut self) -> &mut MemoryUsageStats {
&mut self.usage_stats
}
pub fn usage_stats(&self) -> &MemoryUsageStats {
&self.usage_stats
}
pub fn eviction_policy(&self) -> EvictionPolicy {
self.config.eviction_policy
}
pub fn retrieve_similar(
&self,
task_data: &TaskData<T>,
k: usize,
) -> Result<Vec<MemoryEpisode<T>>> {
if self.episodes.is_empty() {
return Ok(Vec::new());
}
let count = k.min(self.episodes.len());
let result: Vec<MemoryEpisode<T>> =
self.episodes.iter().rev().take(count).cloned().collect();
let _ = task_data; Ok(result)
}
pub fn store_episode(
&mut self,
task_data: TaskData<T>,
result: AdaptationResult<T>,
) -> Result<()> {
let episode = MemoryEpisode {
episode_id: format!("ep_{}", self.usage_stats.total_episodes),
task_data,
adaptation_result: result,
timestamp: std::time::SystemTime::now(),
metadata: EpisodeMetadata {
difficulty: DifficultyLevel::Medium,
domain: DomainType::Optimization,
success_rate: 0.0,
tags: Vec::new(),
},
access_count: 0,
};
if self.episodes.len() >= self.config.max_memory_size {
self.episodes.pop_front();
}
self.episodes.push_back(episode);
self.usage_stats.total_episodes += 1;
self.usage_stats.memory_utilization =
self.episodes.len() as f64 / self.config.max_memory_size as f64;
Ok(())
}
}
impl<T: Float + Debug + Send + Sync + 'static> FastAdaptationEngine<T> {
pub fn new(config: FastAdaptationConfig) -> Result<Self> {
Ok(Self {
algorithms: Vec::new(),
config,
})
}
pub fn register_algorithm(&mut self, algorithm: Box<dyn FastAdaptationAlgorithm<T>>) {
self.algorithms.push(algorithm);
}
pub fn algorithm_count(&self) -> usize {
self.algorithms.len()
}
pub fn from_params(inner_lr: T, adaptation_steps: usize) -> Result<Self> {
let _ = (inner_lr, adaptation_steps);
Self::new(FastAdaptationConfig {
enable_caching: true,
enable_prediction: true,
max_adaptation_time: Duration::from_secs(60),
_performance_threshold: 0.8,
})
}
pub fn config(&self) -> &FastAdaptationConfig {
&self.config
}
pub fn adapt_fast(
&mut self,
optimizer: &mut dyn FewShotOptimizer<T>,
task_data: &TaskData<T>,
strategy: AdaptationStrategyType,
config: &AdaptationConfig,
) -> Result<AdaptationResult<T>> {
let start = Instant::now();
let label = format!("{strategy:?}");
let mut result =
if let Some(algorithm) = self.algorithms.iter_mut().find(|a| a.name() == label) {
algorithm.adapt_fast(optimizer, task_data, None)?
} else {
optimizer.adapt_few_shot(&task_data.support_set, &task_data.query_set, config)?
};
if result.task_representation.is_empty() {
result.task_representation = optimizer.get_task_representation(task_data)?;
}
result.resource_usage.total_time = start.elapsed();
Ok(result)
}
}
impl<T: Float + Debug + Send + Sync + 'static> FewShotPerformanceTracker<T> {
pub fn new(config: TrackingConfig) -> Result<Self> {
Ok(Self {
performance_history: VecDeque::new(),
config,
stats: PerformanceStats {
best_performance: T::zero(),
average_performance: T::zero(),
performance_variance: T::zero(),
improvement_rate: T::zero(),
success_rate: T::zero(),
},
})
}
pub fn record_performance(&mut self, result: &AdaptationResult<T>) -> Result<()> {
let record = PerformanceRecord {
task_id: String::new(),
performance: result.performance.query_performance,
adaptation_time: result.resource_usage.total_time,
strategy_used: String::new(),
timestamp: std::time::SystemTime::now(),
additional_metrics: HashMap::new(),
};
if self.performance_history.len() >= self.config.max_history_size {
self.performance_history.pop_front();
}
self.performance_history.push_back(record);
if !self.performance_history.is_empty() {
let mut sum = T::zero();
let mut best = T::neg_infinity();
for r in &self.performance_history {
sum = sum + r.performance;
if r.performance > best {
best = r.performance;
}
}
let count_t = scirs2_core::numeric::NumCast::from(self.performance_history.len())
.unwrap_or_else(|| T::one());
self.stats.average_performance = sum / count_t;
self.stats.best_performance = best;
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct TaskMetadata {
pub task_name: String,
pub domain: DomainType,
pub difficulty: DifficultyLevel,
pub created_at: std::time::SystemTime,
}
#[derive(Debug, Clone)]
pub struct ExampleMetadata {
pub source: String,
pub quality_score: f64,
pub created_at: std::time::SystemTime,
}
#[derive(Debug, Clone)]
pub struct SupportSetStatistics<T: Float + Debug + Send + Sync + 'static> {
pub mean: Array1<T>,
pub variance: Array1<T>,
pub size: usize,
pub diversity_score: T,
}
#[derive(Debug, Clone)]
pub struct QuerySetStatistics<T: Float + Debug + Send + Sync + 'static> {
pub mean: Array1<T>,
pub variance: Array1<T>,
pub size: usize,
}
#[derive(Debug, Clone, Copy)]
pub enum EvaluationMetric {
Accuracy,
Loss,
F1Score,
Precision,
Recall,
AUC,
MSE,
MAE,
FinalPerformance,
Efficiency,
TrainingTime,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_support_set_creation() {
let support_set = SupportSet::<f64> {
examples: vec![SupportExample {
features: Array1::from_vec(vec![1.0, 2.0, 3.0]),
target: 0.5,
weight: 1.0,
context: HashMap::new(),
metadata: ExampleMetadata {
source: "test".to_string(),
quality_score: 0.9,
created_at: std::time::SystemTime::now(),
},
}],
task_metadata: TaskMetadata {
task_name: "test_task".to_string(),
domain: DomainType::Optimization,
difficulty: DifficultyLevel::Easy,
created_at: std::time::SystemTime::now(),
},
statistics: SupportSetStatistics {
mean: Array1::from_vec(vec![1.0, 2.0, 3.0]),
variance: Array1::from_vec(vec![0.1, 0.1, 0.1]),
size: 1,
diversity_score: 0.8,
},
temporal_order: None,
};
assert_eq!(support_set.examples.len(), 1);
assert_eq!(support_set.statistics.size, 1);
}
#[test]
fn test_adaptation_config() {
let config = AdaptationConfig {
adaptation_steps: 10,
adaptation_lr: 0.01,
strategy: AdaptationStrategyType::MAML,
early_stopping: None,
regularization: RegularizationConfig {
l2_strength: 0.001,
dropout_rate: 0.1,
gradient_clip: Some(1.0),
task_regularization: HashMap::new(),
},
resource_constraints: ResourceConstraints {
max_time: Duration::from_secs(60),
max_memory_mb: 1024,
max_compute_budget: 100.0,
},
};
assert_eq!(config.adaptation_steps, 10);
assert_eq!(config.adaptation_lr, 0.01);
assert!(matches!(config.strategy, AdaptationStrategyType::MAML));
}
#[test]
fn test_domain_info() {
let domain_info = DomainInfo {
domain_type: DomainType::ComputerVision,
characteristics: DomainCharacteristics {
input_dim: 784,
output_dim: 10,
temporal: false,
stochasticity: 0.1,
noise_level: 0.05,
sparsity: 0.0,
},
difficulty_level: DifficultyLevel::Medium,
constraints: vec![DomainConstraint {
constraint_type: ConstraintType::LatencyRequirement,
description: "Max 100ms inference time".to_string(),
enforcement: ConstraintEnforcement::Hard,
}],
};
assert!(matches!(
domain_info.domain_type,
DomainType::ComputerVision
));
assert_eq!(domain_info.characteristics.input_dim, 784);
assert_eq!(domain_info.constraints.len(), 1);
}
struct RecordingOptimizer {
adapt_calls: usize,
}
impl FewShotOptimizer<f64> for RecordingOptimizer {
fn adapt_few_shot(
&mut self,
support_set: &SupportSet<f64>,
_query_set: &QuerySet<f64>,
_adaptation_config: &AdaptationConfig,
) -> Result<AdaptationResult<f64>> {
self.adapt_calls += 1;
let width = support_set
.examples
.first()
.map(|e| e.features.len())
.unwrap_or(1);
Ok(AdaptationResult {
adapted_state: OptimizerState {
parameters: Array1::zeros(width),
gradients: Array1::zeros(width),
momentum: None,
hidden_states: HashMap::new(),
memory_buffers: HashMap::new(),
step: 1,
step_count: 1,
loss: Some(0.05),
learning_rate: 0.01,
metadata: crate::StateMetadata::default(),
},
performance: AdaptationPerformance {
query_performance: 0.42,
support_performance: 0.37,
adaptation_speed: 2,
final_loss: 0.05,
improvement: 0.11,
stability: 0.5,
},
task_representation: Array1::zeros(0),
adaptation_trajectory: Vec::new(),
resource_usage: ResourceUsage {
total_time: Duration::from_secs(999),
peak_memory_mb: 1.0,
compute_cost: 1.0,
energy_consumption: 1.0,
},
})
}
fn get_task_representation(&self, _taskdata: &TaskData<f64>) -> Result<Array1<f64>> {
Ok(Array1::from_vec(vec![7.0, 8.0, 9.0]))
}
fn compute_adaptation_loss(
&self,
_support_set: &SupportSet<f64>,
_query_set: &QuerySet<f64>,
) -> Result<f64> {
Ok(0.1)
}
fn update_meta_parameters(&mut self, _metagradients: &MetaGradients<f64>) -> Result<()> {
Ok(())
}
fn get_transfer_state(&self) -> TransferState<f64> {
TransferState {
representations: HashMap::new(),
meta_parameters: HashMap::new(),
task_embeddings: Array2::zeros((1, 1)),
transfer_stats: TransferStatistics {
source_performance: 0.0,
target_performance: 0.0,
transfer_efficiency: 0.0,
steps_saved: 0,
},
}
}
fn load_transfer_state(&mut self, _state: TransferState<f64>) -> Result<()> {
Ok(())
}
}
fn make_task_data() -> TaskData<f64> {
TaskData {
task_id: "t".to_string(),
support_set: SupportSet {
examples: vec![SupportExample {
features: Array1::from_vec(vec![1.0, 2.0, 3.0]),
target: 0.5,
weight: 1.0,
context: HashMap::new(),
metadata: ExampleMetadata {
source: "test".to_string(),
quality_score: 0.9,
created_at: std::time::SystemTime::now(),
},
}],
task_metadata: TaskMetadata {
task_name: "test_task".to_string(),
domain: DomainType::Optimization,
difficulty: DifficultyLevel::Easy,
created_at: std::time::SystemTime::now(),
},
statistics: SupportSetStatistics {
mean: Array1::from_vec(vec![1.0, 2.0, 3.0]),
variance: Array1::from_vec(vec![0.1, 0.1, 0.1]),
size: 1,
diversity_score: 0.8,
},
temporal_order: None,
},
query_set: QuerySet {
examples: vec![QueryExample {
features: Array1::from_vec(vec![1.0, 2.0, 3.0]),
true_target: Some(0.4),
weight: 1.0,
context: HashMap::new(),
}],
statistics: QuerySetStatistics {
mean: Array1::from_vec(vec![1.0, 2.0, 3.0]),
variance: Array1::from_vec(vec![0.1, 0.1, 0.1]),
size: 1,
},
eval_metrics: vec![EvaluationMetric::MSE],
},
task_params: HashMap::new(),
domain_info: DomainInfo {
domain_type: DomainType::Optimization,
characteristics: DomainCharacteristics {
input_dim: 3,
output_dim: 1,
temporal: false,
stochasticity: 0.0,
noise_level: 0.0,
sparsity: 0.0,
},
difficulty_level: DifficultyLevel::Easy,
constraints: vec![],
},
}
}
fn make_adaptation_config() -> AdaptationConfig {
AdaptationConfig {
adaptation_steps: 5,
adaptation_lr: 0.01,
strategy: AdaptationStrategyType::MAML,
early_stopping: None,
regularization: RegularizationConfig {
l2_strength: 0.0,
dropout_rate: 0.0,
gradient_clip: None,
task_regularization: HashMap::new(),
},
resource_constraints: ResourceConstraints {
max_time: Duration::from_secs(1),
max_memory_mb: 64,
max_compute_budget: 1.0,
},
}
}
#[test]
fn test_adapt_fast_delegates_instead_of_fabricating() {
let mut engine = FastAdaptationEngine::<f64>::new(FastAdaptationConfig {
enable_caching: true,
enable_prediction: true,
max_adaptation_time: Duration::from_secs(1),
_performance_threshold: 0.8,
})
.expect("engine");
assert_eq!(
engine.algorithm_count(),
0,
"no algorithm registered by default"
);
let mut optimizer = RecordingOptimizer { adapt_calls: 0 };
let task_data = make_task_data();
let config = make_adaptation_config();
let result = engine
.adapt_fast(
&mut optimizer,
&task_data,
AdaptationStrategyType::MAML,
&config,
)
.expect("adapt_fast should succeed");
assert_eq!(
optimizer.adapt_calls, 1,
"adapt_fast must actually call the optimizer, not ignore it"
);
approx::assert_abs_diff_eq!(result.performance.query_performance, 0.42, epsilon = 1e-12);
approx::assert_abs_diff_eq!(
result.performance.support_performance,
0.37,
epsilon = 1e-12
);
assert_eq!(result.performance.adaptation_speed, 2);
assert_eq!(result.adapted_state.parameters.len(), 3);
assert_eq!(
result.task_representation.as_slice(),
Some(&[7.0, 8.0, 9.0][..])
);
assert!(result.resource_usage.total_time < Duration::from_secs(999));
}
#[test]
fn test_adapt_fast_prefers_a_registered_algorithm() {
struct AlwaysProto {
calls: usize,
}
impl FastAdaptationAlgorithm<f64> for AlwaysProto {
fn adapt_fast(
&mut self,
_optimizer: &mut dyn FewShotOptimizer<f64>,
_task_data: &TaskData<f64>,
_target_performance: Option<f64>,
) -> Result<AdaptationResult<f64>> {
self.calls += 1;
Ok(AdaptationResult {
adapted_state: OptimizerState {
parameters: Array1::zeros(2),
gradients: Array1::zeros(2),
momentum: None,
hidden_states: HashMap::new(),
memory_buffers: HashMap::new(),
step: 0,
step_count: 0,
loss: None,
learning_rate: 0.02,
metadata: crate::StateMetadata::default(),
},
performance: AdaptationPerformance {
query_performance: 0.77,
support_performance: 0.66,
adaptation_speed: 1,
final_loss: 0.2,
improvement: 0.3,
stability: 0.4,
},
task_representation: Array1::from_vec(vec![1.0]),
adaptation_trajectory: Vec::new(),
resource_usage: ResourceUsage {
total_time: Duration::from_secs(0),
peak_memory_mb: 0.0,
compute_cost: 0.0,
energy_consumption: 0.0,
},
})
}
fn estimate_adaptation_time(&self, _taskdata: &TaskData<f64>) -> Duration {
Duration::from_millis(1)
}
fn name(&self) -> &str {
"Prototypical"
}
}
let mut engine = FastAdaptationEngine::<f64>::new(FastAdaptationConfig {
enable_caching: false,
enable_prediction: false,
max_adaptation_time: Duration::from_secs(1),
_performance_threshold: 0.5,
})
.expect("engine");
engine.register_algorithm(Box::new(AlwaysProto { calls: 0 }));
assert_eq!(engine.algorithm_count(), 1);
let mut optimizer = RecordingOptimizer { adapt_calls: 0 };
let task_data = make_task_data();
let config = make_adaptation_config();
let result = engine
.adapt_fast(
&mut optimizer,
&task_data,
AdaptationStrategyType::Prototypical,
&config,
)
.expect("adapt_fast should succeed");
assert_eq!(
optimizer.adapt_calls, 0,
"the registered algorithm should have been used instead of the optimizer fallback"
);
approx::assert_abs_diff_eq!(result.performance.query_performance, 0.77, epsilon = 1e-12);
}
}