Skip to main content

optirs_learned/
few_shot.rs

1// Few-Shot Learning Enhancement for Optimizer Meta-Learning
2//
3// This module implements advanced few-shot learning techniques specifically designed
4// for quickly adapting optimizers to new tasks with minimal data. It includes
5// prototypical networks, meta-learning approaches, and rapid adaptation mechanisms.
6
7use scirs2_core::ndarray::{Array1, Array2};
8use scirs2_core::numeric::Float;
9use std::collections::{HashMap, VecDeque};
10use std::fmt::Debug;
11use std::time::{Duration, Instant};
12
13use super::OptimizerState;
14use crate::error::{OptimError, Result};
15
16pub mod prototypical;
17
18pub use prototypical::{activate, xavier_encoder_layer};
19
20/// Few-shot learning coordinator for optimizer adaptation
21pub struct FewShotLearningSystem<T: Float + Debug + Send + Sync + 'static> {
22    /// Base meta-learned optimizer
23    base_optimizer: Box<dyn FewShotOptimizer<T>>,
24
25    /// Prototypical network for task representation
26    prototype_network: PrototypicalNetwork<T>,
27
28    /// Memory bank for storing task experiences
29    memory_bank: EpisodicMemoryBank<T>,
30
31    /// Fast adaptation engine
32    fast_adaptation: FastAdaptationEngine<T>,
33
34    /// Performance tracker
35    performance_tracker: FewShotPerformanceTracker<T>,
36}
37
38/// Base trait for few-shot optimizers
39pub trait FewShotOptimizer<T: Float + Debug + Send + Sync + 'static>: Send + Sync {
40    /// Adapt to new task with few examples
41    fn adapt_few_shot(
42        &mut self,
43        support_set: &SupportSet<T>,
44        query_set: &QuerySet<T>,
45        adaptation_config: &AdaptationConfig,
46    ) -> Result<AdaptationResult<T>>;
47
48    /// Get task representation
49    fn get_task_representation(&self, taskdata: &TaskData<T>) -> Result<Array1<T>>;
50
51    /// Compute adaptation loss
52    fn compute_adaptation_loss(
53        &self,
54        support_set: &SupportSet<T>,
55        query_set: &QuerySet<T>,
56    ) -> Result<T>;
57
58    /// Update meta-parameters
59    fn update_meta_parameters(&mut self, metagradients: &MetaGradients<T>) -> Result<()>;
60
61    /// Get current state for transfer
62    fn get_transfer_state(&self) -> TransferState<T>;
63
64    /// Load transfer state
65    fn load_transfer_state(&mut self, state: TransferState<T>) -> Result<()>;
66}
67
68/// Support set for few-shot learning
69#[derive(Debug, Clone)]
70pub struct SupportSet<T: Float + Debug + Send + Sync + 'static> {
71    /// Support examples
72    pub examples: Vec<SupportExample<T>>,
73
74    /// Task metadata
75    pub task_metadata: TaskMetadata,
76
77    /// Support _set statistics
78    pub statistics: SupportSetStatistics<T>,
79
80    /// Temporal ordering (if applicable)
81    pub temporal_order: Option<Vec<usize>>,
82}
83
84/// Individual support example
85#[derive(Debug, Clone)]
86pub struct SupportExample<T: Float + Debug + Send + Sync + 'static> {
87    /// Input features
88    pub features: Array1<T>,
89
90    /// Target output
91    pub target: T,
92
93    /// Example weight/importance
94    pub weight: T,
95
96    /// Context information
97    pub context: HashMap<String, T>,
98
99    /// Example metadata
100    pub metadata: ExampleMetadata,
101}
102
103/// Query set for evaluation
104#[derive(Debug, Clone)]
105pub struct QuerySet<T: Float + Debug + Send + Sync + 'static> {
106    /// Query examples
107    pub examples: Vec<QueryExample<T>>,
108
109    /// Query statistics
110    pub statistics: QuerySetStatistics<T>,
111
112    /// Evaluation metrics
113    pub eval_metrics: Vec<EvaluationMetric>,
114}
115
116/// Individual query example
117#[derive(Debug, Clone)]
118pub struct QueryExample<T: Float + Debug + Send + Sync + 'static> {
119    /// Input features
120    pub features: Array1<T>,
121
122    /// True target (for evaluation)
123    pub true_target: Option<T>,
124
125    /// Query weight
126    pub weight: T,
127
128    /// Query context
129    pub context: HashMap<String, T>,
130}
131
132/// Task data container
133#[derive(Debug, Clone)]
134pub struct TaskData<T: Float + Debug + Send + Sync + 'static> {
135    /// Task identifier
136    pub task_id: String,
137
138    /// Support set
139    pub support_set: SupportSet<T>,
140
141    /// Query set
142    pub query_set: QuerySet<T>,
143
144    /// Task-specific parameters
145    pub task_params: HashMap<String, T>,
146
147    /// Task domain information
148    pub domain_info: DomainInfo,
149}
150
151/// Domain information
152#[derive(Debug, Clone)]
153pub struct DomainInfo {
154    /// Domain type
155    pub domain_type: DomainType,
156
157    /// Domain characteristics
158    pub characteristics: DomainCharacteristics,
159
160    /// Expected difficulty
161    pub difficulty_level: DifficultyLevel,
162
163    /// Domain-specific constraints
164    pub constraints: Vec<DomainConstraint>,
165}
166
167/// Domain types
168#[derive(Debug, Clone, Copy)]
169pub enum DomainType {
170    ComputerVision,
171    NaturalLanguageProcessing,
172    ReinforcementLearning,
173    TimeSeriesForecasting,
174    ScientificComputing,
175    Optimization,
176    ControlSystems,
177    GamePlaying,
178    Robotics,
179    Healthcare,
180}
181
182/// Domain characteristics
183#[derive(Debug, Clone)]
184pub struct DomainCharacteristics {
185    /// Input dimensionality
186    pub input_dim: usize,
187
188    /// Output dimensionality
189    pub output_dim: usize,
190
191    /// Temporal dependencies
192    pub temporal: bool,
193
194    /// Stochasticity level
195    pub stochasticity: f64,
196
197    /// Noise level
198    pub noise_level: f64,
199
200    /// Data sparsity
201    pub sparsity: f64,
202}
203
204/// Difficulty levels
205#[derive(Debug, Clone, Copy)]
206pub enum DifficultyLevel {
207    Trivial,
208    Easy,
209    Medium,
210    Hard,
211    Expert,
212    Extreme,
213}
214
215/// Domain constraints
216#[derive(Debug, Clone)]
217pub struct DomainConstraint {
218    /// Constraint type
219    pub constraint_type: ConstraintType,
220
221    /// Constraint description
222    pub description: String,
223
224    /// Enforcement level
225    pub enforcement: ConstraintEnforcement,
226}
227
228/// Constraint types
229#[derive(Debug, Clone, Copy)]
230pub enum ConstraintType {
231    ResourceLimit,
232    TemporalConstraint,
233    AccuracyRequirement,
234    LatencyRequirement,
235    MemoryConstraint,
236    EnergyConstraint,
237    SafetyConstraint,
238}
239
240/// Constraint enforcement levels
241#[derive(Debug, Clone, Copy)]
242pub enum ConstraintEnforcement {
243    Hard,
244    Soft,
245    Advisory,
246}
247
248/// Adaptation configuration
249#[derive(Debug, Clone)]
250pub struct AdaptationConfig {
251    /// Number of adaptation steps
252    pub adaptation_steps: usize,
253
254    /// Learning rate for adaptation
255    pub adaptation_lr: f64,
256
257    /// Adaptation strategy
258    pub strategy: AdaptationStrategyType,
259
260    /// Early stopping configuration
261    pub early_stopping: Option<EarlyStoppingConfig>,
262
263    /// Regularization parameters
264    pub regularization: RegularizationConfig,
265
266    /// Resource constraints
267    pub resource_constraints: ResourceConstraints,
268}
269
270/// Adaptation strategy types
271#[derive(Debug, Clone, Copy)]
272pub enum AdaptationStrategyType {
273    /// Model-Agnostic Meta-Learning (MAML)
274    MAML,
275
276    /// First-Order MAML (FOMAML)
277    FOMAML,
278
279    /// Prototypical Networks
280    Prototypical,
281
282    /// Matching Networks
283    Matching,
284
285    /// Relation Networks
286    Relation,
287
288    /// Meta-SGD
289    MetaSGD,
290
291    /// Learned optimizer approach
292    LearnedOptimizer,
293
294    /// Gradient-based meta-learning
295    GradientBased,
296
297    /// Memory-augmented networks
298    MemoryAugmented,
299}
300
301/// Early stopping configuration
302#[derive(Debug, Clone)]
303pub struct EarlyStoppingConfig {
304    /// Patience (steps without improvement)
305    pub patience: usize,
306
307    /// Minimum improvement threshold
308    pub min_improvement: f64,
309
310    /// Validation frequency
311    pub validation_frequency: usize,
312}
313
314/// Regularization configuration
315#[derive(Debug, Clone)]
316pub struct RegularizationConfig {
317    /// L2 regularization strength
318    pub l2_strength: f64,
319
320    /// Dropout rate
321    pub dropout_rate: f64,
322
323    /// Gradient clipping threshold
324    pub gradient_clip: Option<f64>,
325
326    /// Task-specific regularization
327    pub task_regularization: HashMap<String, f64>,
328}
329
330/// Resource constraints for adaptation
331#[derive(Debug, Clone)]
332pub struct ResourceConstraints {
333    /// Maximum adaptation time
334    pub max_time: Duration,
335
336    /// Maximum memory usage (MB)
337    pub max_memory_mb: usize,
338
339    /// Maximum computational budget
340    pub max_compute_budget: f64,
341}
342
343/// Adaptation result
344#[derive(Debug, Clone)]
345pub struct AdaptationResult<T: Float + Debug + Send + Sync + 'static> {
346    /// Adapted optimizer state
347    pub adapted_state: OptimizerState<T>,
348
349    /// Adaptation performance
350    pub performance: AdaptationPerformance<T>,
351
352    /// Task representation learned
353    pub task_representation: Array1<T>,
354
355    /// Adaptation trajectory
356    pub adaptation_trajectory: Vec<AdaptationStep<T>>,
357
358    /// Resource usage
359    pub resource_usage: ResourceUsage<T>,
360}
361
362/// Adaptation performance metrics
363#[derive(Debug, Clone)]
364pub struct AdaptationPerformance<T: Float + Debug + Send + Sync + 'static> {
365    /// Query set performance
366    pub query_performance: T,
367
368    /// Support set performance
369    pub support_performance: T,
370
371    /// Adaptation speed (steps to convergence)
372    pub adaptation_speed: usize,
373
374    /// Final loss
375    pub final_loss: T,
376
377    /// Performance improvement
378    pub improvement: T,
379
380    /// Stability measure
381    pub stability: T,
382}
383
384/// Individual adaptation step
385#[derive(Debug, Clone)]
386pub struct AdaptationStep<T: Float + Debug + Send + Sync + 'static> {
387    /// Step number
388    pub step: usize,
389
390    /// Loss at this step
391    pub loss: T,
392
393    /// Performance at this step
394    pub performance: T,
395
396    /// Gradient norm
397    pub gradient_norm: T,
398
399    /// Step time
400    pub step_time: Duration,
401}
402
403/// Resource usage tracking
404#[derive(Debug, Clone)]
405pub struct ResourceUsage<T: Float + Debug + Send + Sync + 'static> {
406    /// Total time taken
407    pub total_time: Duration,
408
409    /// Peak memory usage (MB)
410    pub peak_memory_mb: T,
411
412    /// Computational cost
413    pub compute_cost: T,
414
415    /// Energy consumption
416    pub energy_consumption: T,
417}
418
419/// Meta-gradients for updating meta-parameters
420#[derive(Debug, Clone)]
421pub struct MetaGradients<T: Float + Debug + Send + Sync + 'static> {
422    /// Parameter gradients
423    pub param_gradients: HashMap<String, Array1<T>>,
424
425    /// Learning rate gradients
426    pub lr_gradients: HashMap<String, T>,
427
428    /// Architecture gradients
429    pub arch_gradients: HashMap<String, Array1<T>>,
430
431    /// Meta-gradient norm
432    pub gradient_norm: T,
433}
434
435/// Transfer state for cross-task transfer
436#[derive(Debug, Clone)]
437pub struct TransferState<T: Float + Debug + Send + Sync + 'static> {
438    /// Learned representations
439    pub representations: HashMap<String, Array1<T>>,
440
441    /// Meta-parameters
442    pub meta_parameters: HashMap<String, Array1<T>>,
443
444    /// Task embeddings
445    pub task_embeddings: Array2<T>,
446
447    /// Transfer statistics
448    pub transfer_stats: TransferStatistics<T>,
449}
450
451/// Transfer statistics
452#[derive(Debug, Clone)]
453pub struct TransferStatistics<T: Float + Debug + Send + Sync + 'static> {
454    /// Source task performance
455    pub source_performance: T,
456
457    /// Target task performance
458    pub target_performance: T,
459
460    /// Transfer efficiency
461    pub transfer_efficiency: T,
462
463    /// Adaptation steps saved
464    pub steps_saved: usize,
465}
466
467/// Prototypical network for task representation
468pub struct PrototypicalNetwork<T: Float + Debug + Send + Sync + 'static> {
469    /// Encoder network. Visible to the `prototypical` child module, which owns
470    /// the encoder's initialization and forward pass.
471    pub(super) encoder: EncoderNetwork<T>,
472
473    /// Prototype storage
474    prototypes: HashMap<String, Prototype<T>>,
475
476    /// Distance metric
477    distance_metric: DistanceMetric,
478
479    /// Network parameters
480    parameters: PrototypicalNetworkParams<T>,
481}
482
483/// Encoder network for feature extraction
484#[derive(Debug)]
485pub struct EncoderNetwork<T: Float + Debug + Send + Sync + 'static> {
486    /// Network layers
487    layers: Vec<EncoderLayer<T>>,
488
489    /// Activation function
490    activation: ActivationFunction,
491}
492
493/// Individual encoder layer
494#[derive(Debug)]
495pub struct EncoderLayer<T: Float + Debug + Send + Sync + 'static> {
496    /// Weight matrix
497    pub weights: Array2<T>,
498
499    /// Bias vector
500    pub bias: Array1<T>,
501
502    /// Layer type
503    pub layer_type: LayerType,
504}
505
506/// Layer types
507#[derive(Debug, Clone, Copy)]
508pub enum LayerType {
509    Linear,
510    Convolutional,
511    Recurrent,
512    Attention,
513    Residual,
514}
515
516/// Activation functions
517#[derive(Debug, Clone, Copy)]
518pub enum ActivationFunction {
519    ReLU,
520    Tanh,
521    Sigmoid,
522    Swish,
523    GELU,
524    Mish,
525}
526
527/// Task prototypes
528#[derive(Debug, Clone)]
529pub struct Prototype<T: Float + Debug + Send + Sync + 'static> {
530    /// Prototype vector
531    pub vector: Array1<T>,
532
533    /// Prototype confidence
534    pub confidence: T,
535
536    /// Number of examples used
537    pub example_count: usize,
538
539    /// Last update time
540    pub last_updated: std::time::SystemTime,
541
542    /// Prototype metadata
543    pub metadata: PrototypeMetadata,
544}
545
546/// Prototype metadata
547#[derive(Debug, Clone)]
548pub struct PrototypeMetadata {
549    /// Task category
550    pub task_category: String,
551
552    /// Domain type
553    pub domain: DomainType,
554
555    /// Creation timestamp
556    pub created_at: std::time::SystemTime,
557
558    /// Update count
559    pub update_count: usize,
560}
561
562/// Distance metrics for prototype comparison
563#[derive(Debug, Clone, Copy)]
564pub enum DistanceMetric {
565    Euclidean,
566    Cosine,
567    Manhattan,
568    Mahalanobis,
569    Learned,
570}
571
572/// Prototypical network parameters
573#[derive(Debug, Clone)]
574pub struct PrototypicalNetworkParams<T: Float + Debug + Send + Sync + 'static> {
575    /// Embedding dimension
576    pub embedding_dim: usize,
577
578    /// Learning rate
579    pub learning_rate: T,
580
581    /// Temperature parameter
582    pub temperature: T,
583
584    /// Prototype update rate
585    pub prototype_update_rate: T,
586}
587
588/// Support set manager.
589///
590/// Currently a validated holder for [`SupportSetManagerConfig`]: the support-set
591/// store and selection strategy it declared were never written to or consulted
592/// by anything, so they have been removed rather than left as write-only state.
593pub struct SupportSetManager<T: Float + Debug + Send + Sync + 'static> {
594    /// Manager configuration
595    config: SupportSetManagerConfig,
596
597    _element: std::marker::PhantomData<T>,
598}
599
600/// Support set manager configuration
601#[derive(Debug, Clone)]
602pub struct SupportSetManagerConfig {
603    /// Minimum support set size
604    pub min_support_size: usize,
605
606    /// Maximum support set size
607    pub max_support_size: usize,
608
609    /// Quality threshold
610    pub quality_threshold: f64,
611
612    /// Enable augmentation
613    pub enable_augmentation: bool,
614
615    /// Cache support sets
616    pub cache_support_sets: bool,
617}
618
619/// Adaptation strategy trait
620pub trait AdaptationStrategy<T: Float + Debug + Send + Sync + 'static>: Send + Sync {
621    /// Perform adaptation
622    fn adapt(
623        &mut self,
624        optimizer: &mut dyn FewShotOptimizer<T>,
625        task_data: &TaskData<T>,
626        config: &AdaptationConfig,
627    ) -> Result<AdaptationResult<T>>;
628
629    /// Get strategy name
630    fn name(&self) -> &str;
631
632    /// Get strategy parameters
633    fn parameters(&self) -> HashMap<String, f64>;
634
635    /// Update strategy parameters
636    fn update_parameters(&mut self, params: HashMap<String, f64>) -> Result<()>;
637}
638
639/// Task similarity calculator
640pub struct TaskSimilarityCalculator<T: Float + Debug + Send + Sync + 'static> {
641    /// Similarity cache
642    similarity_cache: HashMap<(String, String), T>,
643
644    /// Calculator configuration
645    config: SimilarityCalculatorConfig<T>,
646}
647
648/// Similarity metric trait
649pub trait SimilarityMetric<T: Float + Debug + Send + Sync + 'static>: Send + Sync {
650    /// Calculate similarity between tasks
651    fn calculate_similarity(&self, task1: &TaskData<T>, task2: &TaskData<T>) -> Result<T>;
652
653    /// Get metric name
654    fn name(&self) -> &str;
655
656    /// Get metric weight
657    fn weight(&self) -> T;
658}
659
660/// Similarity calculator configuration
661#[derive(Debug, Clone)]
662pub struct SimilarityCalculatorConfig<T: Float + Debug + Send + Sync + 'static> {
663    /// Enable caching
664    pub enable_caching: bool,
665
666    /// Cache size limit
667    pub cache_size_limit: usize,
668
669    /// Similarity threshold
670    pub similarity_threshold: T,
671
672    /// Use task metadata
673    pub use_metadata: bool,
674}
675
676/// Episodic memory bank for storing task experiences
677pub struct EpisodicMemoryBank<T: Float + Debug + Send + Sync + 'static> {
678    /// Memory episodes
679    episodes: VecDeque<MemoryEpisode<T>>,
680
681    /// Memory configuration
682    config: MemoryBankConfig<T>,
683
684    /// Usage statistics
685    usage_stats: MemoryUsageStats,
686}
687
688/// Memory episode
689#[derive(Debug, Clone)]
690pub struct MemoryEpisode<T: Float + Debug + Send + Sync + 'static> {
691    /// Episode ID
692    pub episode_id: String,
693
694    /// Task data
695    pub task_data: TaskData<T>,
696
697    /// Adaptation result
698    pub adaptation_result: AdaptationResult<T>,
699
700    /// Episode timestamp
701    pub timestamp: std::time::SystemTime,
702
703    /// Episode metadata
704    pub metadata: EpisodeMetadata,
705
706    /// Access count
707    pub access_count: usize,
708}
709
710/// Episode metadata
711#[derive(Debug, Clone)]
712pub struct EpisodeMetadata {
713    /// Task difficulty
714    pub difficulty: DifficultyLevel,
715
716    /// Domain type
717    pub domain: DomainType,
718
719    /// Success rate
720    pub success_rate: f64,
721
722    /// Tags
723    pub tags: Vec<String>,
724}
725
726/// Memory bank configuration
727#[derive(Debug, Clone)]
728pub struct MemoryBankConfig<T: Float + Debug + Send + Sync + 'static> {
729    /// Maximum memory size
730    pub max_memory_size: usize,
731
732    /// Eviction policy
733    pub eviction_policy: EvictionPolicy,
734
735    /// Similarity threshold for retrieval
736    pub similarity_threshold: T,
737
738    /// Enable compression
739    pub enable_compression: bool,
740}
741
742/// Memory eviction policies
743#[derive(Debug, Clone, Copy)]
744pub enum EvictionPolicy {
745    LRU,         // Least Recently Used
746    LFU,         // Least Frequently Used
747    Performance, // Worst Performing
748    Age,         // Oldest First
749    Random,
750}
751
752/// Memory bank statistics (summary view)
753#[derive(Debug, Clone)]
754pub struct MemoryBankStats<T: Float + Debug + Send + Sync + 'static> {
755    /// Number of stored episodes
756    pub count: usize,
757
758    /// Average performance across episodes
759    pub avg_performance: T,
760
761    /// Capacity used as a fraction
762    pub capacity_used: f64,
763
764    /// Total capacity
765    pub total_capacity: usize,
766}
767
768/// Memory usage statistics
769#[derive(Debug, Clone)]
770pub struct MemoryUsageStats {
771    /// Total episodes stored
772    pub total_episodes: usize,
773
774    /// Memory utilization
775    pub memory_utilization: f64,
776
777    /// Hit rate
778    pub hit_rate: f64,
779
780    /// Average retrieval time
781    pub avg_retrieval_time: Duration,
782}
783
784/// Fast adaptation engine
785pub struct FastAdaptationEngine<T: Float + Debug + Send + Sync + 'static> {
786    /// Adaptation algorithms
787    algorithms: Vec<Box<dyn FastAdaptationAlgorithm<T>>>,
788
789    /// Engine configuration
790    config: FastAdaptationConfig,
791}
792
793/// Fast adaptation algorithm trait
794pub trait FastAdaptationAlgorithm<T: Float + Debug + Send + Sync + 'static>: Send + Sync {
795    /// Perform fast adaptation
796    fn adapt_fast(
797        &mut self,
798        optimizer: &mut dyn FewShotOptimizer<T>,
799        task_data: &TaskData<T>,
800        target_performance: Option<T>,
801    ) -> Result<AdaptationResult<T>>;
802
803    /// Estimate adaptation time
804    fn estimate_adaptation_time(&self, taskdata: &TaskData<T>) -> Duration;
805
806    /// Get algorithm name
807    fn name(&self) -> &str;
808}
809
810/// Fast adaptation configuration
811#[derive(Debug, Clone)]
812pub struct FastAdaptationConfig {
813    /// Enable caching
814    pub enable_caching: bool,
815
816    /// Enable prediction
817    pub enable_prediction: bool,
818
819    /// Maximum adaptation time
820    pub max_adaptation_time: Duration,
821
822    /// Performance threshold
823    pub _performance_threshold: f64,
824}
825
826/// Performance tracker for few-shot learning
827pub struct FewShotPerformanceTracker<T: Float + Debug + Send + Sync + 'static> {
828    /// Performance history
829    performance_history: VecDeque<PerformanceRecord<T>>,
830
831    /// Tracking configuration
832    config: TrackingConfig,
833
834    /// Performance statistics
835    stats: PerformanceStats<T>,
836}
837
838/// Performance record
839#[derive(Debug, Clone)]
840pub struct PerformanceRecord<T: Float + Debug + Send + Sync + 'static> {
841    /// Task ID
842    pub task_id: String,
843
844    /// Performance value
845    pub performance: T,
846
847    /// Adaptation time
848    pub adaptation_time: Duration,
849
850    /// Strategy used
851    pub strategy_used: String,
852
853    /// Timestamp
854    pub timestamp: std::time::SystemTime,
855
856    /// Additional metrics
857    pub additional_metrics: HashMap<String, T>,
858}
859
860/// Performance metric trait
861pub trait PerformanceMetric<T: Float + Debug + Send + Sync + 'static>: Send + Sync {
862    /// Calculate performance metric
863    fn calculate(&self, records: &[PerformanceRecord<T>]) -> Result<T>;
864
865    /// Get metric name
866    fn name(&self) -> &str;
867
868    /// Is higher better
869    fn higher_is_better(&self) -> bool;
870}
871
872/// Tracking configuration
873#[derive(Debug, Clone)]
874pub struct TrackingConfig {
875    /// Maximum history size
876    pub max_history_size: usize,
877
878    /// Update frequency
879    pub update_frequency: Duration,
880
881    /// Enable detailed tracking
882    pub detailed_tracking: bool,
883
884    /// Export results
885    pub export_results: bool,
886}
887
888/// Performance statistics
889#[derive(Debug)]
890pub struct PerformanceStats<T: Float + Debug + Send + Sync + 'static> {
891    /// Best performance
892    pub best_performance: T,
893
894    /// Average performance
895    pub average_performance: T,
896
897    /// Performance variance
898    pub performance_variance: T,
899
900    /// Improvement rate
901    pub improvement_rate: T,
902
903    /// Success rate
904    pub success_rate: T,
905}
906
907// Implementation stubs for key structures
908impl<T: Float + Debug + Send + Sync + 'static> FewShotLearningSystem<T> {
909    /// Create new few-shot learning system
910    pub fn new(
911        base_optimizer: Box<dyn FewShotOptimizer<T>>,
912        config: FewShotConfig<T>,
913    ) -> Result<Self> {
914        Ok(Self {
915            base_optimizer,
916            prototype_network: PrototypicalNetwork::new(config.prototype_config)?,
917            memory_bank: EpisodicMemoryBank::new(config.memory_config)?,
918            fast_adaptation: FastAdaptationEngine::new(config.adaptation_config)?,
919            performance_tracker: FewShotPerformanceTracker::new(config.tracking_config)?,
920        })
921    }
922
923    /// Learn from few examples
924    pub fn learn_few_shot(
925        &mut self,
926        task_data: TaskData<T>,
927        adaptation_config: AdaptationConfig,
928    ) -> Result<AdaptationResult<T>> {
929        // Comprehensive few-shot learning implementation
930        let _start_time = Instant::now();
931
932        // 1. Extract task representation using prototypical network
933        let task_representation = self.prototype_network.encode_task(&task_data)?;
934
935        // 2. Retrieve similar tasks from memory
936        let similar_tasks = self.memory_bank.retrieve_similar(&task_data, 5)?;
937
938        // 3. Select best adaptation strategy based on task characteristics
939        let strategy = self.select_adaptation_strategy(&task_data, &similar_tasks)?;
940
941        // 4. Perform fast adaptation
942        let mut adaptation_result = self.fast_adaptation.adapt_fast(
943            &mut *self.base_optimizer,
944            &task_data,
945            strategy,
946            &adaptation_config,
947        )?;
948
949        // 5. Update task representation
950        adaptation_result.task_representation = task_representation;
951
952        // 6. Store experience in memory bank
953        self.memory_bank
954            .store_episode(task_data.clone(), adaptation_result.clone())?;
955
956        // 7. Update performance tracker
957        self.performance_tracker
958            .record_performance(&adaptation_result)?;
959
960        // 8. Update prototypical network with new experience
961        self.prototype_network
962            .update_prototypes(&task_data, &adaptation_result)?;
963
964        Ok(adaptation_result)
965    }
966
967    fn select_adaptation_strategy(
968        &self,
969        task_data: &TaskData<T>,
970        _similar_tasks: &[MemoryEpisode<T>],
971    ) -> Result<AdaptationStrategyType> {
972        // Strategy selection based on task characteristics and historical performance
973        match task_data.domain_info.difficulty_level {
974            DifficultyLevel::Trivial | DifficultyLevel::Easy => Ok(AdaptationStrategyType::FOMAML),
975            DifficultyLevel::Medium => Ok(AdaptationStrategyType::MAML),
976            DifficultyLevel::Hard | DifficultyLevel::Expert => {
977                Ok(AdaptationStrategyType::Prototypical)
978            }
979            DifficultyLevel::Extreme => Ok(AdaptationStrategyType::MemoryAugmented),
980        }
981    }
982}
983
984/// Few-shot learning system configuration
985#[derive(Debug, Clone)]
986pub struct FewShotConfig<T: Float + Debug + Send + Sync + 'static> {
987    /// Prototypical network configuration
988    pub prototype_config: PrototypicalNetworkConfig<T>,
989
990    /// Support set management configuration
991    pub support_set_config: SupportSetManagerConfig,
992
993    /// Similarity calculation configuration
994    pub similarity_config: SimilarityCalculatorConfig<T>,
995
996    /// Memory bank configuration
997    pub memory_config: MemoryBankConfig<T>,
998
999    /// Fast adaptation configuration
1000    pub adaptation_config: FastAdaptationConfig,
1001
1002    /// Performance tracking configuration
1003    pub tracking_config: TrackingConfig,
1004}
1005
1006/// Prototypical network configuration
1007#[derive(Debug, Clone)]
1008pub struct PrototypicalNetworkConfig<T: Float + Debug + Send + Sync + 'static> {
1009    /// Embedding dimension
1010    pub embedding_dim: usize,
1011
1012    /// Learning rate
1013    pub learning_rate: T,
1014
1015    /// Number of encoder layers
1016    pub num_layers: usize,
1017
1018    /// Hidden dimension
1019    pub hidden_dim: usize,
1020}
1021
1022// Constructors and core methods for major components
1023impl<T: Float + Debug + Send + Sync + 'static> PrototypicalNetwork<T> {
1024    /// Create a new prototypical network from configuration
1025    pub fn new(config: PrototypicalNetworkConfig<T>) -> Result<Self> {
1026        if config.embedding_dim == 0 {
1027            return Err(OptimError::InvalidConfig(
1028                "embedding_dim must be > 0".to_string(),
1029            ));
1030        }
1031        // Build a single-layer encoder: input_dim -> embedding_dim.
1032        // Xavier-initialized, not zeros: a zero-weight encoder outputs the zero
1033        // vector for every input and has a zero gradient, so it can never learn.
1034        let input_dim = config.hidden_dim.max(config.embedding_dim);
1035        let layer = xavier_encoder_layer::<T>(input_dim, config.embedding_dim, LayerType::Linear)?;
1036        Ok(Self {
1037            encoder: EncoderNetwork {
1038                layers: vec![layer],
1039                activation: ActivationFunction::ReLU,
1040            },
1041            prototypes: HashMap::new(),
1042            distance_metric: DistanceMetric::Euclidean,
1043            parameters: PrototypicalNetworkParams {
1044                embedding_dim: config.embedding_dim,
1045                learning_rate: config.learning_rate,
1046                temperature: T::one(),
1047                prototype_update_rate: scirs2_core::numeric::NumCast::from(0.1)
1048                    .unwrap_or_else(|| T::one()),
1049            },
1050        })
1051    }
1052
1053    /// Create from embedding dimension and number of classes (convenience)
1054    pub fn from_dims(embedding_dim: usize, _num_classes: usize) -> Result<Self> {
1055        if embedding_dim == 0 {
1056            return Err(OptimError::InvalidConfig(
1057                "embedding_dim must be > 0".to_string(),
1058            ));
1059        }
1060        let layer = xavier_encoder_layer::<T>(embedding_dim, embedding_dim, LayerType::Linear)?;
1061        Ok(Self {
1062            encoder: EncoderNetwork {
1063                layers: vec![layer],
1064                activation: ActivationFunction::ReLU,
1065            },
1066            prototypes: HashMap::new(),
1067            distance_metric: DistanceMetric::Euclidean,
1068            parameters: PrototypicalNetworkParams {
1069                embedding_dim,
1070                learning_rate: scirs2_core::numeric::NumCast::from(0.01)
1071                    .unwrap_or_else(|| T::one()),
1072                temperature: T::one(),
1073                prototype_update_rate: scirs2_core::numeric::NumCast::from(0.1)
1074                    .unwrap_or_else(|| T::one()),
1075            },
1076        })
1077    }
1078
1079    /// Get the embedding dimension
1080    pub fn embedding_dim(&self) -> usize {
1081        self.parameters.embedding_dim
1082    }
1083
1084    /// Get encoder layers (for projection)
1085    pub fn encoder_layers(&self) -> &[EncoderLayer<T>] {
1086        &self.encoder.layers
1087    }
1088
1089    /// Get mutable access to prototypes
1090    pub fn prototypes_mut(&mut self) -> &mut HashMap<String, Prototype<T>> {
1091        &mut self.prototypes
1092    }
1093
1094    /// Get read access to prototypes
1095    pub fn prototypes(&self) -> &HashMap<String, Prototype<T>> {
1096        &self.prototypes
1097    }
1098
1099    /// Get the distance metric
1100    pub fn distance_metric(&self) -> &DistanceMetric {
1101        &self.distance_metric
1102    }
1103
1104    /// Encode a task into an embedding vector.
1105    ///
1106    /// Standard prototypical-network form (Snell et al. 2017): every support
1107    /// example is passed through the learned encoder `f_φ` and the prototype is
1108    /// the **mean of the embeddings**. Previously this computed a truncated
1109    /// coordinate-wise mean of the *raw* features and never touched
1110    /// `self.encoder` at all, so the encoder was dead weight and the
1111    /// "embedding" was just the input.
1112    ///
1113    /// # Errors
1114    /// Returns `Err` when the support set is empty or the encoder rejects the
1115    /// feature width.
1116    pub fn encode_task(&self, task_data: &TaskData<T>) -> Result<Array1<T>> {
1117        if task_data.support_set.examples.is_empty() {
1118            return Err(OptimError::InsufficientData(
1119                "No support examples for encoding".to_string(),
1120            ));
1121        }
1122
1123        let width = self.encoder.embedding_width()?;
1124        let mut sum = Array1::<T>::zeros(width);
1125        let count = task_data.support_set.examples.len();
1126        for ex in &task_data.support_set.examples {
1127            let embedded = self.encoder.forward(&ex.features)?;
1128            for i in 0..width {
1129                sum[i] = sum[i] + embedded[i];
1130            }
1131        }
1132        let count_t = scirs2_core::numeric::NumCast::from(count).unwrap_or_else(|| T::one());
1133        for slot in sum.iter_mut() {
1134            *slot = *slot / count_t;
1135        }
1136        Ok(sum)
1137    }
1138
1139    /// Width of the embedding [`Self::encode_task`] produces.
1140    ///
1141    /// # Errors
1142    /// Returns `Err` when the encoder has no layers.
1143    pub fn embedding_width(&self) -> Result<usize> {
1144        self.encoder.embedding_width()
1145    }
1146
1147    /// Update prototypes with new experience
1148    pub fn update_prototypes(
1149        &mut self,
1150        task_data: &TaskData<T>,
1151        _result: &AdaptationResult<T>,
1152    ) -> Result<()> {
1153        let task_repr = self.encode_task(task_data)?;
1154        let task_id = task_data.task_id.clone();
1155        let update_rate = self.parameters.prototype_update_rate;
1156        let one_minus = T::one() - update_rate;
1157
1158        if let Some(proto) = self.prototypes.get_mut(&task_id) {
1159            // Exponential moving average update
1160            let dim = proto.vector.len().min(task_repr.len());
1161            for i in 0..dim {
1162                proto.vector[i] = one_minus * proto.vector[i] + update_rate * task_repr[i];
1163            }
1164            proto.example_count += task_data.support_set.examples.len();
1165            proto.last_updated = std::time::SystemTime::now();
1166            proto.metadata.update_count += 1;
1167        } else {
1168            let proto = Prototype {
1169                vector: task_repr,
1170                confidence: T::one(),
1171                example_count: task_data.support_set.examples.len(),
1172                last_updated: std::time::SystemTime::now(),
1173                metadata: PrototypeMetadata {
1174                    task_category: task_id.clone(),
1175                    domain: task_data.domain_info.domain_type,
1176                    created_at: std::time::SystemTime::now(),
1177                    update_count: 1,
1178                },
1179            };
1180            self.prototypes.insert(task_id, proto);
1181        }
1182        Ok(())
1183    }
1184}
1185
1186impl<T: Float + Debug + Send + Sync + 'static> SupportSetManager<T> {
1187    /// Create a new support set manager
1188    pub fn new(config: SupportSetManagerConfig) -> Result<Self> {
1189        if config.max_support_size == 0 {
1190            return Err(OptimError::InvalidConfig(
1191                "max_support_size must be > 0".to_string(),
1192            ));
1193        }
1194        Ok(Self {
1195            config,
1196            _element: std::marker::PhantomData,
1197        })
1198    }
1199
1200    /// Create from max support size (convenience)
1201    pub fn from_max_size(max_support_size: usize) -> Result<Self> {
1202        Self::new(SupportSetManagerConfig {
1203            min_support_size: 1,
1204            max_support_size,
1205            quality_threshold: 0.5,
1206            enable_augmentation: true,
1207            cache_support_sets: true,
1208        })
1209    }
1210
1211    /// Get the max support size
1212    pub fn max_support_size(&self) -> usize {
1213        self.config.max_support_size
1214    }
1215
1216    /// Get the config
1217    pub fn config(&self) -> &SupportSetManagerConfig {
1218        &self.config
1219    }
1220}
1221
1222impl<T: Float + Debug + Send + Sync + 'static> TaskSimilarityCalculator<T> {
1223    /// Create a new task similarity calculator
1224    pub fn new(config: SimilarityCalculatorConfig<T>) -> Result<Self> {
1225        Ok(Self {
1226            similarity_cache: HashMap::new(),
1227            config,
1228        })
1229    }
1230
1231    /// Create with default configuration
1232    pub fn default_new() -> Result<Self> {
1233        Self::new(SimilarityCalculatorConfig {
1234            enable_caching: true,
1235            cache_size_limit: 1000,
1236            similarity_threshold: scirs2_core::numeric::NumCast::from(0.5)
1237                .unwrap_or_else(|| T::zero()),
1238            use_metadata: true,
1239        })
1240    }
1241
1242    /// Get mutable access to the similarity cache
1243    pub fn similarity_cache_mut(&mut self) -> &mut HashMap<(String, String), T> {
1244        &mut self.similarity_cache
1245    }
1246
1247    /// Get whether caching is enabled
1248    pub fn caching_enabled(&self) -> bool {
1249        self.config.enable_caching
1250    }
1251}
1252
1253impl<T: Float + Debug + Send + Sync + 'static> EpisodicMemoryBank<T> {
1254    /// Create a new episodic memory bank
1255    pub fn new(config: MemoryBankConfig<T>) -> Result<Self> {
1256        if config.max_memory_size == 0 {
1257            return Err(OptimError::InvalidConfig(
1258                "max_memory_size must be > 0".to_string(),
1259            ));
1260        }
1261        Ok(Self {
1262            episodes: VecDeque::new(),
1263            config,
1264            usage_stats: MemoryUsageStats {
1265                total_episodes: 0,
1266                memory_utilization: 0.0,
1267                hit_rate: 0.0,
1268                avg_retrieval_time: Duration::from_secs(0),
1269            },
1270        })
1271    }
1272
1273    /// Create from capacity (convenience)
1274    pub fn from_capacity(capacity: usize) -> Result<Self> {
1275        Self::new(MemoryBankConfig {
1276            max_memory_size: capacity,
1277            eviction_policy: EvictionPolicy::Performance,
1278            similarity_threshold: scirs2_core::numeric::NumCast::from(0.3)
1279                .unwrap_or_else(|| T::zero()),
1280            enable_compression: false,
1281        })
1282    }
1283
1284    /// Get the capacity
1285    pub fn capacity(&self) -> usize {
1286        self.config.max_memory_size
1287    }
1288
1289    /// Get episode count
1290    pub fn len(&self) -> usize {
1291        self.episodes.len()
1292    }
1293
1294    /// Check if empty
1295    pub fn is_empty(&self) -> bool {
1296        self.episodes.is_empty()
1297    }
1298
1299    /// Get mutable access to episodes
1300    pub fn episodes_mut(&mut self) -> &mut VecDeque<MemoryEpisode<T>> {
1301        &mut self.episodes
1302    }
1303
1304    /// Get read access to episodes
1305    pub fn episodes(&self) -> &VecDeque<MemoryEpisode<T>> {
1306        &self.episodes
1307    }
1308
1309    /// Get mutable access to usage stats
1310    pub fn usage_stats_mut(&mut self) -> &mut MemoryUsageStats {
1311        &mut self.usage_stats
1312    }
1313
1314    /// Get usage stats
1315    pub fn usage_stats(&self) -> &MemoryUsageStats {
1316        &self.usage_stats
1317    }
1318
1319    /// Get eviction policy
1320    pub fn eviction_policy(&self) -> EvictionPolicy {
1321        self.config.eviction_policy
1322    }
1323
1324    /// Retrieve similar episodes to a task
1325    pub fn retrieve_similar(
1326        &self,
1327        task_data: &TaskData<T>,
1328        k: usize,
1329    ) -> Result<Vec<MemoryEpisode<T>>> {
1330        if self.episodes.is_empty() {
1331            return Ok(Vec::new());
1332        }
1333        // Simple retrieval: return up to k most recent episodes
1334        // (full similarity-based retrieval is in episodic_memory_impl)
1335        let count = k.min(self.episodes.len());
1336        let result: Vec<MemoryEpisode<T>> =
1337            self.episodes.iter().rev().take(count).cloned().collect();
1338        let _ = task_data; // suppress unused warning
1339        Ok(result)
1340    }
1341
1342    /// Store a new episode
1343    pub fn store_episode(
1344        &mut self,
1345        task_data: TaskData<T>,
1346        result: AdaptationResult<T>,
1347    ) -> Result<()> {
1348        let episode = MemoryEpisode {
1349            episode_id: format!("ep_{}", self.usage_stats.total_episodes),
1350            task_data,
1351            adaptation_result: result,
1352            timestamp: std::time::SystemTime::now(),
1353            metadata: EpisodeMetadata {
1354                difficulty: DifficultyLevel::Medium,
1355                domain: DomainType::Optimization,
1356                success_rate: 0.0,
1357                tags: Vec::new(),
1358            },
1359            access_count: 0,
1360        };
1361
1362        if self.episodes.len() >= self.config.max_memory_size {
1363            self.episodes.pop_front();
1364        }
1365        self.episodes.push_back(episode);
1366        self.usage_stats.total_episodes += 1;
1367        self.usage_stats.memory_utilization =
1368            self.episodes.len() as f64 / self.config.max_memory_size as f64;
1369        Ok(())
1370    }
1371}
1372
1373impl<T: Float + Debug + Send + Sync + 'static> FastAdaptationEngine<T> {
1374    /// Create a new fast adaptation engine
1375    pub fn new(config: FastAdaptationConfig) -> Result<Self> {
1376        Ok(Self {
1377            algorithms: Vec::new(),
1378            config,
1379        })
1380    }
1381
1382    /// Register a concrete adaptation algorithm, dispatched to by
1383    /// [`Self::adapt_fast`] when its [`FastAdaptationAlgorithm::name`] matches
1384    /// the requested [`AdaptationStrategyType`]'s `{:?}` label.
1385    pub fn register_algorithm(&mut self, algorithm: Box<dyn FastAdaptationAlgorithm<T>>) {
1386        self.algorithms.push(algorithm);
1387    }
1388
1389    /// Number of algorithms currently registered.
1390    pub fn algorithm_count(&self) -> usize {
1391        self.algorithms.len()
1392    }
1393
1394    /// Create from inner learning rate and adaptation steps (convenience)
1395    pub fn from_params(inner_lr: T, adaptation_steps: usize) -> Result<Self> {
1396        let _ = (inner_lr, adaptation_steps);
1397        Self::new(FastAdaptationConfig {
1398            enable_caching: true,
1399            enable_prediction: true,
1400            max_adaptation_time: Duration::from_secs(60),
1401            _performance_threshold: 0.8,
1402        })
1403    }
1404
1405    /// Get the configuration
1406    pub fn config(&self) -> &FastAdaptationConfig {
1407        &self.config
1408    }
1409
1410    /// Perform fast adaptation.
1411    ///
1412    /// Dispatches to a registered [`FastAdaptationAlgorithm`] whose
1413    /// [`FastAdaptationAlgorithm::name`] matches `strategy`'s `{:?}` label
1414    /// (registered via [`Self::register_algorithm`]); when none matches -- the
1415    /// common case, since the crate ships no built-in algorithms -- falls back
1416    /// to `optimizer`'s own [`FewShotOptimizer::adapt_few_shot`], which every
1417    /// `FewShotOptimizer` implementation must provide. Either way the
1418    /// returned [`AdaptationResult`] reflects what actually happened, not a
1419    /// fixed placeholder.
1420    pub fn adapt_fast(
1421        &mut self,
1422        optimizer: &mut dyn FewShotOptimizer<T>,
1423        task_data: &TaskData<T>,
1424        strategy: AdaptationStrategyType,
1425        config: &AdaptationConfig,
1426    ) -> Result<AdaptationResult<T>> {
1427        let start = Instant::now();
1428        let label = format!("{strategy:?}");
1429
1430        let mut result =
1431            if let Some(algorithm) = self.algorithms.iter_mut().find(|a| a.name() == label) {
1432                algorithm.adapt_fast(optimizer, task_data, None)?
1433            } else {
1434                optimizer.adapt_few_shot(&task_data.support_set, &task_data.query_set, config)?
1435            };
1436
1437        // A delegate that did not learn an explicit task representation still
1438        // gets a real one from the optimizer, instead of leaving it empty.
1439        if result.task_representation.is_empty() {
1440            result.task_representation = optimizer.get_task_representation(task_data)?;
1441        }
1442        // Wall-clock time for this call (including dispatch overhead)
1443        // supersedes whatever -- if anything -- the delegate measured itself.
1444        result.resource_usage.total_time = start.elapsed();
1445
1446        Ok(result)
1447    }
1448}
1449
1450impl<T: Float + Debug + Send + Sync + 'static> FewShotPerformanceTracker<T> {
1451    /// Create a new performance tracker
1452    pub fn new(config: TrackingConfig) -> Result<Self> {
1453        Ok(Self {
1454            performance_history: VecDeque::new(),
1455            config,
1456            stats: PerformanceStats {
1457                best_performance: T::zero(),
1458                average_performance: T::zero(),
1459                performance_variance: T::zero(),
1460                improvement_rate: T::zero(),
1461                success_rate: T::zero(),
1462            },
1463        })
1464    }
1465
1466    /// Record a performance result
1467    pub fn record_performance(&mut self, result: &AdaptationResult<T>) -> Result<()> {
1468        let record = PerformanceRecord {
1469            task_id: String::new(),
1470            performance: result.performance.query_performance,
1471            adaptation_time: result.resource_usage.total_time,
1472            strategy_used: String::new(),
1473            timestamp: std::time::SystemTime::now(),
1474            additional_metrics: HashMap::new(),
1475        };
1476
1477        if self.performance_history.len() >= self.config.max_history_size {
1478            self.performance_history.pop_front();
1479        }
1480        self.performance_history.push_back(record);
1481
1482        // Update stats
1483        if !self.performance_history.is_empty() {
1484            let mut sum = T::zero();
1485            let mut best = T::neg_infinity();
1486            for r in &self.performance_history {
1487                sum = sum + r.performance;
1488                if r.performance > best {
1489                    best = r.performance;
1490                }
1491            }
1492            let count_t = scirs2_core::numeric::NumCast::from(self.performance_history.len())
1493                .unwrap_or_else(|| T::one());
1494            self.stats.average_performance = sum / count_t;
1495            self.stats.best_performance = best;
1496        }
1497        Ok(())
1498    }
1499}
1500
1501// Task metadata and example metadata
1502#[derive(Debug, Clone)]
1503pub struct TaskMetadata {
1504    pub task_name: String,
1505    pub domain: DomainType,
1506    pub difficulty: DifficultyLevel,
1507    pub created_at: std::time::SystemTime,
1508}
1509
1510#[derive(Debug, Clone)]
1511pub struct ExampleMetadata {
1512    pub source: String,
1513    pub quality_score: f64,
1514    pub created_at: std::time::SystemTime,
1515}
1516
1517#[derive(Debug, Clone)]
1518pub struct SupportSetStatistics<T: Float + Debug + Send + Sync + 'static> {
1519    pub mean: Array1<T>,
1520    pub variance: Array1<T>,
1521    pub size: usize,
1522    pub diversity_score: T,
1523}
1524
1525#[derive(Debug, Clone)]
1526pub struct QuerySetStatistics<T: Float + Debug + Send + Sync + 'static> {
1527    pub mean: Array1<T>,
1528    pub variance: Array1<T>,
1529    pub size: usize,
1530}
1531
1532/// Evaluation metrics for few-shot learning
1533#[derive(Debug, Clone, Copy)]
1534pub enum EvaluationMetric {
1535    Accuracy,
1536    Loss,
1537    F1Score,
1538    Precision,
1539    Recall,
1540    AUC,
1541    MSE,
1542    MAE,
1543    FinalPerformance,
1544    Efficiency,
1545    TrainingTime,
1546}
1547
1548#[cfg(test)]
1549mod tests {
1550    use super::*;
1551
1552    #[test]
1553    fn test_support_set_creation() {
1554        let support_set = SupportSet::<f64> {
1555            examples: vec![SupportExample {
1556                features: Array1::from_vec(vec![1.0, 2.0, 3.0]),
1557                target: 0.5,
1558                weight: 1.0,
1559                context: HashMap::new(),
1560                metadata: ExampleMetadata {
1561                    source: "test".to_string(),
1562                    quality_score: 0.9,
1563                    created_at: std::time::SystemTime::now(),
1564                },
1565            }],
1566            task_metadata: TaskMetadata {
1567                task_name: "test_task".to_string(),
1568                domain: DomainType::Optimization,
1569                difficulty: DifficultyLevel::Easy,
1570                created_at: std::time::SystemTime::now(),
1571            },
1572            statistics: SupportSetStatistics {
1573                mean: Array1::from_vec(vec![1.0, 2.0, 3.0]),
1574                variance: Array1::from_vec(vec![0.1, 0.1, 0.1]),
1575                size: 1,
1576                diversity_score: 0.8,
1577            },
1578            temporal_order: None,
1579        };
1580
1581        assert_eq!(support_set.examples.len(), 1);
1582        assert_eq!(support_set.statistics.size, 1);
1583    }
1584
1585    #[test]
1586    fn test_adaptation_config() {
1587        let config = AdaptationConfig {
1588            adaptation_steps: 10,
1589            adaptation_lr: 0.01,
1590            strategy: AdaptationStrategyType::MAML,
1591            early_stopping: None,
1592            regularization: RegularizationConfig {
1593                l2_strength: 0.001,
1594                dropout_rate: 0.1,
1595                gradient_clip: Some(1.0),
1596                task_regularization: HashMap::new(),
1597            },
1598            resource_constraints: ResourceConstraints {
1599                max_time: Duration::from_secs(60),
1600                max_memory_mb: 1024,
1601                max_compute_budget: 100.0,
1602            },
1603        };
1604
1605        assert_eq!(config.adaptation_steps, 10);
1606        assert_eq!(config.adaptation_lr, 0.01);
1607        assert!(matches!(config.strategy, AdaptationStrategyType::MAML));
1608    }
1609
1610    #[test]
1611    fn test_domain_info() {
1612        let domain_info = DomainInfo {
1613            domain_type: DomainType::ComputerVision,
1614            characteristics: DomainCharacteristics {
1615                input_dim: 784,
1616                output_dim: 10,
1617                temporal: false,
1618                stochasticity: 0.1,
1619                noise_level: 0.05,
1620                sparsity: 0.0,
1621            },
1622            difficulty_level: DifficultyLevel::Medium,
1623            constraints: vec![DomainConstraint {
1624                constraint_type: ConstraintType::LatencyRequirement,
1625                description: "Max 100ms inference time".to_string(),
1626                enforcement: ConstraintEnforcement::Hard,
1627            }],
1628        };
1629
1630        assert!(matches!(
1631            domain_info.domain_type,
1632            DomainType::ComputerVision
1633        ));
1634        assert_eq!(domain_info.characteristics.input_dim, 784);
1635        assert_eq!(domain_info.constraints.len(), 1);
1636    }
1637
1638    /// A minimal [`FewShotOptimizer`] that records whether it was invoked and
1639    /// returns values distinguishable from `FastAdaptationEngine::adapt_fast`'s
1640    /// old fabricated constants (0.85 / 0.90 / 5 steps / 0.1 / 0.25 / 0.95), so
1641    /// a passing test can only mean the real delegation path ran.
1642    struct RecordingOptimizer {
1643        adapt_calls: usize,
1644    }
1645
1646    impl FewShotOptimizer<f64> for RecordingOptimizer {
1647        fn adapt_few_shot(
1648            &mut self,
1649            support_set: &SupportSet<f64>,
1650            _query_set: &QuerySet<f64>,
1651            _adaptation_config: &AdaptationConfig,
1652        ) -> Result<AdaptationResult<f64>> {
1653            self.adapt_calls += 1;
1654            let width = support_set
1655                .examples
1656                .first()
1657                .map(|e| e.features.len())
1658                .unwrap_or(1);
1659            Ok(AdaptationResult {
1660                adapted_state: OptimizerState {
1661                    parameters: Array1::zeros(width),
1662                    gradients: Array1::zeros(width),
1663                    momentum: None,
1664                    hidden_states: HashMap::new(),
1665                    memory_buffers: HashMap::new(),
1666                    step: 1,
1667                    step_count: 1,
1668                    loss: Some(0.05),
1669                    learning_rate: 0.01,
1670                    metadata: crate::StateMetadata::default(),
1671                },
1672                performance: AdaptationPerformance {
1673                    query_performance: 0.42,
1674                    support_performance: 0.37,
1675                    adaptation_speed: 2,
1676                    final_loss: 0.05,
1677                    improvement: 0.11,
1678                    stability: 0.5,
1679                },
1680                // Deliberately left empty: `adapt_fast` must fall back to
1681                // `get_task_representation` rather than leaving it empty.
1682                task_representation: Array1::zeros(0),
1683                adaptation_trajectory: Vec::new(),
1684                resource_usage: ResourceUsage {
1685                    // Deliberately a marker value: `adapt_fast` must overwrite
1686                    // this with real wall-clock time, not pass it through.
1687                    total_time: Duration::from_secs(999),
1688                    peak_memory_mb: 1.0,
1689                    compute_cost: 1.0,
1690                    energy_consumption: 1.0,
1691                },
1692            })
1693        }
1694
1695        fn get_task_representation(&self, _taskdata: &TaskData<f64>) -> Result<Array1<f64>> {
1696            Ok(Array1::from_vec(vec![7.0, 8.0, 9.0]))
1697        }
1698
1699        fn compute_adaptation_loss(
1700            &self,
1701            _support_set: &SupportSet<f64>,
1702            _query_set: &QuerySet<f64>,
1703        ) -> Result<f64> {
1704            Ok(0.1)
1705        }
1706
1707        fn update_meta_parameters(&mut self, _metagradients: &MetaGradients<f64>) -> Result<()> {
1708            Ok(())
1709        }
1710
1711        fn get_transfer_state(&self) -> TransferState<f64> {
1712            TransferState {
1713                representations: HashMap::new(),
1714                meta_parameters: HashMap::new(),
1715                task_embeddings: Array2::zeros((1, 1)),
1716                transfer_stats: TransferStatistics {
1717                    source_performance: 0.0,
1718                    target_performance: 0.0,
1719                    transfer_efficiency: 0.0,
1720                    steps_saved: 0,
1721                },
1722            }
1723        }
1724
1725        fn load_transfer_state(&mut self, _state: TransferState<f64>) -> Result<()> {
1726            Ok(())
1727        }
1728    }
1729
1730    fn make_task_data() -> TaskData<f64> {
1731        TaskData {
1732            task_id: "t".to_string(),
1733            support_set: SupportSet {
1734                examples: vec![SupportExample {
1735                    features: Array1::from_vec(vec![1.0, 2.0, 3.0]),
1736                    target: 0.5,
1737                    weight: 1.0,
1738                    context: HashMap::new(),
1739                    metadata: ExampleMetadata {
1740                        source: "test".to_string(),
1741                        quality_score: 0.9,
1742                        created_at: std::time::SystemTime::now(),
1743                    },
1744                }],
1745                task_metadata: TaskMetadata {
1746                    task_name: "test_task".to_string(),
1747                    domain: DomainType::Optimization,
1748                    difficulty: DifficultyLevel::Easy,
1749                    created_at: std::time::SystemTime::now(),
1750                },
1751                statistics: SupportSetStatistics {
1752                    mean: Array1::from_vec(vec![1.0, 2.0, 3.0]),
1753                    variance: Array1::from_vec(vec![0.1, 0.1, 0.1]),
1754                    size: 1,
1755                    diversity_score: 0.8,
1756                },
1757                temporal_order: None,
1758            },
1759            query_set: QuerySet {
1760                examples: vec![QueryExample {
1761                    features: Array1::from_vec(vec![1.0, 2.0, 3.0]),
1762                    true_target: Some(0.4),
1763                    weight: 1.0,
1764                    context: HashMap::new(),
1765                }],
1766                statistics: QuerySetStatistics {
1767                    mean: Array1::from_vec(vec![1.0, 2.0, 3.0]),
1768                    variance: Array1::from_vec(vec![0.1, 0.1, 0.1]),
1769                    size: 1,
1770                },
1771                eval_metrics: vec![EvaluationMetric::MSE],
1772            },
1773            task_params: HashMap::new(),
1774            domain_info: DomainInfo {
1775                domain_type: DomainType::Optimization,
1776                characteristics: DomainCharacteristics {
1777                    input_dim: 3,
1778                    output_dim: 1,
1779                    temporal: false,
1780                    stochasticity: 0.0,
1781                    noise_level: 0.0,
1782                    sparsity: 0.0,
1783                },
1784                difficulty_level: DifficultyLevel::Easy,
1785                constraints: vec![],
1786            },
1787        }
1788    }
1789
1790    fn make_adaptation_config() -> AdaptationConfig {
1791        AdaptationConfig {
1792            adaptation_steps: 5,
1793            adaptation_lr: 0.01,
1794            strategy: AdaptationStrategyType::MAML,
1795            early_stopping: None,
1796            regularization: RegularizationConfig {
1797                l2_strength: 0.0,
1798                dropout_rate: 0.0,
1799                gradient_clip: None,
1800                task_regularization: HashMap::new(),
1801            },
1802            resource_constraints: ResourceConstraints {
1803                max_time: Duration::from_secs(1),
1804                max_memory_mb: 64,
1805                max_compute_budget: 1.0,
1806            },
1807        }
1808    }
1809
1810    /// F15 regression: `adapt_fast` must delegate to the real optimizer
1811    /// (falling back to `FewShotOptimizer::adapt_few_shot` when, as by
1812    /// default, no algorithm is registered) instead of returning fabricated,
1813    /// input-independent constants.
1814    #[test]
1815    fn test_adapt_fast_delegates_instead_of_fabricating() {
1816        let mut engine = FastAdaptationEngine::<f64>::new(FastAdaptationConfig {
1817            enable_caching: true,
1818            enable_prediction: true,
1819            max_adaptation_time: Duration::from_secs(1),
1820            _performance_threshold: 0.8,
1821        })
1822        .expect("engine");
1823        assert_eq!(
1824            engine.algorithm_count(),
1825            0,
1826            "no algorithm registered by default"
1827        );
1828
1829        let mut optimizer = RecordingOptimizer { adapt_calls: 0 };
1830        let task_data = make_task_data();
1831        let config = make_adaptation_config();
1832
1833        let result = engine
1834            .adapt_fast(
1835                &mut optimizer,
1836                &task_data,
1837                AdaptationStrategyType::MAML,
1838                &config,
1839            )
1840            .expect("adapt_fast should succeed");
1841
1842        assert_eq!(
1843            optimizer.adapt_calls, 1,
1844            "adapt_fast must actually call the optimizer, not ignore it"
1845        );
1846        // Values from `RecordingOptimizer`, not the old fabricated constants
1847        // (query_performance 0.85, support_performance 0.90, adaptation_speed
1848        // 5, final_loss 0.1, improvement 0.25, stability 0.95).
1849        approx::assert_abs_diff_eq!(result.performance.query_performance, 0.42, epsilon = 1e-12);
1850        approx::assert_abs_diff_eq!(
1851            result.performance.support_performance,
1852            0.37,
1853            epsilon = 1e-12
1854        );
1855        assert_eq!(result.performance.adaptation_speed, 2);
1856        // The delegate's parameters (width 3, matching the support example),
1857        // not the old hard-coded `Array1::zeros(1)`.
1858        assert_eq!(result.adapted_state.parameters.len(), 3);
1859        // An empty delegate task representation is filled in from the
1860        // optimizer, not left as the old hard-coded `Array1::zeros(128)`.
1861        assert_eq!(
1862            result.task_representation.as_slice(),
1863            Some(&[7.0, 8.0, 9.0][..])
1864        );
1865        // Real wall-clock time replaces the delegate's marker value.
1866        assert!(result.resource_usage.total_time < Duration::from_secs(999));
1867    }
1868
1869    /// F15 regression: a registered algorithm matching the requested strategy
1870    /// takes priority over the optimizer fallback.
1871    #[test]
1872    fn test_adapt_fast_prefers_a_registered_algorithm() {
1873        struct AlwaysProto {
1874            calls: usize,
1875        }
1876        impl FastAdaptationAlgorithm<f64> for AlwaysProto {
1877            fn adapt_fast(
1878                &mut self,
1879                _optimizer: &mut dyn FewShotOptimizer<f64>,
1880                _task_data: &TaskData<f64>,
1881                _target_performance: Option<f64>,
1882            ) -> Result<AdaptationResult<f64>> {
1883                self.calls += 1;
1884                Ok(AdaptationResult {
1885                    adapted_state: OptimizerState {
1886                        parameters: Array1::zeros(2),
1887                        gradients: Array1::zeros(2),
1888                        momentum: None,
1889                        hidden_states: HashMap::new(),
1890                        memory_buffers: HashMap::new(),
1891                        step: 0,
1892                        step_count: 0,
1893                        loss: None,
1894                        learning_rate: 0.02,
1895                        metadata: crate::StateMetadata::default(),
1896                    },
1897                    performance: AdaptationPerformance {
1898                        query_performance: 0.77,
1899                        support_performance: 0.66,
1900                        adaptation_speed: 1,
1901                        final_loss: 0.2,
1902                        improvement: 0.3,
1903                        stability: 0.4,
1904                    },
1905                    task_representation: Array1::from_vec(vec![1.0]),
1906                    adaptation_trajectory: Vec::new(),
1907                    resource_usage: ResourceUsage {
1908                        total_time: Duration::from_secs(0),
1909                        peak_memory_mb: 0.0,
1910                        compute_cost: 0.0,
1911                        energy_consumption: 0.0,
1912                    },
1913                })
1914            }
1915
1916            fn estimate_adaptation_time(&self, _taskdata: &TaskData<f64>) -> Duration {
1917                Duration::from_millis(1)
1918            }
1919
1920            fn name(&self) -> &str {
1921                "Prototypical"
1922            }
1923        }
1924
1925        let mut engine = FastAdaptationEngine::<f64>::new(FastAdaptationConfig {
1926            enable_caching: false,
1927            enable_prediction: false,
1928            max_adaptation_time: Duration::from_secs(1),
1929            _performance_threshold: 0.5,
1930        })
1931        .expect("engine");
1932        engine.register_algorithm(Box::new(AlwaysProto { calls: 0 }));
1933        assert_eq!(engine.algorithm_count(), 1);
1934
1935        let mut optimizer = RecordingOptimizer { adapt_calls: 0 };
1936        let task_data = make_task_data();
1937        let config = make_adaptation_config();
1938
1939        let result = engine
1940            .adapt_fast(
1941                &mut optimizer,
1942                &task_data,
1943                AdaptationStrategyType::Prototypical,
1944                &config,
1945            )
1946            .expect("adapt_fast should succeed");
1947
1948        assert_eq!(
1949            optimizer.adapt_calls, 0,
1950            "the registered algorithm should have been used instead of the optimizer fallback"
1951        );
1952        approx::assert_abs_diff_eq!(result.performance.query_performance, 0.77, epsilon = 1e-12);
1953    }
1954}