use crate::configuration::ProblemSolving;
use crate::island::configuration::{IslandConfiguration, MigrationPolicy};
use crate::island::topology::neighbors;
use crate::nsga2::pareto::ParetoIndividual;
use crate::population::Population;
use crate::traits::ChromosomeT;
use log::debug;
use rand::Rng;
pub fn migrate<U>(
islands: &mut [Population<U>],
config: &IslandConfiguration,
problem_solving: ProblemSolving,
) -> Result<(), crate::error::GaError>
where
U: ChromosomeT,
{
let num_islands = islands.len();
if num_islands <= 1 {
return Ok(());
}
for island in islands.iter() {
if island.size() == 0 {
return Err(crate::error::GaError::MigrationError(
"Cannot migrate from an empty island".to_string(),
));
}
if config.migration_count > island.size() {
return Err(crate::error::GaError::MigrationError(format!(
"Migration count ({}) exceeds island population size ({})",
config.migration_count,
island.size()
)));
}
}
let mut rng = crate::rng::make_rng();
let mut all_migrants: Vec<Vec<U>> = Vec::with_capacity(num_islands);
for island in islands.iter() {
let migrants = match config.migration_policy {
MigrationPolicy::BestReplaceWorst => {
select_best(island, config.migration_count, problem_solving)
}
MigrationPolicy::RandomReplaceWorst | MigrationPolicy::RandomReplaceRandom => {
select_random(island, config.migration_count, &mut rng)
}
MigrationPolicy::TournamentMigrant => {
select_tournament(island, config.migration_count, problem_solving, &mut rng)
}
};
all_migrants.push(migrants);
}
for (source_idx, source_migrants) in all_migrants.iter().enumerate() {
let neighbors = neighbors(source_idx, num_islands, &config.topology);
for &dest_idx in &neighbors {
let migrants = source_migrants.clone();
match config.migration_policy {
MigrationPolicy::BestReplaceWorst
| MigrationPolicy::RandomReplaceWorst
| MigrationPolicy::TournamentMigrant => {
replace_worst(&mut islands[dest_idx], &migrants, problem_solving);
}
MigrationPolicy::RandomReplaceRandom => {
replace_random(&mut islands[dest_idx], &migrants, &mut rng);
}
}
debug!(
target: "island_events",
"Migrated {} individuals from island {} to island {} (policy={:?})",
migrants.len(),
source_idx,
dest_idx,
config.migration_policy
);
}
}
Ok(())
}
fn select_best<U>(
population: &Population<U>,
count: usize,
problem_solving: ProblemSolving,
) -> Vec<U>
where
U: ChromosomeT,
{
let mut indices: Vec<usize> = (0..population.size()).collect();
indices.sort_by(|&a, &b| {
let fa = population.chromosomes[a].fitness();
let fb = population.chromosomes[b].fitness();
match problem_solving {
ProblemSolving::Minimization | ProblemSolving::FixedFitness => {
fa.partial_cmp(&fb).unwrap_or(std::cmp::Ordering::Equal)
}
ProblemSolving::Maximization => {
fb.partial_cmp(&fa).unwrap_or(std::cmp::Ordering::Equal)
}
}
});
indices
.into_iter()
.take(count)
.map(|i| population.chromosomes[i].clone())
.collect()
}
fn select_random<U>(population: &Population<U>, count: usize, rng: &mut impl Rng) -> Vec<U>
where
U: ChromosomeT,
{
let n = population.size();
(0..count)
.map(|_| {
let idx = rng.random_range(0..n);
population.chromosomes[idx].clone()
})
.collect()
}
fn select_tournament<U>(
population: &Population<U>,
count: usize,
problem_solving: ProblemSolving,
rng: &mut impl Rng,
) -> Vec<U>
where
U: ChromosomeT,
{
let n = population.size();
(0..count)
.map(|_| {
let i = rng.random_range(0..n);
let j = rng.random_range(0..n);
let fi = population.chromosomes[i].fitness();
let fj = population.chromosomes[j].fitness();
let winner = match problem_solving {
ProblemSolving::Minimization | ProblemSolving::FixedFitness => {
if fi <= fj {
i
} else {
j
}
}
ProblemSolving::Maximization => {
if fi >= fj {
i
} else {
j
}
}
};
population.chromosomes[winner].clone()
})
.collect()
}
fn replace_worst<U>(population: &mut Population<U>, migrants: &[U], problem_solving: ProblemSolving)
where
U: ChromosomeT,
{
if migrants.is_empty() || population.size() == 0 {
return;
}
let mut indices: Vec<usize> = (0..population.size()).collect();
indices.sort_by(|&a, &b| {
let fa = population.chromosomes[a].fitness();
let fb = population.chromosomes[b].fitness();
match problem_solving {
ProblemSolving::Minimization | ProblemSolving::FixedFitness => {
fb.partial_cmp(&fa).unwrap_or(std::cmp::Ordering::Equal)
}
ProblemSolving::Maximization => {
fa.partial_cmp(&fb).unwrap_or(std::cmp::Ordering::Equal)
}
}
});
let replace_count = migrants.len().min(population.size());
for (m_idx, &worst_idx) in indices.iter().take(replace_count).enumerate() {
population.chromosomes[worst_idx] = migrants[m_idx].clone();
}
}
fn replace_random<U>(population: &mut Population<U>, migrants: &[U], rng: &mut impl Rng)
where
U: ChromosomeT,
{
if migrants.is_empty() || population.size() == 0 {
return;
}
let n = population.size();
let replace_count = migrants.len().min(n);
for (m_idx, _) in migrants.iter().enumerate().take(replace_count) {
let target = rng.random_range(0..n);
population.chromosomes[target] = migrants[m_idx].clone();
}
}
pub fn migrate_pareto<U>(
islands: &mut [Vec<ParetoIndividual<U>>],
config: &IslandConfiguration,
) -> Result<(), crate::error::GaError>
where
U: ChromosomeT,
{
let num_islands = islands.len();
if num_islands <= 1 {
return Ok(());
}
for island in islands.iter() {
if island.is_empty() {
return Err(crate::error::GaError::MigrationError(
"Cannot migrate from an empty island".to_string(),
));
}
if config.migration_count > island.len() {
return Err(crate::error::GaError::MigrationError(format!(
"Migration count ({}) exceeds island population size ({})",
config.migration_count,
island.len()
)));
}
}
let mut all_migrants: Vec<Vec<ParetoIndividual<U>>> = Vec::with_capacity(num_islands);
for island in islands.iter() {
let migrants = select_best_pareto(island, config.migration_count);
all_migrants.push(migrants);
}
for (source_idx, source_migrants) in all_migrants.iter().enumerate() {
let dest_indices = neighbors(source_idx, num_islands, &config.topology);
for &dest_idx in &dest_indices {
let migrants = source_migrants.clone();
replace_worst_pareto(&mut islands[dest_idx], &migrants);
debug!(
target: "island_events",
"Pareto migration: {} individuals from island {} to island {}",
migrants.len(),
source_idx,
dest_idx
);
}
}
Ok(())
}
fn select_best_pareto<U>(island: &[ParetoIndividual<U>], count: usize) -> Vec<ParetoIndividual<U>>
where
U: ChromosomeT,
{
let mut indices: Vec<usize> = (0..island.len()).collect();
indices.sort_by(|&a, &b| {
island[a].rank.cmp(&island[b].rank).then_with(|| {
island[b]
.crowding_distance
.partial_cmp(&island[a].crowding_distance)
.unwrap_or(std::cmp::Ordering::Equal)
})
});
indices
.into_iter()
.take(count)
.map(|i| island[i].clone())
.collect()
}
fn replace_worst_pareto<U>(island: &mut [ParetoIndividual<U>], migrants: &[ParetoIndividual<U>])
where
U: ChromosomeT,
{
if migrants.is_empty() || island.is_empty() {
return;
}
let mut indices: Vec<usize> = (0..island.len()).collect();
indices.sort_by(|&a, &b| {
island[b].rank.cmp(&island[a].rank).then_with(|| {
island[a]
.crowding_distance
.partial_cmp(&island[b].crowding_distance)
.unwrap_or(std::cmp::Ordering::Equal)
})
});
let replace_count = migrants.len().min(island.len());
for (m_idx, &worst_idx) in indices.iter().take(replace_count).enumerate() {
island[worst_idx] = migrants[m_idx].clone();
}
}