1use 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
11pub struct TransferLearner {
13 pub source_domains: Vec<SourceDomain>,
15 pub strategies: Vec<TransferStrategy>,
17 pub similarity_analyzer: DomainSimilarityAnalyzer,
19 pub transfer_history: VecDeque<TransferRecord>,
21 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 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 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 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 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 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 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 similarities.push((source_domain.clone(), similarity));
127 }
128 }
129
130 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 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 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 let mut feature_mappings = HashMap::new();
209
210 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 let mut transferred_params = HashMap::new();
238
239 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 for (param_name, total) in param_sums {
253 if let Some(&count) = param_counts.get(¶m_name) {
254 let avg_value = total / count as f64;
255 let adapted_value =
256 self.adapt_parameter(¶m_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 let mut relations = HashMap::new();
280
281 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 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 match param_name {
321 "initial_temperature" => {
322 let size_factor = (target_features.size as f64 / 100.0).sqrt();
324 value * size_factor * similarity
325 }
326 "cooling_rate" => {
327 let density_factor = target_features.density.mul_add(0.2, 1.0);
329 value * density_factor
330 }
331 "num_sweeps" | "max_iterations" => {
332 let size_factor = (target_features.size as f64 / 100.0).ln().max(1.0);
334 value * size_factor
335 }
336 _ => value * similarity, }
338 }
339
340 fn calculate_experience_similarity(
341 &self,
342 exp_features: &ProblemFeatures,
343 target_features: &ProblemFeatures,
344 ) -> f64 {
345 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 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 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 }
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#[derive(Debug, Clone)]
411pub struct SourceDomain {
412 pub id: String,
414 pub characteristics: DomainCharacteristics,
416 pub experiences: Vec<OptimizationExperience>,
418 pub models: Vec<TransferableModel>,
420 pub last_updated: Instant,
422}
423
424#[derive(Debug, Clone)]
426pub struct DomainCharacteristics {
427 pub domain: ProblemDomain,
429 pub avg_problem_size: f64,
431 pub avg_density: f64,
433 pub typical_algorithms: Vec<AlgorithmType>,
435 pub performance_distribution: DistributionStats,
437 pub feature_importance: HashMap<String, f64>,
439}
440
441#[derive(Debug, Clone)]
443pub struct TransferableModel {
444 pub model_type: ModelType,
446 pub source_domain_id: String,
448 pub parameters: HashMap<String, f64>,
450 pub knowledge: Knowledge,
452 pub confidence: f64,
454 pub adaptation_required: bool,
456}
457
458#[derive(Debug, Clone, PartialEq, Eq)]
460pub enum ModelType {
461 InstanceBased,
463 FeatureBased,
465 ParameterBased,
467 RelationBased,
469 CompleteBased,
471}
472
473#[derive(Debug, Clone)]
475pub enum Knowledge {
476 Instances(Vec<OptimizationExperience>),
478 FeatureMapping(HashMap<String, Vec<f64>>),
480 Parameters(HashMap<String, DistributionStats>),
482 Relations(HashMap<String, f64>),
484 CompleteModel(Vec<u8>),
486}
487
488#[derive(Debug, Clone)]
490pub struct TransferRecord {
491 pub timestamp: Instant,
493 pub source_domains: Vec<DomainCharacteristics>,
495 pub target_features: ProblemFeatures,
497 pub strategies_used: Vec<TransferStrategy>,
499 pub success_rate: f64,
501 pub models_transferred: usize,
503}
504
505#[derive(Debug)]
507pub struct DomainSimilarityAnalyzer {
508 pub metrics: Vec<SimilarityMetric>,
510 pub methods: Vec<SimilarityMethod>,
512 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 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 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 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 } else {
565 0.3
566 };
567 similarity_scores.push(domain_similarity);
568
569 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); similarity_scores.push(size_similarity);
573
574 let density_similarity =
576 1.0 - (source_characteristics.avg_density - target_features.density).abs();
577 similarity_scores.push(density_similarity);
578
579 let weights = vec![0.4, 0.3, 0.3]; let overall_similarity: f64 = similarity_scores
582 .iter()
583 .zip(weights.iter())
584 .map(|(score, weight)| score * weight)
585 .sum();
586
587 self.similarity_cache.insert(cache_key, overall_similarity);
589
590 if self.similarity_cache.len() > 10_000 {
592 self.similarity_cache.clear();
593 }
594
595 Ok(overall_similarity)
596 }
597}
598
599#[derive(Debug, Clone, PartialEq, Eq)]
601pub enum SimilarityMetric {
602 FeatureSimilarity,
604 StatisticalSimilarity,
606 PerformanceSimilarity,
608 DomainSimilarity,
610 TaskSimilarity,
612}
613
614#[derive(Debug, Clone, PartialEq, Eq)]
616pub enum SimilarityMethod {
617 EuclideanDistance,
619 CosineSimilarity,
621 PearsonCorrelation,
623 KLDivergence,
625 MaximumMeanDiscrepancy,
627}
628
629#[derive(Debug, Clone, PartialEq, Eq)]
631pub enum TransferStrategy {
632 InstanceTransfer,
634 FeatureTransfer,
636 ParameterTransfer,
638 RelationTransfer,
640 ModelTransfer,
642}
643
644#[derive(Debug, Clone, PartialEq, Eq)]
646pub enum AdaptationMechanism {
647 FineTuning,
649 DomainAdaptation,
651 MultiTaskLearning,
653 ProgressiveTransfer,
655 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}