#[doc(hidden)]
#[macro_export]
macro_rules! build_dataframe_explore {
(
$name:ident,
input {$($input:ident: $input_ty:ty)*},
vec {$($input_vec:ident: [$input_ty_vec:ty; $input_len:expr])*}
$($derive: tt)*
) => {
#[derive(Clone, Debug, $($derive,)*)]
struct $name {
$(pub $input: $input_ty,)*
$(pub $input_vec: [$input_ty_vec; $input_len],)*
}
impl DataFrame for $name{
fn field_names() -> &'static [&'static str] {
static NAMES: &'static [&'static str]
= &[$(stringify!($input),)* $(stringify!($input_vec),)*];
NAMES
}
fn to_string(&self) -> Vec<String> {
let mut v: Vec<String> = Vec::new();
$(
v.push(format!("{:?}", self.$input));
)*
$(
v.push(format!("{:?}", self.$input_vec));
)*
v
}
}
impl $name {
pub fn new(
$($input: $input_ty,)* $($input_vec: [$input_ty_vec; $input_len],)*
) -> $name{
$name {
$(
$input,
)*
$(
$input_vec,
)*
}
}
}
};
(
$name:ident,
input {$($input:ident: $input_ty:ty)*}
$($derive: tt)*
) => {
build_dataframe_explore!(
$name,
input {$($input: $input_ty)*},
vec { }
$($derive)*
);
};
(
$name:ident,
vec {$($input_vec:ident: [$input_ty_vec:ty; $input_len:expr])*}
$($derive: tt)*
) => {
build_dataframe_explore!(
$name,
input { },
vec {$($input_vec: [$input_ty_vec; $input_len])*}
$($derive)*
);
};
}
#[macro_export]
macro_rules! evolutionary_search {
(
$init_population:tt,
$fitness:tt,
$selection:tt,
$mutation:tt,
$crossover:tt,
$cmp:tt,
$state: ty,
$desired_fitness: expr,
$generation_num: expr,
$step: expr,
$($reps: expr,)?
ComputingMode::$computing_mode: tt
) => {{
use $crate::cfg_if::cfg_if;
use $crate::engine::schedule::Schedule;
use $crate::engine::state::State;
use $crate::ComputingMode;
let cp_mode = ComputingMode::$computing_mode;
match cp_mode {
ComputingMode::Parallel => {
cfg_if!{
if #[cfg(not(any(feature = "distributed_mpi", feature = "cloud")))] {
println!("Parallel exploration");
explore_ga_parallel!(
$init_population,
$fitness,
$selection,
$mutation,
$crossover,
$cmp,
$state,
$desired_fitness,
$generation_num,
$step,
$($reps,)?
)
} else {
panic!("Parallel computing mode doesn't require distributed or cloud features");
}
}
},
ComputingMode::Distributed => {
cfg_if!{
if #[cfg(feature = "distributed_mpi")] {
explore_ga_distributed_mpi!(
$init_population,
$fitness,
$selection,
$mutation,
$crossover,
$cmp,
$state,
$desired_fitness,
$generation_num,
$step,
$($reps,)?
)
} else {
panic!("Distributed computing mode requires distributed_mpi feature");
}
}
},
ComputingMode::Cloud => {
cfg_if!{
if #[cfg(feature="aws")] {
println!("Cloud GA exploration with AWS");
println!("WARNING: this mode is not yet implemented");
}
else {
panic!("Cloud computing mode is not available. Please enable the feature 'aws' to use this mode.");
}
}
},
}
}};
(
$init_population:tt,
$fitness:tt,
$selection:tt,
$mutation:tt,
$crossover:tt,
$cmp:tt,
$state: ty,
$desired_fitness: expr,
$generation_num: expr,
$step: expr,
$($reps: expr)?
) => {{
use $crate::engine::schedule::Schedule;
use $crate::engine::state::State;
explore_ga_sequential!(
$init_population,
$fitness,
$selection,
$mutation,
$crossover,
$cmp,
$state,
$desired_fitness,
$generation_num,
$step,
$($reps,)?
)
}}
}
#[macro_export]
macro_rules! explore_ga_sequential {
(
$init_population:tt,
$fitness:tt,
$selection:tt,
$mutation:tt,
$crossover:tt,
$cmp:tt,
$state: ty,
$desired_fitness: expr,
$generation_num: expr,
$step: expr,
$($reps: expr,)?
) => {{
println!("Running sequential GA exploration...");
let start = Instant::now();
build_dataframe_explore!(BufferGA, input {
generation: u32
index: i32
fitness: f32
individual: String
});
let mut reps = 1;
$(reps = $reps;)?
let mut generation = 0;
let mut best_fitness: Option<f32> = None;
let mut best_generation = 0;
let mut result: Vec<BufferGA> = Vec::new();
let mut population: Vec<String> = $init_population();
let mut pop_fitness: Vec<(String, f32)> = Vec::new();
let mut flag = false;
let mut best_individual: String = String::new();
loop {
if $generation_num != 0 && generation == $generation_num {
println!("Reached {} generations, exiting...", $generation_num);
break;
}
generation += 1;
println!("Computing generation {}...", generation);
let mut best_fitness_gen: Option<f32> = None;
let mut best_individual_gen: String = String::new();
let mut index = 0;
for individual in population.iter_mut() {
let mut computed_ind: Vec<($state, Schedule)> = Vec::new();
for r in 0..(reps as usize){
let mut individual_state = <$state>::new_with_parameters(r, individual);
let mut schedule: Schedule = Schedule::new();
individual_state.init(&mut schedule);
for _ in 0..($step as usize) {
let individual_state = individual_state.as_state_mut();
schedule.step(individual_state);
if individual_state.end_condition(&mut schedule) {
break;
}
}
computed_ind.push((individual_state, schedule));
}
let fitness = $fitness(&mut computed_ind);
pop_fitness.push((individual.clone(), fitness));
match best_fitness_gen{
Some(_) =>
if $cmp(&fitness, &best_fitness_gen.expect("Error reading best fitness gen")) {
best_fitness_gen = Some(fitness);
best_individual_gen = individual.clone();
},
None => {
best_fitness_gen = Some(fitness);
best_individual_gen = individual.clone();
}
}
result.push(BufferGA::new(
generation,
index,
fitness,
individual.clone()
));
if $cmp(&fitness, &$desired_fitness) {
println!("Found individual with desired fitness! Exiting...");
flag = true;
break;
}
index += 1;
}
match best_fitness{
Some(_) =>
if $cmp(&best_fitness_gen.expect("Error reading best fitness gen"), &best_fitness.expect("Error reading best fitness")) {
best_fitness = best_fitness_gen;
best_individual = best_individual_gen.clone();
best_generation = generation;
},
None => {
best_fitness = best_fitness_gen;
best_individual = best_individual_gen.clone();
best_generation = generation;
}
}
let elapsed_time = start.elapsed();
println!("*** Completed generation {} after {} seconds ***", generation, elapsed_time.as_secs_f32());
println!("- Best fitness in generation {} is {:#?} using {:#?}", generation, best_fitness_gen.expect("Error reading best fitness gen"), best_individual_gen);
println!("-- Overall best fitness is found in generation {} and is {:#?} using {:#?}", best_generation, best_fitness.expect("Error reading best fitness"), best_individual);
if flag {
break;
}
$selection(&mut pop_fitness);
if pop_fitness.len() <= 1 {
println!("Population size <= 1, exiting...");
break;
}
population.clear();
for (individual, _) in pop_fitness.iter_mut() {
population.push(individual.clone());
}
pop_fitness.clear();
$crossover(&mut population);
for i in 0..population.len() {
$mutation(&mut population[i]);
}
}
println!("Resulting best fitness is {}", best_fitness.expect("Error reading best fitness"));
println!("- The best individual is: \n\t{}", best_individual);
result
}};
}
#[macro_export]
macro_rules! explore_ga_parallel {
(
$init_population:tt,
$fitness:tt,
$selection:tt,
$mutation:tt,
$crossover:tt,
$cmp: tt,
$state: ty,
$desired_fitness: expr,
$generation_num: expr,
$step: expr,
$($reps: expr,)?
) => {{
println!("Running parallel GA exploration...");
let start = Instant::now();
build_dataframe_explore!(BufferGA, input {
generation: u32
index: i32
fitness: f32
individual: String
state: String
});
let mut reps = 1;
$( reps = $reps;)?
let mut generation = 0;
let mut best_fitness: Option<f32> = None;
let mut best_generation = 0;
let mut population: Vec<String> = $init_population();
let mut pop_fitness: Vec<(String, f32)> = Vec::new();
let mut best_individual: String = String::new();
let mut flag = false;
let population = Arc::new(Mutex::new(population));
let mut res: Vec<BufferGA> = Vec::new();
loop {
if $generation_num != 0 && generation == $generation_num {
println!("Reached {} generations, exiting...", $generation_num);
break;
}
generation += 1;
println!("Computing generation {}...", generation);
let mut best_fitness_gen: Option<f32> = None;
let mut best_individual_gen: String = String::new();
let mut len = population.lock().expect("Error in population lock acquisition").len();
let mut result = Vec::new();
(0..len).into_par_iter().map( |index| {
let mut computed_ind: Vec<($state, Schedule)> = Vec::new();
let mut save_state: String = String::new();
for r in 0..(reps as usize){
let mut schedule: Schedule = Schedule::new();
let mut individual: $state;
{
let mut population = population.lock().expect("Error in population lock acquisition");
individual = <$state>::new_with_parameters(r, &population[index]);
}
individual.init(&mut schedule);
for _ in 0..($step as usize) {
let individual = individual.as_state_mut();
schedule.step(individual);
if individual.end_condition(&mut schedule) {
break;
}
}
save_state = format!("{}", individual);
computed_ind.push((individual, schedule));
}
let fitness = $fitness(&mut computed_ind);
let mut population = population.lock().expect("Error in population lock acquisition");
BufferGA::new(
generation,
index as i32,
fitness,
population[index].clone(),
save_state
)
}).collect_into_vec(&mut result);
for i in 0..result.len() {
let fitness = result[i].fitness;
let individual = result[i].individual.clone();
pop_fitness.push((individual.clone(), fitness));
match best_fitness_gen {
Some(_) =>
if $cmp(&fitness, &best_fitness_gen.expect("Error reading best fitness gen")) {
best_fitness_gen = Some(fitness);
best_individual_gen = individual.clone();
},
None => {
best_fitness_gen = Some(fitness);
best_individual_gen = individual.clone();
}
}
if $cmp(&fitness, &$desired_fitness) {
println!("Found individual with desired fitness! Exiting...");
flag = true;
}
}
match best_fitness {
Some(_) =>
if $cmp(&best_fitness_gen.expect("Error reading best fitness gen"), &best_fitness.expect("Error reading best fitness")) {
best_fitness = best_fitness_gen.clone();
best_individual = best_individual_gen.clone();
best_generation = generation;
},
None => {
best_fitness = best_fitness_gen.clone();
best_individual = best_individual_gen.clone();
best_generation = generation;
}
}
let elapsed_time = start.elapsed();
println!("*** Completed generation {} after {} seconds ***", generation, elapsed_time.as_secs_f32());
println!("- Best fitness in generation {} is {:#?} using {:#?}", generation, best_fitness_gen.expect("Error reading best fitness gen"), best_individual_gen);
println!("-- Overall best fitness is found in generation {} and is {:#?} using {:#?}", best_generation, best_fitness.expect("Error reading best fitness"), best_individual);
res.append(&mut result);
if flag {
break;
}
$selection(&mut pop_fitness);
if pop_fitness.len() < 1 {
println!("Population size < 1, exiting...");
break;
}
{
let mut population = population.lock().expect("Error in population lock acquisition");
population.clear();
for (individual, _) in pop_fitness.iter_mut() {
population.push(individual.clone())
}
pop_fitness.clear();
$crossover(&mut population);
for i in 0..population.len() {
$mutation(&mut population[i]);
}
}
}
println!("Resulting best fitness is {:#?}", best_fitness.expect("Error reading best fitness"));
println!("- The best individual is:\n\t{}", best_individual);
res
}};
}