-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #194 from mahf-opt/add-swarm-operators
Add more swarm-based metaheuristics
- Loading branch information
Showing
13 changed files
with
488 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
//! Replacement components for the Black Hole algorithm (BH). | ||
use rand::Rng; | ||
use serde::{Deserialize, Serialize}; | ||
|
||
use crate::{ | ||
problems::LimitedVectorProblem, utils::squared_euclidean, Component, ExecResult, | ||
SingleObjectiveProblem, State, | ||
}; | ||
|
||
#[derive(Clone, Serialize, Deserialize)] | ||
pub struct EventHorizon; | ||
|
||
impl EventHorizon { | ||
pub fn new<P>() -> Box<dyn Component<P>> | ||
where | ||
P: LimitedVectorProblem<Element = f64>, | ||
P: SingleObjectiveProblem, | ||
{ | ||
Box::new(Self) | ||
} | ||
} | ||
|
||
impl<P> Component<P> for EventHorizon | ||
where | ||
P: LimitedVectorProblem<Element = f64>, | ||
P: SingleObjectiveProblem, | ||
{ | ||
fn execute(&self, problem: &P, state: &mut State<P>) -> ExecResult<()> { | ||
let mut populations = state.populations_mut(); | ||
let offspring = populations.current_mut(); | ||
|
||
let f_bh = state.best_objective_value().unwrap().value(); | ||
|
||
let fitness_sum = offspring.iter().map(|x| x.objective().value()).sum::<f64>(); | ||
let radius = f_bh / fitness_sum; | ||
|
||
let (index, best) = offspring | ||
.iter() | ||
.enumerate() | ||
.min_by_key(|(_u, i)| i.objective()) | ||
.unwrap(); | ||
let distances = offspring | ||
.iter() | ||
.map(|o| squared_euclidean(o.solution(), best.solution()).sqrt()) | ||
.collect::<Vec<f64>>(); | ||
|
||
for (u, i) in offspring.iter_mut().enumerate() { | ||
// do not replace best individual | ||
if distances[u] < radius && u != index { | ||
let rand: Vec<f64> = problem | ||
.domain() | ||
.iter() | ||
.map(|d| state.random_mut().gen_range(d.clone())) | ||
.collect(); | ||
*i.solution_mut() = rand; | ||
} | ||
} | ||
Ok(()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,6 +8,7 @@ use crate::{ | |
Individual, Problem, State, | ||
}; | ||
|
||
pub mod bh; | ||
pub mod common; | ||
pub mod sa; | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
use rand::distributions::{Distribution, Uniform}; | ||
use serde::Serialize; | ||
|
||
use crate::{ | ||
component::ExecResult, | ||
components::Component, | ||
identifier::{Global, Identifier, PhantomId}, | ||
population::{AsSolutionsMut, BestIndividual}, | ||
problems::LimitedVectorProblem, | ||
SingleObjectiveProblem, State, | ||
}; | ||
|
||
/// Updates the positions in the black hole algorithm. | ||
/// | ||
/// Originally proposed for, and used as operator in [`bh`]. | ||
/// The operator is similar to the [`ParticleVelocitiesUpdate`] in [`pso`]. | ||
/// Specifically, they are identical when for [`pso`]: | ||
/// inertia weight = 0, | ||
/// c_1 = 0, | ||
/// c_2 = 1, | ||
/// v_max = 1 | ||
/// | ||
/// [`bh`]: crate::heuristics::bh | ||
/// [`ParticleVelocitiesUpdate`]: crate::components::swarm::pso::ParticleVelocitiesUpdate` | ||
/// [`pso`]: crate::heuristics::pso | ||
#[derive(Clone, Serialize)] | ||
pub struct BlackHoleParticlesUpdate<I: Identifier = Global> { | ||
id: PhantomId<I>, | ||
} | ||
|
||
impl<I: Identifier> BlackHoleParticlesUpdate<I> { | ||
pub fn from_params() -> Self { | ||
Self { | ||
id: PhantomId::default(), | ||
} | ||
} | ||
|
||
pub fn new_with_id<P>() -> Box<dyn Component<P>> | ||
where | ||
P: SingleObjectiveProblem + LimitedVectorProblem<Element = f64>, | ||
{ | ||
Box::new(Self::from_params()) | ||
} | ||
} | ||
|
||
impl BlackHoleParticlesUpdate<Global> { | ||
pub fn new<P>() -> Box<dyn Component<P>> | ||
where | ||
P: SingleObjectiveProblem + LimitedVectorProblem<Element = f64>, | ||
{ | ||
Self::new_with_id() | ||
} | ||
} | ||
|
||
impl<P, I> Component<P> for BlackHoleParticlesUpdate<I> | ||
where | ||
P: SingleObjectiveProblem + LimitedVectorProblem<Element = f64>, | ||
I: Identifier, | ||
{ | ||
fn init(&self, _problem: &P, _state: &mut State<P>) -> ExecResult<()> { | ||
Ok(()) | ||
} | ||
|
||
fn execute(&self, _problem: &P, state: &mut State<P>) -> ExecResult<()> { | ||
let distribution = Uniform::new(0.0, 1.0); | ||
|
||
// Get necessary state like global best `xg` | ||
let best = state.populations().current().best_individual().cloned(); | ||
let best_ind = best.unwrap(); | ||
let xg = best_ind.solution(); | ||
let mut population = state.populations_mut(); | ||
let xs = population.current_mut().as_solutions_mut(); | ||
|
||
// Perform the update step. | ||
for x in xs { | ||
for i in 0..x.len() { | ||
// Calculate change in position | ||
let pos = distribution.sample(&mut *state.random_mut()) * (xg[i] - x[i]); | ||
// Add value to particle position | ||
x[i] += pos; | ||
} | ||
} | ||
Ok(()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
use std::array::from_mut; | ||
|
||
use better_any::{Tid, TidAble}; | ||
use derive_more::{Deref, DerefMut}; | ||
use itertools::izip; | ||
use rand::Rng; | ||
use serde::Serialize; | ||
|
||
use crate::{ | ||
component::ExecResult, | ||
components::Component, | ||
identifier::{Global, Identifier, PhantomId}, | ||
problems::LimitedVectorProblem, | ||
state::{common, common::Evaluator}, | ||
CustomState, State, | ||
}; | ||
|
||
/// Updates the and firefly positions. | ||
/// | ||
/// Originally proposed for, and used as operator in [`fa`]. | ||
/// | ||
/// Uses the [`RandomizationParameter`]. | ||
/// | ||
/// [`fa`]: crate::heuristics::fa | ||
#[derive(Clone, Serialize)] | ||
pub struct FireflyPositionsUpdate<I: Identifier = Global> { | ||
pub alpha: f64, | ||
pub beta: f64, | ||
pub gamma: f64, | ||
id: PhantomId<I>, | ||
} | ||
|
||
impl<I: Identifier> FireflyPositionsUpdate<I> { | ||
pub fn from_params(alpha: f64, beta: f64, gamma: f64) -> Self { | ||
Self { | ||
alpha, | ||
beta, | ||
gamma, | ||
id: PhantomId::default(), | ||
} | ||
} | ||
|
||
pub fn new_with_id<P>(alpha: f64, beta: f64, gamma: f64) -> Box<dyn Component<P>> | ||
where | ||
P: LimitedVectorProblem<Element = f64>, | ||
{ | ||
Box::new(Self::from_params(alpha, beta, gamma)) | ||
} | ||
} | ||
|
||
impl FireflyPositionsUpdate<Global> { | ||
pub fn new<P>(alpha: f64, beta: f64, gamma: f64) -> Box<dyn Component<P>> | ||
where | ||
P: LimitedVectorProblem<Element = f64>, | ||
{ | ||
Self::new_with_id(alpha, beta, gamma) | ||
} | ||
} | ||
|
||
impl<P, I> Component<P> for FireflyPositionsUpdate<I> | ||
where | ||
P: LimitedVectorProblem<Element = f64>, | ||
I: Identifier, | ||
{ | ||
fn init(&self, _problem: &P, state: &mut State<P>) -> ExecResult<()> { | ||
state.insert(RandomizationParameter(self.alpha)); | ||
Ok(()) | ||
} | ||
|
||
fn execute(&self, problem: &P, state: &mut State<P>) -> ExecResult<()> { | ||
// Prepare parameters | ||
let &Self { beta, gamma, .. } = self; | ||
let a = state.get_value::<RandomizationParameter>(); | ||
|
||
// Get population from state | ||
let mut individuals = state.populations_mut().pop(); | ||
|
||
// scale for adapting to problem domain | ||
let scales = problem | ||
.domain() | ||
.iter() | ||
.map(|p| (p.end - p.start).abs()) | ||
.collect::<Vec<f64>>(); | ||
|
||
// Perform the update step. | ||
for i in 0..individuals.len() { | ||
for j in 0..individuals.len() { | ||
// if individual j is "more attractive" (i.e. has lower fitness), move towards j | ||
if individuals[i].objective() > individuals[j].objective() { | ||
// draw random values from uniform distribution between 0 and 1 | ||
// according to paper: also possible to use normal distribution, depending on problem | ||
let rands: Vec<f64> = (0..problem.dimension()) | ||
.map(|_| state.random_mut().gen_range(0.0..1.0)) | ||
.collect(); | ||
let mut current = individuals[i].clone(); | ||
izip!( | ||
current.solution_mut(), | ||
individuals[j].solution(), | ||
&scales, | ||
rands | ||
) | ||
.map(|(xi, xj, scale, rand)| { | ||
let pos = beta * (-gamma * (*xi - xj).powf(2.0)).exp() * (xj - *xi) | ||
+ a * (rand - 0.5) * scale; | ||
(xi, pos) | ||
}) | ||
.for_each(|(xi, pos)| *xi += pos); | ||
individuals[i] = current; | ||
|
||
state.holding::<Evaluator<P, I>>( | ||
|evaluator: &mut Evaluator<P, I>, state| { | ||
evaluator.as_inner_mut().evaluate( | ||
problem, | ||
state, | ||
from_mut(&mut individuals[i]), | ||
); | ||
Ok(()) | ||
}, | ||
)?; | ||
*state.borrow_value_mut::<common::Evaluations>() += 1; | ||
} | ||
} | ||
} | ||
state.populations_mut().push(individuals); | ||
Ok(()) | ||
} | ||
} | ||
|
||
/// The randomization parameter used to update the firefly positions. | ||
#[derive(Deref, DerefMut, Tid)] | ||
pub struct RandomizationParameter( | ||
#[deref] | ||
#[deref_mut] | ||
pub f64, | ||
); | ||
|
||
impl CustomState<'_> for RandomizationParameter {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
pub mod bh; | ||
pub mod fa; | ||
pub mod pso; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.