Skip to content

Commit

Permalink
Added test_with_retries macro to repeat GA tests. Some tests may fail…
Browse files Browse the repository at this point in the history
… due to the randomness in the solutions
  • Loading branch information
s-simoncelli committed Aug 2, 2024
1 parent 3d22e53 commit 4a28c65
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 14 deletions.
10 changes: 10 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[workspace]
resolver = "2"
members = ["optirustic", "hv-fonseca-et-al-2006-sys", "hv-wfg-sys"]
members = ["optirustic", "optirustic-macros", "hv-fonseca-et-al-2006-sys", "hv-wfg-sys"]
default-members = ["optirustic", "hv-fonseca-et-al-2006-sys", "hv-wfg-sys"]

# Run test with optimisation to speed up tests solving optimisation problems.
Expand Down
12 changes: 12 additions & 0 deletions optirustic-macros/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "optirustic-macros"
version = "0.1.0"
edition = "2021"

[lib]
proc-macro = true

[dependencies]
syn = { version = "*", features = ["full"] }
quote = "*"
proc-macro2 = "*"
35 changes: 35 additions & 0 deletions optirustic-macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use proc_macro::TokenStream;

use quote::quote;
use syn::{parse_macro_input, ItemFn};

/// An attribute macro to repeat a test `n` times until the test passes. The test passes if it does
/// not panic once, it fails if it panics `n` times.
#[proc_macro_attribute]
pub fn test_with_retries(attrs: TokenStream, item: TokenStream) -> TokenStream {
let input_fn = parse_macro_input!(item as ItemFn);
let fn_name = &input_fn.sig.ident;
let tries = attrs
.to_string()
.parse::<u8>()
.expect("Attr must be an int");

let expanded = quote! {
#[test]
fn #fn_name() {
#input_fn
for i in 1..=#tries {
let result = std::panic::catch_unwind(|| { #fn_name() });

if result.is_ok() {
return;
}

if i == #tries {
std::panic::resume_unwind(result.unwrap_err());
}
};
}
};
expanded.into()
}
1 change: 1 addition & 0 deletions optirustic/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ rayon = "1.10.0"
env_logger = "0.11.3"
chrono = { version = "0.4.38", features = ["serde"] }
ordered-float = "4.2.0"
optirustic-macros = { path = "../optirustic-macros" }
hv-fonseca-et-al-2006-sys = { path = "../hv-fonseca-et-al-2006-sys" }
hv-wfg-sys = { path = "../hv-wfg-sys" }
plotters = { version = "0.3.6", optional = true }
Expand Down
29 changes: 16 additions & 13 deletions optirustic/src/algorithms/nsga2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ use rand::RngCore;
use serde::{Deserialize, Serialize};

use crate::algorithms::{Algorithm, ExportHistory, StoppingConditionType};
use crate::core::utils::{argsort, get_rng, vector_max, vector_min, Sort};
use crate::core::{
Individual, Individuals, IndividualsMut, OError, Population, Problem, VariableValue,
};
use crate::core::utils::{argsort, get_rng, Sort, vector_max, vector_min};
use crate::operators::{
Crossover, CrowdedComparison, Mutation, PolynomialMutation, PolynomialMutationArgs, Selector,
SimulatedBinaryCrossover, SimulatedBinaryCrossoverArgs, TournamentSelector,
Expand Down Expand Up @@ -47,7 +47,7 @@ pub struct NSGA2Arg {
/// Instead of initialising the population with random variables, see the initial population
/// with the variable values from a JSON files exported with this tool. This option lets you
/// restart the evolution from a previous generation; you can use any history file (exported
/// when the field `export_history`) or the file exported when the stopping condition was reached.
/// when the field `export_history`) or the file exported when the stopping condition was reached.
pub resume_from_file: Option<PathBuf>,
/// The seed used in the random number generator (RNG). You can specify a seed in case you want
/// to try to reproduce results. NSGA2 is a stochastic algorithm that relies on a RNG at
Expand Down Expand Up @@ -441,13 +441,14 @@ impl Algorithm<NSGA2Arg> for NSGA2 {
&self.args
}
}

#[cfg(test)]
mod test_sorting {
use float_cmp::assert_approx_eq;

use crate::algorithms::NSGA2;
use crate::core::{Individuals, ObjectiveDirection, VariableValue};
use crate::core::utils::individuals_from_obj_values_dummy;
use crate::core::{Individuals, ObjectiveDirection, VariableValue};

#[test]
/// Test the crowding distance algorithm (not enough points).
Expand Down Expand Up @@ -669,15 +670,18 @@ mod test_sorting {
}
#[cfg(test)]
mod test_problems {
use crate::algorithms::{Algorithm, MaxGeneration, NSGA2, NSGA2Arg, StoppingConditionType};
use optirustic_macros::test_with_retries;

use crate::algorithms::{Algorithm, MaxGeneration, NSGA2Arg, StoppingConditionType, NSGA2};
use crate::core::builtin_problems::{
SCHProblem, ZTD1Problem, ZTD2Problem, ZTD3Problem, ZTD4Problem,
};
use crate::core::test_utils::{check_exact_value, check_value_in_range};

const BOUND_TOL: f64 = 1.0 / 1000.0;
const LOOSE_BOUND_TOL: f64 = 0.1;
#[test]

#[test_with_retries(3)]
/// Test problem 1 from Deb et al. (2002). Optional solution x in [0; 2]
fn test_sch_problem() {
let problem = SCHProblem::create().unwrap();
Expand All @@ -703,7 +707,7 @@ mod test_problems {
}
}

#[test]
#[test_with_retries(3)]
/// Test the ZTD1 problem from Deb et al. (2002) with 30 variables. Solution x1 in [0; 1] and
/// x2 to x30 = 0. The exact solutions are tested using a strict and loose bounds.
fn test_ztd1_problem() {
Expand Down Expand Up @@ -750,7 +754,7 @@ mod test_problems {
}
}

#[test]
#[test_with_retries(3)]
/// Test the ZTD2 problem from Deb et al. (2002) with 30 variables. Solution x1 in [0; 1] and
/// x2 to x30 = 0. The exact solutions are tested using a strict and loose bounds.
fn test_ztd2_problem() {
Expand Down Expand Up @@ -802,7 +806,7 @@ mod test_problems {
}
}

#[test]
#[test_with_retries(3)]
/// Test the ZTD3 problem from Deb et al. (2002) with 30 variables. Solution x1 in [0; 1] and
/// x2 to x30 = 0. The exact solutions are tested using a strict and loose bounds.
fn test_ztd3_problem() {
Expand Down Expand Up @@ -854,15 +858,13 @@ mod test_problems {
}
}

#[test]
#[test_with_retries(3)]
/// Test the ZTD4 problem from Deb et al. (2002) with 30 variables. Solution x1 in [0; 1] and
/// x2 to x10 = 0. The exact solutions are tested using a strict and loose bounds.
fn test_ztd4_problem() {
let number_of_individuals: usize = 10;
let problem = ZTD4Problem::create(number_of_individuals).unwrap();
let args = NSGA2Arg {
number_of_individuals,
// this may take longer to converge
stopping_condition: StoppingConditionType::MaxGeneration(MaxGeneration(3000)),
crossover_operator_options: None,
mutation_operator_options: None,
Expand All @@ -871,7 +873,8 @@ mod test_problems {
resume_from_file: None,
seed: Some(1),
};
let mut algo = NSGA2::new(problem, args).unwrap();
let problem = ZTD4Problem::create(number_of_individuals).unwrap();
let mut algo = NSGA2::new(problem, args.clone()).unwrap();
algo.run().unwrap();
let results = algo.get_results();

Expand Down Expand Up @@ -908,7 +911,7 @@ mod test_problems {
}
}

#[test]
#[test_with_retries(3)]
/// Test the ZTD6 problem from Deb et al. (2002) with 30 variables. Solution x1 in [0; 1] and
/// x2 to x10 = 0. The exact solutions are tested using a strict and loose bounds.
fn test_ztd6_problem() {
Expand Down

0 comments on commit 4a28c65

Please sign in to comment.