quantrs2_anneal/meta_learning_optimization/
transfer_learning.rs

1//! Transfer learning system for meta-learning optimization
2
3use std::collections::{HashMap, VecDeque};
4use std::time::{Duration, Instant};
5
6use super::config::*;
7use super::feature_extraction::{
8    AlgorithmType, DistributionStats, OptimizationExperience, ProblemDomain, ProblemFeatures,
9};
10
11/// Transfer learning system
12pub struct TransferLearner {
13    /// Source domains
14    pub source_domains: Vec<SourceDomain>,
15    /// Transfer strategies
16    pub strategies: Vec<TransferStrategy>,
17    /// Domain similarity analyzer
18    pub similarity_analyzer: DomainSimilarityAnalyzer,
19    /// Transfer history
20    pub transfer_history: VecDeque<TransferRecord>,
21    /// Adaptation mechanisms
22    pub adaptation_mechanisms: Vec<AdaptationMechanism>,
23}
24
25impl TransferLearner {
26    #[must_use]
27    pub fn new() -> Self {
28        Self {
29            source_domains: Vec::new(),
30            strategies: vec![
31                TransferStrategy::InstanceTransfer,
32                TransferStrategy::FeatureTransfer,
33                TransferStrategy::ParameterTransfer,
34            ],
35            similarity_analyzer: DomainSimilarityAnalyzer::new(),
36            transfer_history: VecDeque::new(),
37            adaptation_mechanisms: vec![
38                AdaptationMechanism::FineTuning,
39                AdaptationMechanism::DomainAdaptation,
40            ],
41        }
42    }
43
44    pub fn add_source_domain(&mut self, domain: SourceDomain) {
45        self.source_domains.push(domain);
46
47        // Limit number of source domains
48        while self.source_domains.len() > 50 {
49            self.source_domains.remove(0);
50        }
51    }
52
53    pub fn transfer_knowledge(
54        &mut self,
55        target_features: &ProblemFeatures,
56        target_domain: &ProblemDomain,
57    ) -> Result<Vec<TransferableModel>, String> {
58        // Find most similar source domains
59        let similar_domains = self.find_similar_domains(target_features, target_domain)?;
60
61        if similar_domains.is_empty() {
62            return Ok(Vec::new());
63        }
64
65        let mut transferred_models = Vec::new();
66
67        for (domain, similarity) in similar_domains.iter().take(3) {
68            // Top 3 similar domains
69            for strategy in &self.strategies.clone() {
70                if let Ok(model) =
71                    self.apply_transfer_strategy(domain, target_features, strategy, *similarity)
72                {
73                    transferred_models.push(model);
74                }
75            }
76        }
77
78        // Record transfer attempt
79        let transfer_record = TransferRecord {
80            timestamp: Instant::now(),
81            source_domains: similar_domains
82                .iter()
83                .map(|(d, _)| d.characteristics.clone())
84                .collect(),
85            target_features: target_features.clone(),
86            strategies_used: self.strategies.clone(),
87            success_rate: if transferred_models.is_empty() {
88                0.0
89            } else {
90                1.0
91            },
92            models_transferred: transferred_models.len(),
93        };
94
95        self.transfer_history.push_back(transfer_record);
96
97        // Limit transfer history
98        while self.transfer_history.len() > 1000 {
99            self.transfer_history.pop_front();
100        }
101
102        Ok(transferred_models)
103    }
104
105    fn find_similar_domains(
106        &mut self,
107        target_features: &ProblemFeatures,
108        target_domain: &ProblemDomain,
109    ) -> Result<Vec<(SourceDomain, f64)>, String> {
110        let mut similarities = Vec::new();
111
112        for source_domain in &self.source_domains {
113            // Check domain compatibility
114            if !self.is_domain_compatible(&source_domain.characteristics.domain, target_domain) {
115                continue;
116            }
117
118            let similarity = self.similarity_analyzer.calculate_similarity(
119                &source_domain.characteristics,
120                target_features,
121                target_domain,
122            )?;
123
124            if similarity > 0.3 {
125                // Minimum similarity threshold
126                similarities.push((source_domain.clone(), similarity));
127            }
128        }
129
130        // Sort by similarity (descending)
131        similarities.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
132
133        Ok(similarities)
134    }
135
136    fn is_domain_compatible(
137        &self,
138        source_domain: &ProblemDomain,
139        target_domain: &ProblemDomain,
140    ) -> bool {
141        // Allow transfer within same domain or to/from general domains
142        source_domain == target_domain
143            || matches!(source_domain, ProblemDomain::Combinatorial)
144            || matches!(target_domain, ProblemDomain::Combinatorial)
145    }
146
147    fn apply_transfer_strategy(
148        &self,
149        source_domain: &SourceDomain,
150        target_features: &ProblemFeatures,
151        strategy: &TransferStrategy,
152        similarity: f64,
153    ) -> Result<TransferableModel, String> {
154        match strategy {
155            TransferStrategy::InstanceTransfer => {
156                self.instance_transfer(source_domain, target_features, similarity)
157            }
158            TransferStrategy::FeatureTransfer => {
159                self.feature_transfer(source_domain, target_features, similarity)
160            }
161            TransferStrategy::ParameterTransfer => {
162                self.parameter_transfer(source_domain, target_features, similarity)
163            }
164            TransferStrategy::RelationTransfer => {
165                self.relation_transfer(source_domain, target_features, similarity)
166            }
167            TransferStrategy::ModelTransfer => {
168                self.model_transfer(source_domain, target_features, similarity)
169            }
170        }
171    }
172
173    fn instance_transfer(
174        &self,
175        source_domain: &SourceDomain,
176        target_features: &ProblemFeatures,
177        similarity: f64,
178    ) -> Result<TransferableModel, String> {
179        // Transfer relevant instances based on similarity
180        let relevant_experiences: Vec<_> = source_domain
181            .experiences
182            .iter()
183            .filter(|exp| {
184                let exp_similarity =
185                    self.calculate_experience_similarity(&exp.problem_features, target_features);
186                exp_similarity > 0.5
187            })
188            .cloned()
189            .collect();
190
191        Ok(TransferableModel {
192            model_type: ModelType::InstanceBased,
193            source_domain_id: source_domain.id.clone(),
194            parameters: HashMap::new(),
195            knowledge: Knowledge::Instances(relevant_experiences),
196            confidence: similarity * 0.8,
197            adaptation_required: true,
198        })
199    }
200
201    fn feature_transfer(
202        &self,
203        source_domain: &SourceDomain,
204        target_features: &ProblemFeatures,
205        similarity: f64,
206    ) -> Result<TransferableModel, String> {
207        // Transfer feature representations and transformations
208        let mut feature_mappings = HashMap::new();
209
210        // Simple feature mapping based on similarity
211        feature_mappings.insert(
212            "size_scaling".to_string(),
213            target_features.size as f64 / source_domain.characteristics.avg_problem_size,
214        );
215        feature_mappings.insert(
216            "density_scaling".to_string(),
217            target_features.density / source_domain.characteristics.avg_density,
218        );
219
220        Ok(TransferableModel {
221            model_type: ModelType::FeatureBased,
222            source_domain_id: source_domain.id.clone(),
223            parameters: feature_mappings,
224            knowledge: Knowledge::FeatureMapping(HashMap::new()),
225            confidence: similarity * 0.9,
226            adaptation_required: false,
227        })
228    }
229
230    fn parameter_transfer(
231        &self,
232        source_domain: &SourceDomain,
233        target_features: &ProblemFeatures,
234        similarity: f64,
235    ) -> Result<TransferableModel, String> {
236        // Transfer learned parameters from source domain
237        let mut transferred_params = HashMap::new();
238
239        // Get average hyperparameters from source domain experiences
240        if !source_domain.experiences.is_empty() {
241            let mut param_sums: HashMap<String, f64> = HashMap::new();
242            let mut param_counts: HashMap<String, usize> = HashMap::new();
243
244            for experience in &source_domain.experiences {
245                for (param_name, param_value) in &experience.configuration.hyperparameters {
246                    *param_sums.entry(param_name.clone()).or_insert(0.0) += param_value;
247                    *param_counts.entry(param_name.clone()).or_insert(0) += 1;
248                }
249            }
250
251            // Calculate averages and adapt to target domain
252            for (param_name, total) in param_sums {
253                if let Some(&count) = param_counts.get(&param_name) {
254                    let avg_value = total / count as f64;
255                    let adapted_value =
256                        self.adapt_parameter(&param_name, avg_value, target_features, similarity);
257                    transferred_params.insert(param_name, adapted_value);
258                }
259            }
260        }
261
262        Ok(TransferableModel {
263            model_type: ModelType::ParameterBased,
264            source_domain_id: source_domain.id.clone(),
265            parameters: transferred_params,
266            knowledge: Knowledge::Parameters(HashMap::new()),
267            confidence: similarity * 0.7,
268            adaptation_required: true,
269        })
270    }
271
272    fn relation_transfer(
273        &self,
274        source_domain: &SourceDomain,
275        target_features: &ProblemFeatures,
276        similarity: f64,
277    ) -> Result<TransferableModel, String> {
278        // Transfer relational knowledge between features and performance
279        let mut relations = HashMap::new();
280
281        // Simplified relation extraction
282        relations.insert("size_performance_relation".to_string(), 0.8);
283        relations.insert("density_performance_relation".to_string(), 0.6);
284
285        Ok(TransferableModel {
286            model_type: ModelType::RelationBased,
287            source_domain_id: source_domain.id.clone(),
288            parameters: relations,
289            knowledge: Knowledge::Relations(HashMap::new()),
290            confidence: similarity * 0.6,
291            adaptation_required: true,
292        })
293    }
294
295    fn model_transfer(
296        &self,
297        source_domain: &SourceDomain,
298        target_features: &ProblemFeatures,
299        similarity: f64,
300    ) -> Result<TransferableModel, String> {
301        // Transfer entire learned models
302        Ok(TransferableModel {
303            model_type: ModelType::CompleteBased,
304            source_domain_id: source_domain.id.clone(),
305            parameters: HashMap::new(),
306            knowledge: Knowledge::CompleteModel(Vec::new()),
307            confidence: similarity * 0.5,
308            adaptation_required: true,
309        })
310    }
311
312    fn adapt_parameter(
313        &self,
314        param_name: &str,
315        value: f64,
316        target_features: &ProblemFeatures,
317        similarity: f64,
318    ) -> f64 {
319        // Simple parameter adaptation based on problem characteristics
320        match param_name {
321            "initial_temperature" => {
322                // Scale initial temperature based on problem size
323                let size_factor = (target_features.size as f64 / 100.0).sqrt();
324                value * size_factor * similarity
325            }
326            "cooling_rate" => {
327                // Adapt cooling rate based on problem density
328                let density_factor = target_features.density.mul_add(0.2, 1.0);
329                value * density_factor
330            }
331            "num_sweeps" | "max_iterations" => {
332                // Scale iterations based on problem size
333                let size_factor = (target_features.size as f64 / 100.0).ln().max(1.0);
334                value * size_factor
335            }
336            _ => value * similarity, // Default adaptation
337        }
338    }
339
340    fn calculate_experience_similarity(
341        &self,
342        exp_features: &ProblemFeatures,
343        target_features: &ProblemFeatures,
344    ) -> f64 {
345        // Simple similarity calculation
346        let size_similarity = 1.0
347            - (exp_features.size as f64 - target_features.size as f64).abs()
348                / exp_features.size.max(target_features.size) as f64;
349        let density_similarity = 1.0 - (exp_features.density - target_features.density).abs();
350
351        f64::midpoint(size_similarity, density_similarity)
352    }
353
354    pub fn adapt_model(
355        &self,
356        model: &mut TransferableModel,
357        target_features: &ProblemFeatures,
358    ) -> Result<(), String> {
359        if !model.adaptation_required {
360            return Ok(());
361        }
362
363        match model.model_type {
364            ModelType::ParameterBased => {
365                // Fine-tune parameters for target domain
366                for (param_name, param_value) in &mut model.parameters {
367                    *param_value = self.adapt_parameter(
368                        param_name,
369                        *param_value,
370                        target_features,
371                        model.confidence,
372                    );
373                }
374            }
375            ModelType::InstanceBased => {
376                // Filter instances based on target domain relevance
377                if let Knowledge::Instances(ref mut instances) = model.knowledge {
378                    instances.retain(|exp| {
379                        self.calculate_experience_similarity(&exp.problem_features, target_features)
380                            > 0.4
381                    });
382                }
383            }
384            _ => {
385                // Other adaptation mechanisms would be implemented here
386            }
387        }
388
389        model.adaptation_required = false;
390        Ok(())
391    }
392
393    #[must_use]
394    pub fn evaluate_transfer_success(&self) -> f64 {
395        if self.transfer_history.is_empty() {
396            return 0.0;
397        }
398
399        let total_success: f64 = self
400            .transfer_history
401            .iter()
402            .map(|record| record.success_rate)
403            .sum();
404
405        total_success / self.transfer_history.len() as f64
406    }
407}
408
409/// Source domain for transfer learning
410#[derive(Debug, Clone)]
411pub struct SourceDomain {
412    /// Domain identifier
413    pub id: String,
414    /// Domain characteristics
415    pub characteristics: DomainCharacteristics,
416    /// Experiences from this domain
417    pub experiences: Vec<OptimizationExperience>,
418    /// Performance models
419    pub models: Vec<TransferableModel>,
420    /// Last updated
421    pub last_updated: Instant,
422}
423
424/// Domain characteristics
425#[derive(Debug, Clone)]
426pub struct DomainCharacteristics {
427    /// Problem domain type
428    pub domain: ProblemDomain,
429    /// Average problem size
430    pub avg_problem_size: f64,
431    /// Average problem density
432    pub avg_density: f64,
433    /// Typical algorithms used
434    pub typical_algorithms: Vec<AlgorithmType>,
435    /// Performance distribution
436    pub performance_distribution: DistributionStats,
437    /// Feature importance
438    pub feature_importance: HashMap<String, f64>,
439}
440
441/// Transferable model
442#[derive(Debug, Clone)]
443pub struct TransferableModel {
444    /// Model type
445    pub model_type: ModelType,
446    /// Source domain identifier
447    pub source_domain_id: String,
448    /// Model parameters
449    pub parameters: HashMap<String, f64>,
450    /// Encoded knowledge
451    pub knowledge: Knowledge,
452    /// Transfer confidence
453    pub confidence: f64,
454    /// Whether adaptation is required
455    pub adaptation_required: bool,
456}
457
458/// Types of transferable models
459#[derive(Debug, Clone, PartialEq, Eq)]
460pub enum ModelType {
461    /// Instance-based transfer
462    InstanceBased,
463    /// Feature-based transfer
464    FeatureBased,
465    /// Parameter-based transfer
466    ParameterBased,
467    /// Relation-based transfer
468    RelationBased,
469    /// Complete model transfer
470    CompleteBased,
471}
472
473/// Knowledge representation
474#[derive(Debug, Clone)]
475pub enum Knowledge {
476    /// Instance knowledge
477    Instances(Vec<OptimizationExperience>),
478    /// Feature mapping knowledge
479    FeatureMapping(HashMap<String, Vec<f64>>),
480    /// Parameter knowledge
481    Parameters(HashMap<String, DistributionStats>),
482    /// Relational knowledge
483    Relations(HashMap<String, f64>),
484    /// Complete model knowledge
485    CompleteModel(Vec<u8>),
486}
487
488/// Transfer record
489#[derive(Debug, Clone)]
490pub struct TransferRecord {
491    /// Transfer timestamp
492    pub timestamp: Instant,
493    /// Source domain characteristics
494    pub source_domains: Vec<DomainCharacteristics>,
495    /// Target features
496    pub target_features: ProblemFeatures,
497    /// Strategies used
498    pub strategies_used: Vec<TransferStrategy>,
499    /// Transfer success rate
500    pub success_rate: f64,
501    /// Number of models transferred
502    pub models_transferred: usize,
503}
504
505/// Domain similarity analyzer
506#[derive(Debug)]
507pub struct DomainSimilarityAnalyzer {
508    /// Similarity metrics
509    pub metrics: Vec<SimilarityMetric>,
510    /// Similarity methods
511    pub methods: Vec<SimilarityMethod>,
512    /// Cached similarities
513    pub similarity_cache: HashMap<String, f64>,
514}
515
516impl DomainSimilarityAnalyzer {
517    #[must_use]
518    pub fn new() -> Self {
519        Self {
520            metrics: vec![
521                SimilarityMetric::FeatureSimilarity,
522                SimilarityMetric::StatisticalSimilarity,
523                SimilarityMetric::PerformanceSimilarity,
524            ],
525            methods: vec![
526                SimilarityMethod::EuclideanDistance,
527                SimilarityMethod::CosineSimilarity,
528                SimilarityMethod::KLDivergence,
529            ],
530            similarity_cache: HashMap::new(),
531        }
532    }
533
534    pub fn calculate_similarity(
535        &mut self,
536        source_characteristics: &DomainCharacteristics,
537        target_features: &ProblemFeatures,
538        target_domain: &ProblemDomain,
539    ) -> Result<f64, String> {
540        // Create cache key
541        let cache_key = format!(
542            "{:?}_{}_{}_{}_{:?}",
543            source_characteristics.domain,
544            source_characteristics.avg_problem_size as u32,
545            target_features.size,
546            target_features.density,
547            target_domain
548        );
549
550        // Check cache first
551        if let Some(&cached_similarity) = self.similarity_cache.get(&cache_key) {
552            return Ok(cached_similarity);
553        }
554
555        let mut similarity_scores = Vec::new();
556
557        // Domain type similarity
558        let domain_similarity = if source_characteristics.domain == *target_domain {
559            1.0
560        } else if matches!(source_characteristics.domain, ProblemDomain::Combinatorial)
561            || matches!(target_domain, ProblemDomain::Combinatorial)
562        {
563            0.7 // Combinatorial problems are somewhat similar to others
564        } else {
565            0.3
566        };
567        similarity_scores.push(domain_similarity);
568
569        // Size similarity
570        let size_ratio = source_characteristics.avg_problem_size / target_features.size as f64;
571        let size_similarity = 1.0 - (size_ratio.ln().abs() / 3.0).min(1.0); // Logarithmic similarity
572        similarity_scores.push(size_similarity);
573
574        // Density similarity
575        let density_similarity =
576            1.0 - (source_characteristics.avg_density - target_features.density).abs();
577        similarity_scores.push(density_similarity);
578
579        // Calculate overall similarity as weighted average
580        let weights = vec![0.4, 0.3, 0.3]; // Domain type is most important
581        let overall_similarity: f64 = similarity_scores
582            .iter()
583            .zip(weights.iter())
584            .map(|(score, weight)| score * weight)
585            .sum();
586
587        // Cache the result
588        self.similarity_cache.insert(cache_key, overall_similarity);
589
590        // Limit cache size
591        if self.similarity_cache.len() > 10_000 {
592            self.similarity_cache.clear();
593        }
594
595        Ok(overall_similarity)
596    }
597}
598
599/// Similarity metrics
600#[derive(Debug, Clone, PartialEq, Eq)]
601pub enum SimilarityMetric {
602    /// Feature-based similarity
603    FeatureSimilarity,
604    /// Statistical similarity
605    StatisticalSimilarity,
606    /// Performance similarity
607    PerformanceSimilarity,
608    /// Domain similarity
609    DomainSimilarity,
610    /// Task similarity
611    TaskSimilarity,
612}
613
614/// Similarity measurement methods
615#[derive(Debug, Clone, PartialEq, Eq)]
616pub enum SimilarityMethod {
617    /// Euclidean distance
618    EuclideanDistance,
619    /// Cosine similarity
620    CosineSimilarity,
621    /// Pearson correlation
622    PearsonCorrelation,
623    /// Kullback-Leibler divergence
624    KLDivergence,
625    /// Maximum Mean Discrepancy
626    MaximumMeanDiscrepancy,
627}
628
629/// Transfer strategies
630#[derive(Debug, Clone, PartialEq, Eq)]
631pub enum TransferStrategy {
632    /// Instance transfer
633    InstanceTransfer,
634    /// Feature transfer
635    FeatureTransfer,
636    /// Parameter transfer
637    ParameterTransfer,
638    /// Relation transfer
639    RelationTransfer,
640    /// Model transfer
641    ModelTransfer,
642}
643
644/// Adaptation mechanisms
645#[derive(Debug, Clone, PartialEq, Eq)]
646pub enum AdaptationMechanism {
647    /// Fine-tuning
648    FineTuning,
649    /// Domain adaptation
650    DomainAdaptation,
651    /// Multi-task learning
652    MultiTaskLearning,
653    /// Progressive transfer
654    ProgressiveTransfer,
655    /// Adversarial adaptation
656    AdversarialAdaptation,
657}
658
659#[cfg(test)]
660mod tests {
661    use super::*;
662    use crate::meta_learning_optimization::feature_extraction::{
663        GraphFeatures, SpectralFeatures, StatisticalFeatures,
664    };
665
666    #[test]
667    fn test_transfer_learner_creation() {
668        let transfer_learner = TransferLearner::new();
669        assert_eq!(transfer_learner.source_domains.len(), 0);
670        assert_eq!(transfer_learner.strategies.len(), 3);
671    }
672
673    #[test]
674    fn test_domain_similarity_analyzer() {
675        let mut analyzer = DomainSimilarityAnalyzer::new();
676
677        let source_characteristics = DomainCharacteristics {
678            domain: ProblemDomain::Combinatorial,
679            avg_problem_size: 100.0,
680            avg_density: 0.5,
681            typical_algorithms: vec![AlgorithmType::SimulatedAnnealing],
682            performance_distribution: DistributionStats::default(),
683            feature_importance: HashMap::new(),
684        };
685
686        let target_features = ProblemFeatures {
687            size: 120,
688            density: 0.6,
689            graph_features: GraphFeatures::default(),
690            statistical_features: StatisticalFeatures::default(),
691            spectral_features: SpectralFeatures::default(),
692            domain_features: HashMap::new(),
693        };
694
695        let target_domain = ProblemDomain::Combinatorial;
696
697        let similarity = analyzer.calculate_similarity(
698            &source_characteristics,
699            &target_features,
700            &target_domain,
701        );
702        assert!(similarity.is_ok());
703
704        let sim_value = similarity.expect("calculate_similarity should succeed");
705        assert!(sim_value >= 0.0 && sim_value <= 1.0);
706    }
707
708    #[test]
709    fn test_transferable_model() {
710        let model = TransferableModel {
711            model_type: ModelType::ParameterBased,
712            source_domain_id: "test_domain".to_string(),
713            parameters: HashMap::new(),
714            knowledge: Knowledge::Parameters(HashMap::new()),
715            confidence: 0.8,
716            adaptation_required: true,
717        };
718
719        assert_eq!(model.model_type, ModelType::ParameterBased);
720        assert!(model.adaptation_required);
721        assert_eq!(model.confidence, 0.8);
722    }
723}