Skip to content

Commit

Permalink
Fix black hole algorithm
Browse files Browse the repository at this point in the history
  • Loading branch information
HeleNoir committed Oct 12, 2023
1 parent 5a2cd02 commit 5a03c13
Show file tree
Hide file tree
Showing 10 changed files with 84 additions and 70 deletions.
25 changes: 15 additions & 10 deletions src/components/replacement/bh.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
//! Replacement components for the Black Hole algorithm (BH).
use rand::Rng;
use serde::{Deserialize, Serialize};
use crate::{Component, ExecResult, Individual, SingleObjectiveProblem, State};
use crate::population::BestIndividual;
use crate::problems::LimitedVectorProblem;
use crate::utils::squared_euclidean;
use crate::{Component, ExecResult, Individual, SingleObjectiveProblem, State};
use rand::Rng;
use serde::{Deserialize, Serialize};

#[derive(Clone, Serialize, Deserialize)]
pub struct EventHorizon;
Expand All @@ -28,30 +28,35 @@ impl<P> Component<P> for EventHorizon

fn execute(&self, problem: &P, state: &mut State<P>) -> ExecResult<()> {
let mut populations = state.populations_mut();
let mut offspring = populations.pop();
let offspring = populations.pop();

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 best_ind = state.populations().current().best_individual().cloned();
let best_ind = offspring.best_individual().cloned();
let best = best_ind.unwrap().solution().clone();
let distances = offspring
.iter()
.map(|o| squared_euclidean(o.solution(), &best).sqrt())
.collect::<Vec<f64>>();

for (u, mut i) in offspring.iter().enumerate() {
if distances[u] < radius {
let mut new_offspring: Vec<Individual<P>> = vec![];
for (u, i) in offspring.iter().enumerate() {
// do not replace best individual
if distances[u] < radius && distances[u] != 0.0 {
let rand: Vec<f64> = (0..problem.dimension())
.map(|_| state.random_mut().gen_range(problem.domain()[0].clone()))
.collect();
let j = Individual::new_unevaluated(rand);
i = &j;
//println!("{:?}, {:?}", u, &j);
new_offspring.push(j);
} else {
new_offspring.push(i.clone());
}
}
populations.push(offspring);
populations.push(new_offspring);
Ok(())
}
}
}
2 changes: 1 addition & 1 deletion src/components/replacement/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ use crate::{
Individual, Problem, State,
};

pub mod common;
pub mod bh;
pub mod common;
pub mod sa;

pub use common::{
Expand Down
2 changes: 0 additions & 2 deletions src/components/replacement/sa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ impl<P: SingleObjectiveProblem> Component<P> for ExponentialAnnealingAcceptance
Ok(())
}


#[ensures(state.populations().current().len() == 1, "population after should contain a single individual")]
#[ensures(state.populations().len() == old(state.populations().len()) - 1)]
fn execute(&self, _problem: &P, state: &mut State<P>) -> ExecResult<()> {
Expand All @@ -76,7 +75,6 @@ impl<P: SingleObjectiveProblem> Component<P> for ExponentialAnnealingAcceptance
} else {
populations.pop();
}

Ok(())
}
}
29 changes: 17 additions & 12 deletions src/components/swarm/bh.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
use rand::{
distributions::{Distribution, Uniform},
};
use rand::distributions::{Distribution, Uniform};
use serde::Serialize;

use crate::{component::ExecResult, components::Component, identifier::{Global, Identifier, PhantomId}, problems::{LimitedVectorProblem}, State};
use crate::population::{AsSolutionsMut, BestIndividual};
use crate::{
component::ExecResult,
components::Component,
identifier::{Global, Identifier, PhantomId},
problems::{LimitedVectorProblem},
SingleObjectiveProblem, State
};

/// Updates the positions in the black hole algorithm.
///
Expand All @@ -31,7 +34,7 @@ impl<I: Identifier> BlackHoleParticlesUpdate<I> {

pub fn new_with_id<P>() -> Box<dyn Component<P>>
where
P: LimitedVectorProblem<Element = f64>,
P: SingleObjectiveProblem + LimitedVectorProblem<Element = f64>,
{
Box::new(Self::from_params())
}
Expand All @@ -40,28 +43,30 @@ impl<I: Identifier> BlackHoleParticlesUpdate<I> {
impl BlackHoleParticlesUpdate<Global> {
pub fn new<P>() -> Box<dyn Component<P>>
where
P: LimitedVectorProblem<Element = f64>,
P: SingleObjectiveProblem + LimitedVectorProblem<Element = f64>,
{
Self::new_with_id()
}
}

impl<P, I> Component<P> for BlackHoleParticlesUpdate<I>
where
P: LimitedVectorProblem<Element = f64>,
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 mut distr = Uniform::new(0.0, 1.0);
let distr = Uniform::new(0.0, 1.0);

// Get necessary state like global best `xg`
let best = state.populations().current().best_individual().cloned();
let xg = best.unwrap().solution();
let xs = state.populations_mut().current_mut().as_solutions_mut();
let binding = best.unwrap();
let xg = binding.solution();
let mut binding2 = state.populations_mut();
let xs = binding2.current_mut().as_solutions_mut();

// Perform the update step.
for x in xs {
Expand All @@ -74,4 +79,4 @@ impl<P, I> Component<P> for BlackHoleParticlesUpdate<I>
}
Ok(())
}
}
}
35 changes: 23 additions & 12 deletions src/components/swarm/fa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,18 @@ use std::array::from_mut;

use better_any::{Tid, TidAble};
use derive_more::{Deref, DerefMut};
use itertools::{izip};
use itertools::izip;
use rand::Rng;
use serde::Serialize;

use crate::{component::ExecResult, components::Component, identifier::{Global, Identifier, PhantomId}, problems::{LimitedVectorProblem}, CustomState, State};
use crate::state::common;
use crate::state::common::Evaluator;
use crate::{
component::ExecResult,
components::Component,
identifier::{Global, Identifier, PhantomId},
problems::{LimitedVectorProblem},
CustomState, State};

/// Updates the and firefly positions.
///
Expand Down Expand Up @@ -67,43 +72,49 @@ impl<P, I> Component<P> for FireflyPositionsUpdate<I>
}

fn execute(&self, problem: &P, state: &mut State<P>) -> ExecResult<()> {

// Prepare parameters
let &Self {
beta, gamma, ..
} = self;
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()
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 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 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) })
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]));
.evaluate(
problem,
state,
from_mut(&mut individuals[i])
);
Ok(())
},
)?;
Expand Down
12 changes: 6 additions & 6 deletions src/components/swarm/pso.rs
Original file line number Diff line number Diff line change
Expand Up @@ -335,17 +335,17 @@ impl<I: Identifier> GlobalBestParticleUpdate<I> {
}

pub fn new<P>() -> Box<dyn Component<P>>
where
P: SingleObjectiveProblem + LimitedVectorProblem<Element = f64>,
where
P: SingleObjectiveProblem + LimitedVectorProblem<Element = f64>,
{
Box::new(Self::from_params())
}
}

impl<P, I> Component<P> for GlobalBestParticleUpdate<I>
where
P: SingleObjectiveProblem + LimitedVectorProblem<Element = f64>,
I: Identifier,
where
P: SingleObjectiveProblem + LimitedVectorProblem<Element = f64>,
I: Identifier,
{
fn init(&self, _problem: &P, state: &mut State<P>) -> ExecResult<()> {
state
Expand Down Expand Up @@ -422,4 +422,4 @@ impl ParticleSwarmUpdate<Global> {
) -> Box<dyn Component<P>> {
Self::new_with_id()
}
}
}
22 changes: 11 additions & 11 deletions src/heuristics/bh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,14 @@
use crate::{
component::ExecResult,
components::{boundary, initialization, swarm},
components::{boundary, initialization, replacement, swarm},
conditions::Condition,
configuration::Configuration,
identifier::{Global, Identifier},
logging::Logger,
problems::{LimitedVectorProblem, SingleObjectiveProblem},
Component,
};
use crate::components::replacement;

/// Parameters for [`real_bh`].
pub struct RealProblemParameters {
Expand All @@ -31,12 +30,10 @@ pub fn real_bh<P>(
params: RealProblemParameters,
condition: Box<dyn Condition<P>>,
) -> ExecResult<Configuration<P>>
where
P: SingleObjectiveProblem + LimitedVectorProblem<Element = f64>,
where
P: SingleObjectiveProblem + LimitedVectorProblem<Element = f64>,
{
let RealProblemParameters {
num_particles,
} = params;
let RealProblemParameters { num_particles } = params;

Ok(Configuration::builder()
.do_(initialization::RandomSpread::new(num_particles))
Expand All @@ -46,6 +43,7 @@ pub fn real_bh<P>(
Parameters {
particle_update: swarm::bh::BlackHoleParticlesUpdate::new(),
constraints: boundary::Saturation::new(),
replacement: replacement::bh::EventHorizon::new(),
},
condition,
))
Expand All @@ -56,17 +54,19 @@ pub fn real_bh<P>(
pub struct Parameters<P> {
pub particle_update: Box<dyn Component<P>>,
pub constraints: Box<dyn Component<P>>,
pub replacement: Box<dyn Component<P>>,
}

/// A generic single-objective Black Hole algorithm (BH) template.
pub fn bh<P, I>(params: Parameters<P>, condition: Box<dyn Condition<P>>) -> Box<dyn Component<P>>
where
P: SingleObjectiveProblem + LimitedVectorProblem<Element = f64>,
I: Identifier,
where
P: SingleObjectiveProblem,
I: Identifier,
{
let Parameters {
particle_update,
constraints,
replacement,
} = params;

Configuration::builder()
Expand All @@ -76,7 +76,7 @@ pub fn bh<P, I>(params: Parameters<P>, condition: Box<dyn Condition<P>>) -> Box<
.do_(constraints)
.evaluate_with::<I>()
.update_best_individual()
.do_(replacement::bh::EventHorizon::new())
.do_(replacement)
.evaluate_with::<I>()
.update_best_individual()
.do_(Logger::new())
Expand Down
19 changes: 7 additions & 12 deletions src/heuristics/fa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
//! SAGA 2009. Lecture Notes in Computer Science, vol 5792. Springer, Berlin, Heidelberg.
//! DOI:<https://doi.org/10.1007/978-3-642-04944-6_14>

use crate::{
component::ExecResult,
components::{boundary, initialization, mapping, swarm},
Expand Down Expand Up @@ -37,8 +36,8 @@ pub fn real_fa<P>(
params: RealProblemParameters,
condition: Box<dyn Condition<P>>,
) -> ExecResult<Configuration<P>>
where
P: SingleObjectiveProblem + LimitedVectorProblem<Element = f64>,
where
P: SingleObjectiveProblem + LimitedVectorProblem<Element = f64>,
{
let RealProblemParameters {
pop_size,
Expand All @@ -54,11 +53,7 @@ pub fn real_fa<P>(
.update_best_individual()
.do_(fa::<P, Global>(
Parameters {
firefly_update: swarm::fa::FireflyPositionsUpdate::new(
alpha,
beta,
gamma,
),
firefly_update: swarm::fa::FireflyPositionsUpdate::new(alpha, beta, gamma),
constraints: boundary::Saturation::new(),
alpha_update: Box::from(mapping::sa::GeometricCooling::new(
delta,
Expand All @@ -79,9 +74,9 @@ pub struct Parameters<P> {

/// A generic single-objective Firefly Algorithm (FA) template.
pub fn fa<P, I>(params: Parameters<P>, condition: Box<dyn Condition<P>>) -> Box<dyn Component<P>>
where
P: SingleObjectiveProblem,
I: Identifier,
where
P: SingleObjectiveProblem,
I: Identifier,
{
let Parameters {
firefly_update,
Expand All @@ -100,4 +95,4 @@ pub fn fa<P, I>(params: Parameters<P>, condition: Box<dyn Condition<P>>) -> Box<
.do_(Logger::new())
})
.build_component()
}
}
Loading

0 comments on commit 5a03c13

Please sign in to comment.