use std::collections::{HashMap, VecDeque};
use std::time::{Duration, Instant};
use super::config::*;
use super::feature_extraction::{
AlgorithmType, DistributionStats, OptimizationExperience, ProblemDomain, ProblemFeatures,
};
pub struct TransferLearner {
pub source_domains: Vec<SourceDomain>,
pub strategies: Vec<TransferStrategy>,
pub similarity_analyzer: DomainSimilarityAnalyzer,
pub transfer_history: VecDeque<TransferRecord>,
pub adaptation_mechanisms: Vec<AdaptationMechanism>,
}
impl TransferLearner {
#[must_use]
pub fn new() -> Self {
Self {
source_domains: Vec::new(),
strategies: vec![
TransferStrategy::InstanceTransfer,
TransferStrategy::FeatureTransfer,
TransferStrategy::ParameterTransfer,
],
similarity_analyzer: DomainSimilarityAnalyzer::new(),
transfer_history: VecDeque::new(),
adaptation_mechanisms: vec![
AdaptationMechanism::FineTuning,
AdaptationMechanism::DomainAdaptation,
],
}
}
pub fn add_source_domain(&mut self, domain: SourceDomain) {
self.source_domains.push(domain);
while self.source_domains.len() > 50 {
self.source_domains.remove(0);
}
}
pub fn transfer_knowledge(
&mut self,
target_features: &ProblemFeatures,
target_domain: &ProblemDomain,
) -> Result<Vec<TransferableModel>, String> {
let similar_domains = self.find_similar_domains(target_features, target_domain)?;
if similar_domains.is_empty() {
return Ok(Vec::new());
}
let mut transferred_models = Vec::new();
for (domain, similarity) in similar_domains.iter().take(3) {
for strategy in &self.strategies.clone() {
if let Ok(model) =
self.apply_transfer_strategy(domain, target_features, strategy, *similarity)
{
transferred_models.push(model);
}
}
}
let transfer_record = TransferRecord {
timestamp: Instant::now(),
source_domains: similar_domains
.iter()
.map(|(d, _)| d.characteristics.clone())
.collect(),
target_features: target_features.clone(),
strategies_used: self.strategies.clone(),
success_rate: if transferred_models.is_empty() {
0.0
} else {
1.0
},
models_transferred: transferred_models.len(),
};
self.transfer_history.push_back(transfer_record);
while self.transfer_history.len() > 1000 {
self.transfer_history.pop_front();
}
Ok(transferred_models)
}
fn find_similar_domains(
&mut self,
target_features: &ProblemFeatures,
target_domain: &ProblemDomain,
) -> Result<Vec<(SourceDomain, f64)>, String> {
let mut similarities = Vec::new();
for source_domain in &self.source_domains {
if !self.is_domain_compatible(&source_domain.characteristics.domain, target_domain) {
continue;
}
let similarity = self.similarity_analyzer.calculate_similarity(
&source_domain.characteristics,
target_features,
target_domain,
)?;
if similarity > 0.3 {
similarities.push((source_domain.clone(), similarity));
}
}
similarities.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
Ok(similarities)
}
fn is_domain_compatible(
&self,
source_domain: &ProblemDomain,
target_domain: &ProblemDomain,
) -> bool {
source_domain == target_domain
|| matches!(source_domain, ProblemDomain::Combinatorial)
|| matches!(target_domain, ProblemDomain::Combinatorial)
}
fn apply_transfer_strategy(
&self,
source_domain: &SourceDomain,
target_features: &ProblemFeatures,
strategy: &TransferStrategy,
similarity: f64,
) -> Result<TransferableModel, String> {
match strategy {
TransferStrategy::InstanceTransfer => {
self.instance_transfer(source_domain, target_features, similarity)
}
TransferStrategy::FeatureTransfer => {
self.feature_transfer(source_domain, target_features, similarity)
}
TransferStrategy::ParameterTransfer => {
self.parameter_transfer(source_domain, target_features, similarity)
}
TransferStrategy::RelationTransfer => {
self.relation_transfer(source_domain, target_features, similarity)
}
TransferStrategy::ModelTransfer => {
self.model_transfer(source_domain, target_features, similarity)
}
}
}
fn instance_transfer(
&self,
source_domain: &SourceDomain,
target_features: &ProblemFeatures,
similarity: f64,
) -> Result<TransferableModel, String> {
let relevant_experiences: Vec<_> = source_domain
.experiences
.iter()
.filter(|exp| {
let exp_similarity =
self.calculate_experience_similarity(&exp.problem_features, target_features);
exp_similarity > 0.5
})
.cloned()
.collect();
Ok(TransferableModel {
model_type: ModelType::InstanceBased,
source_domain_id: source_domain.id.clone(),
parameters: HashMap::new(),
knowledge: Knowledge::Instances(relevant_experiences),
confidence: similarity * 0.8,
adaptation_required: true,
})
}
fn feature_transfer(
&self,
source_domain: &SourceDomain,
target_features: &ProblemFeatures,
similarity: f64,
) -> Result<TransferableModel, String> {
let mut feature_mappings = HashMap::new();
feature_mappings.insert(
"size_scaling".to_string(),
target_features.size as f64 / source_domain.characteristics.avg_problem_size,
);
feature_mappings.insert(
"density_scaling".to_string(),
target_features.density / source_domain.characteristics.avg_density,
);
Ok(TransferableModel {
model_type: ModelType::FeatureBased,
source_domain_id: source_domain.id.clone(),
parameters: feature_mappings,
knowledge: Knowledge::FeatureMapping(HashMap::new()),
confidence: similarity * 0.9,
adaptation_required: false,
})
}
fn parameter_transfer(
&self,
source_domain: &SourceDomain,
target_features: &ProblemFeatures,
similarity: f64,
) -> Result<TransferableModel, String> {
let mut transferred_params = HashMap::new();
if !source_domain.experiences.is_empty() {
let mut param_sums: HashMap<String, f64> = HashMap::new();
let mut param_counts: HashMap<String, usize> = HashMap::new();
for experience in &source_domain.experiences {
for (param_name, param_value) in &experience.configuration.hyperparameters {
*param_sums.entry(param_name.clone()).or_insert(0.0) += param_value;
*param_counts.entry(param_name.clone()).or_insert(0) += 1;
}
}
for (param_name, total) in param_sums {
if let Some(&count) = param_counts.get(¶m_name) {
let avg_value = total / count as f64;
let adapted_value =
self.adapt_parameter(¶m_name, avg_value, target_features, similarity);
transferred_params.insert(param_name, adapted_value);
}
}
}
Ok(TransferableModel {
model_type: ModelType::ParameterBased,
source_domain_id: source_domain.id.clone(),
parameters: transferred_params,
knowledge: Knowledge::Parameters(HashMap::new()),
confidence: similarity * 0.7,
adaptation_required: true,
})
}
fn relation_transfer(
&self,
source_domain: &SourceDomain,
target_features: &ProblemFeatures,
similarity: f64,
) -> Result<TransferableModel, String> {
let mut relations = HashMap::new();
relations.insert("size_performance_relation".to_string(), 0.8);
relations.insert("density_performance_relation".to_string(), 0.6);
Ok(TransferableModel {
model_type: ModelType::RelationBased,
source_domain_id: source_domain.id.clone(),
parameters: relations,
knowledge: Knowledge::Relations(HashMap::new()),
confidence: similarity * 0.6,
adaptation_required: true,
})
}
fn model_transfer(
&self,
source_domain: &SourceDomain,
target_features: &ProblemFeatures,
similarity: f64,
) -> Result<TransferableModel, String> {
Ok(TransferableModel {
model_type: ModelType::CompleteBased,
source_domain_id: source_domain.id.clone(),
parameters: HashMap::new(),
knowledge: Knowledge::CompleteModel(Vec::new()),
confidence: similarity * 0.5,
adaptation_required: true,
})
}
fn adapt_parameter(
&self,
param_name: &str,
value: f64,
target_features: &ProblemFeatures,
similarity: f64,
) -> f64 {
match param_name {
"initial_temperature" => {
let size_factor = (target_features.size as f64 / 100.0).sqrt();
value * size_factor * similarity
}
"cooling_rate" => {
let density_factor = target_features.density.mul_add(0.2, 1.0);
value * density_factor
}
"num_sweeps" | "max_iterations" => {
let size_factor = (target_features.size as f64 / 100.0).ln().max(1.0);
value * size_factor
}
_ => value * similarity, }
}
fn calculate_experience_similarity(
&self,
exp_features: &ProblemFeatures,
target_features: &ProblemFeatures,
) -> f64 {
let size_similarity = 1.0
- (exp_features.size as f64 - target_features.size as f64).abs()
/ exp_features.size.max(target_features.size) as f64;
let density_similarity = 1.0 - (exp_features.density - target_features.density).abs();
f64::midpoint(size_similarity, density_similarity)
}
pub fn adapt_model(
&self,
model: &mut TransferableModel,
target_features: &ProblemFeatures,
) -> Result<(), String> {
if !model.adaptation_required {
return Ok(());
}
match model.model_type {
ModelType::ParameterBased => {
for (param_name, param_value) in &mut model.parameters {
*param_value = self.adapt_parameter(
param_name,
*param_value,
target_features,
model.confidence,
);
}
}
ModelType::InstanceBased => {
if let Knowledge::Instances(ref mut instances) = model.knowledge {
instances.retain(|exp| {
self.calculate_experience_similarity(&exp.problem_features, target_features)
> 0.4
});
}
}
_ => {
}
}
model.adaptation_required = false;
Ok(())
}
#[must_use]
pub fn evaluate_transfer_success(&self) -> f64 {
if self.transfer_history.is_empty() {
return 0.0;
}
let total_success: f64 = self
.transfer_history
.iter()
.map(|record| record.success_rate)
.sum();
total_success / self.transfer_history.len() as f64
}
}
#[derive(Debug, Clone)]
pub struct SourceDomain {
pub id: String,
pub characteristics: DomainCharacteristics,
pub experiences: Vec<OptimizationExperience>,
pub models: Vec<TransferableModel>,
pub last_updated: Instant,
}
#[derive(Debug, Clone)]
pub struct DomainCharacteristics {
pub domain: ProblemDomain,
pub avg_problem_size: f64,
pub avg_density: f64,
pub typical_algorithms: Vec<AlgorithmType>,
pub performance_distribution: DistributionStats,
pub feature_importance: HashMap<String, f64>,
}
#[derive(Debug, Clone)]
pub struct TransferableModel {
pub model_type: ModelType,
pub source_domain_id: String,
pub parameters: HashMap<String, f64>,
pub knowledge: Knowledge,
pub confidence: f64,
pub adaptation_required: bool,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ModelType {
InstanceBased,
FeatureBased,
ParameterBased,
RelationBased,
CompleteBased,
}
#[derive(Debug, Clone)]
pub enum Knowledge {
Instances(Vec<OptimizationExperience>),
FeatureMapping(HashMap<String, Vec<f64>>),
Parameters(HashMap<String, DistributionStats>),
Relations(HashMap<String, f64>),
CompleteModel(Vec<u8>),
}
#[derive(Debug, Clone)]
pub struct TransferRecord {
pub timestamp: Instant,
pub source_domains: Vec<DomainCharacteristics>,
pub target_features: ProblemFeatures,
pub strategies_used: Vec<TransferStrategy>,
pub success_rate: f64,
pub models_transferred: usize,
}
#[derive(Debug)]
pub struct DomainSimilarityAnalyzer {
pub metrics: Vec<SimilarityMetric>,
pub methods: Vec<SimilarityMethod>,
pub similarity_cache: HashMap<String, f64>,
}
impl DomainSimilarityAnalyzer {
#[must_use]
pub fn new() -> Self {
Self {
metrics: vec![
SimilarityMetric::FeatureSimilarity,
SimilarityMetric::StatisticalSimilarity,
SimilarityMetric::PerformanceSimilarity,
],
methods: vec![
SimilarityMethod::EuclideanDistance,
SimilarityMethod::CosineSimilarity,
SimilarityMethod::KLDivergence,
],
similarity_cache: HashMap::new(),
}
}
pub fn calculate_similarity(
&mut self,
source_characteristics: &DomainCharacteristics,
target_features: &ProblemFeatures,
target_domain: &ProblemDomain,
) -> Result<f64, String> {
let cache_key = format!(
"{:?}_{}_{}_{}_{:?}",
source_characteristics.domain,
source_characteristics.avg_problem_size as u32,
target_features.size,
target_features.density,
target_domain
);
if let Some(&cached_similarity) = self.similarity_cache.get(&cache_key) {
return Ok(cached_similarity);
}
let mut similarity_scores = Vec::new();
let domain_similarity = if source_characteristics.domain == *target_domain {
1.0
} else if matches!(source_characteristics.domain, ProblemDomain::Combinatorial)
|| matches!(target_domain, ProblemDomain::Combinatorial)
{
0.7 } else {
0.3
};
similarity_scores.push(domain_similarity);
let size_ratio = source_characteristics.avg_problem_size / target_features.size as f64;
let size_similarity = 1.0 - (size_ratio.ln().abs() / 3.0).min(1.0); similarity_scores.push(size_similarity);
let density_similarity =
1.0 - (source_characteristics.avg_density - target_features.density).abs();
similarity_scores.push(density_similarity);
let weights = vec![0.4, 0.3, 0.3]; let overall_similarity: f64 = similarity_scores
.iter()
.zip(weights.iter())
.map(|(score, weight)| score * weight)
.sum();
self.similarity_cache.insert(cache_key, overall_similarity);
if self.similarity_cache.len() > 10_000 {
self.similarity_cache.clear();
}
Ok(overall_similarity)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SimilarityMetric {
FeatureSimilarity,
StatisticalSimilarity,
PerformanceSimilarity,
DomainSimilarity,
TaskSimilarity,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SimilarityMethod {
EuclideanDistance,
CosineSimilarity,
PearsonCorrelation,
KLDivergence,
MaximumMeanDiscrepancy,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TransferStrategy {
InstanceTransfer,
FeatureTransfer,
ParameterTransfer,
RelationTransfer,
ModelTransfer,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AdaptationMechanism {
FineTuning,
DomainAdaptation,
MultiTaskLearning,
ProgressiveTransfer,
AdversarialAdaptation,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::meta_learning_optimization::feature_extraction::{
GraphFeatures, SpectralFeatures, StatisticalFeatures,
};
#[test]
fn test_transfer_learner_creation() {
let transfer_learner = TransferLearner::new();
assert_eq!(transfer_learner.source_domains.len(), 0);
assert_eq!(transfer_learner.strategies.len(), 3);
}
#[test]
fn test_domain_similarity_analyzer() {
let mut analyzer = DomainSimilarityAnalyzer::new();
let source_characteristics = DomainCharacteristics {
domain: ProblemDomain::Combinatorial,
avg_problem_size: 100.0,
avg_density: 0.5,
typical_algorithms: vec![AlgorithmType::SimulatedAnnealing],
performance_distribution: DistributionStats::default(),
feature_importance: HashMap::new(),
};
let target_features = ProblemFeatures {
size: 120,
density: 0.6,
graph_features: GraphFeatures::default(),
statistical_features: StatisticalFeatures::default(),
spectral_features: SpectralFeatures::default(),
domain_features: HashMap::new(),
};
let target_domain = ProblemDomain::Combinatorial;
let similarity = analyzer.calculate_similarity(
&source_characteristics,
&target_features,
&target_domain,
);
assert!(similarity.is_ok());
let sim_value = similarity.expect("calculate_similarity should succeed");
assert!(sim_value >= 0.0 && sim_value <= 1.0);
}
#[test]
fn test_transferable_model() {
let model = TransferableModel {
model_type: ModelType::ParameterBased,
source_domain_id: "test_domain".to_string(),
parameters: HashMap::new(),
knowledge: Knowledge::Parameters(HashMap::new()),
confidence: 0.8,
adaptation_required: true,
};
assert_eq!(model.model_type, ModelType::ParameterBased);
assert!(model.adaptation_required);
assert_eq!(model.confidence, 0.8);
}
}