Skip to content

Commit

Permalink
Merge pull request #158 from mahf-opt/state-refactors
Browse files Browse the repository at this point in the history
  • Loading branch information
Saethox authored Apr 2, 2023
2 parents e2933bc + 516bc05 commit d8d5f98
Show file tree
Hide file tree
Showing 42 changed files with 593 additions and 509 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ version = "0.1.0"
anyhow = "1.0.51"
ciborium = "0.2.0"
coco-rs = "0.5"
derive_deref = "1.1.1"
derive_more = { version = "0.99.17", features = ["deref", "deref_mut"]}
embed-doc-image = "0.1.4"
erased-serde = "0.3.16"
float_eq = "0.7.0"
Expand Down
4 changes: 2 additions & 2 deletions docs/framework.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,9 @@ Configuration::builder()
Every component has to implement the [Component](components::Component) trait, which looks like this:
```ignore
pub trait Component<P: Problem>: AnyComponent {
fn execute(&self, problem: &P, state: &mut State);
fn execute(&self, problem: &P, state: &mut State<P>);
fn initialize(&self, problem: &P, state: &mut State) { ... }
fn initialize(&self, problem: &P, state: &mut State<P>) { ... }
}
```

Expand Down
4 changes: 2 additions & 2 deletions docs/state.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ You would start by defining the state like this:

```rust
use mahf::framework::state::CustomState;
use mahf::derive_deref::{Deref, DerefMut};
use mahf::derive_more::{Deref, DerefMut};
use mahf::serde::Serialize;

#[derive(Default, Debug, Deref, DerefMut, Serialize)]
Expand All @@ -32,7 +32,7 @@ Now you can use it in your component:

The [CustomState] trait serves as a marker for custom state.
You can take a look at its documentation to get a list off all state types provided by MAHF.
If you have custom state representing a single value, it is recommended to also derive [Deref](derive_deref::Deref), [DerefMut](derive_deref::DerefMut) and [serde::Serialize].
If you have custom state representing a single value, it is recommended to also derive [Deref](derive_more::Deref), [DerefMut](derive_more::DerefMut) and [serde::Serialize].

## Mutable Access

Expand Down
35 changes: 16 additions & 19 deletions examples/bmf.rs
Original file line number Diff line number Diff line change
@@ -1,32 +1,29 @@
use mahf::framework::Random;
use mahf::prelude::*;
use problems::bmf::BenchmarkFunction;

type P = problems::bmf::BenchmarkFunction;

fn main() -> anyhow::Result<()> {
let problem = P::sphere(10);
let config = pso::real_pso(
fn main() {
// Specify the problem: Sphere function with 10 dimensions.
let problem: BenchmarkFunction = BenchmarkFunction::sphere(/*dim: */ 10);
// Specify the metaheuristic: Particle Swarm Optimization (pre-implemented in MAHF).
let config: Configuration<BenchmarkFunction> = pso::real_pso(
/*params: */
pso::RealProblemParameters {
num_particles: 100,
num_particles: 20,
weight: 1.0,
c_one: 1.0,
c_two: 1.0,
v_max: 1.0,
},
termination::FixedIterations::new(500),
/*termination: */
termination::FixedIterations::new(/*max_iterations: */ 500)
& termination::DistanceToOpt::new(0.01),
);

let state = config.optimize_with(&problem, |state| state.insert(Random::seeded(0)));
// Execute the metaheuristic on the problem with a random seed.
let state: State<BenchmarkFunction> = config.optimize(&problem);

println!(
"Found Fitness: {:?}",
state.best_objective_value::<P>().unwrap()
);
println!(
"Found Individual: {:?}",
state.best_individual::<P>().unwrap(),
);
// Print the results.
println!("Found Individual: {:?}", state.best_individual().unwrap());
println!("This took {} iterations.", state.iterations());
println!("Global Optimum: {}", problem.known_optimum());

Ok(())
}
2 changes: 1 addition & 1 deletion examples/coco.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ fn main() -> anyhow::Result<()> {
.with_common_extractors(trigger::Iteration::new(10))
.with(
trigger::Change::<common::Progress>::new(0.1),
functions::auto::<common::Progress>,
functions::auto::<common::Progress, _>,
)
.with(
trigger::Iteration::new(50),
Expand Down
57 changes: 37 additions & 20 deletions examples/tsp.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,43 @@
use aco::ant_ops;
use mahf::prelude::*;
use problems::tsp::{self, SymmetricTsp};
use tracking::{files, functions, trigger};

type P = problems::tsp::SymmetricTsp;

fn main() -> anyhow::Result<()> {
let problem = problems::tsp::Instances::BERLIN52.load();

let config = ils::permutation_iterated_local_search(
ils::PermutationProblemParameters {
local_search_params: ls::PermutationProblemParameters {
n_neighbors: 100,
n_swap: 10,
fn main() {
// Specify the problem: TSPLIB instance Berlin52.
let problem: SymmetricTsp = tsp::Instances::BERLIN52.load();
// Specify the metaheuristic: Ant System.
let config: Configuration<SymmetricTsp> = Configuration::builder()
.do_(initialization::Empty::new())
.while_(
termination::FixedEvaluations::new(/*max_evaluations: */ 10_000),
|builder| {
builder
.do_(ant_ops::AcoGeneration::new(
/*num_ants: */ 20, /*alpha: */ 2.0, /*beta: */ 1.0,
/*initial_pheromones: */ 0.0,
))
.evaluate()
.update_best_individual()
.do_(ant_ops::AsPheromoneUpdate::new(
/*evaporation: */ 0.2, /*decay_coefficient: */ 1.0,
))
.do_(tracking::Logger::new())
},
local_search_termination: termination::FixedIterations::new(100),
},
termination::FixedIterations::new(10),
)
.into_builder()
.assert(|state| state.population_stack::<P>().current().len() == 1)
.single_objective_summary()
.build();
)
.build();

config.optimize(&problem);
// Execute the metaheuristic on the problem.
let state: State<SymmetricTsp> = config.optimize_with(&problem, |state| {
// Set the seed to 42.
state.insert(Random::seeded(42));
// Log the best individual every 50 iterations.
state.insert(
tracking::LogSet::<SymmetricTsp>::new()
.with(trigger::Iteration::new(50), functions::best_individual),
);
});

Ok(())
// Save the log to file "aco_berlin52.log".
files::write_log_file("aco_berlin52.log", state.log()).unwrap();
}
18 changes: 8 additions & 10 deletions src/components/constraints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use crate::{
///
/// Types implementing this trait can implement [Component] by wrapping the type in a [BoundaryConstrainer].
pub trait BoundaryConstraint<P: Problem> {
fn constrain(&self, solution: &mut P::Encoding, problem: &P, state: &mut State);
fn constrain(&self, solution: &mut P::Encoding, problem: &P, state: &mut State<P>);
}

#[derive(serde::Serialize, Clone)]
Expand All @@ -24,14 +24,12 @@ where
P: Problem,
T: AnyComponent + BoundaryConstraint<P> + Serialize + Clone,
{
fn execute(&self, problem: &P, state: &mut State) {
let mut population = state.population_stack_mut::<P>().pop();

fn execute(&self, problem: &P, state: &mut State<P>) {
let mut population = state.populations_mut().pop();
for individual in population.iter_mut() {
self.0.constrain(individual.solution_mut(), problem, state);
}

state.population_stack_mut().push(population);
state.populations_mut().push(population);
}
}

Expand All @@ -47,7 +45,7 @@ impl Saturation {
impl<P: Problem<Encoding = Vec<f64>> + VectorProblem<T = f64> + LimitedVectorProblem>
BoundaryConstraint<P> for Saturation
{
fn constrain(&self, solution: &mut Vec<f64>, problem: &P, _state: &mut State) {
fn constrain(&self, solution: &mut Vec<f64>, problem: &P, _state: &mut State<P>) {
for (d, x) in solution.iter_mut().enumerate() {
let range = problem.range(d);
*x = x.clamp(range.start, range.end);
Expand All @@ -68,7 +66,7 @@ impl Toroidal {
impl<P: Problem<Encoding = Vec<f64>> + VectorProblem<T = f64> + LimitedVectorProblem>
BoundaryConstraint<P> for Toroidal
{
fn constrain(&self, solution: &mut Vec<f64>, problem: &P, _state: &mut State) {
fn constrain(&self, solution: &mut Vec<f64>, problem: &P, _state: &mut State<P>) {
for (d, x) in solution.iter_mut().enumerate() {
let range = problem.range(d);
let a = range.start;
Expand Down Expand Up @@ -97,7 +95,7 @@ impl Mirror {
impl<P: Problem<Encoding = Vec<f64>> + VectorProblem<T = f64> + LimitedVectorProblem>
BoundaryConstraint<P> for Mirror
{
fn constrain(&self, solution: &mut Vec<f64>, problem: &P, _state: &mut State) {
fn constrain(&self, solution: &mut Vec<f64>, problem: &P, _state: &mut State<P>) {
for (d, x) in solution.iter_mut().enumerate() {
let range = problem.range(d);
let a = range.start;
Expand Down Expand Up @@ -133,7 +131,7 @@ impl CompleteOneTailedNormalCorrection {
impl<P: Problem<Encoding = Vec<f64>> + VectorProblem<T = f64> + LimitedVectorProblem>
BoundaryConstraint<P> for CompleteOneTailedNormalCorrection
{
fn constrain(&self, solution: &mut Vec<f64>, problem: &P, state: &mut State) {
fn constrain(&self, solution: &mut Vec<f64>, problem: &P, state: &mut State<P>) {
for (d, x) in solution.iter_mut().enumerate() {
let range = problem.range(d);
let a = range.start;
Expand Down
26 changes: 13 additions & 13 deletions src/components/evaluation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use crate::{
///
/// This component should be inserted after every generating component.
///
/// Only the head of the [common::Population] will be evaluated.
/// Only the head of the [common::Populations] will be evaluated.
/// Requires either [common::EvaluatorInstance] to be present
/// in the [State] or [Problem::default_evaluator] to be implemented.
///
Expand All @@ -28,25 +28,25 @@ impl Evaluator {
}

impl<P: Problem> Component<P> for Evaluator {
fn initialize(&self, problem: &P, state: &mut State) {
state.require::<common::Population<P>>();
fn initialize(&self, problem: &P, state: &mut State<P>) {
state.require::<Self, common::Populations<P>>();
state.insert(common::Evaluations(0));

if !state.has::<common::EvaluatorInstance<P>>() {
state.insert(problem.default_evaluator());
}
}

fn execute(&self, problem: &P, state: &mut State) {
if let Some(mut population) = state.population_stack_mut().try_pop() {
fn execute(&self, problem: &P, state: &mut State<P>) {
if let Some(mut population) = state.populations_mut().try_pop() {
state.holding::<common::EvaluatorInstance<P>>(|evaluator_state, state| {
evaluator_state
.evaluator
.evaluate(problem, state, &mut population);
});

*state.get_value_mut::<common::Evaluations>() += population.len() as u32;
state.population_stack_mut().push(population);
state.populations_mut().push(population);
}
}
}
Expand All @@ -65,15 +65,15 @@ impl UpdateBestIndividual {
}

impl<P: SingleObjectiveProblem> Component<P> for UpdateBestIndividual {
fn initialize(&self, _problem: &P, state: &mut State) {
fn initialize(&self, _problem: &P, state: &mut State<P>) {
state.insert(common::BestIndividual::<P>(None));
}

fn execute(&self, _problem: &P, state: &mut State) {
fn execute(&self, _problem: &P, state: &mut State<P>) {
let mut mut_state = state.get_states_mut();

let best_individual = mut_state
.population_stack()
.populations()
.current()
.iter()
.min_by_key(|i| i.objective());
Expand All @@ -100,14 +100,14 @@ impl UpdateParetoFront {
}

impl<P: MultiObjectiveProblem> Component<P> for UpdateParetoFront {
fn initialize(&self, _problem: &P, state: &mut State) {
fn initialize(&self, _problem: &P, state: &mut State<P>) {
state.insert(common::ParetoFront::<P>::new());
}

fn execute(&self, _problem: &P, state: &mut State) {
fn execute(&self, _problem: &P, state: &mut State<P>) {
let mut mut_state = state.get_states_mut();

let front = mut_state.get_mut::<common::ParetoFront<P>>();
front.update_multiple(mut_state.population_stack().current());
let front = mut_state.pareto_front_mut();
front.update_multiple(mut_state.populations().current());
}
}
Loading

0 comments on commit d8d5f98

Please sign in to comment.