Skip to content

Commit

Permalink
Merge pull request Wandalen#1116 from YuliaProkopovych/opt-params-cac…
Browse files Browse the repository at this point in the history
…hing

NOT READY: Caching optimal parameters search results
  • Loading branch information
Wandalen authored Feb 20, 2024
2 parents ded5f90 + 7cbfd96 commit fa5f638
Show file tree
Hide file tree
Showing 15 changed files with 454 additions and 633 deletions.
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -416,3 +416,6 @@ version = "~0.2.0"
path = "module/test/c"
default-features = true

[patch.crates-io]
pathfinder_geometry = { git = "https://github.com/servo/pathfinder.git" }
pathfinder_simd = { git = "https://github.com/servo/pathfinder.git" }
2 changes: 2 additions & 0 deletions module/move/optimization_tools/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ piston_window = { version = "0.120.0", optional = true }
exmex = { version = "0.18.0", features = [ "partial" ], optional = true }
rayon = "1.8.0"
thiserror = "1.0.56"
rkyv = { version = "0.7.44", features = [ "validation" ] }
ordered-float = "4.2.0"

[dev-dependencies]
test_tools = { workspace = true }
Original file line number Diff line number Diff line change
Expand Up @@ -108,17 +108,24 @@ pub trait InitialProblem
}

/// Indicates state of population proportions with no percentage for elites selection set.
#[ derive( Debug ) ]
pub struct NoElites;
/// Indicates state of population proportions with no percentage for mutating population set.
#[ derive( Debug ) ]
pub struct NoMutations;
/// Indicates state of population proportions with no percentage for crossover set.
#[ derive( Debug ) ]
pub struct NoCrossover;

/// Proportion of population modifications with crossover, mutations and elites cloning.
#[ derive( Debug ) ]
pub struct PopulationModificationProportions< E, M, C >
{
/// Percent of most fit individuals cloned to next population.
elite_selection_rate : E,
/// Percent of individuals mutated in new population.
mutation_rate : M,
/// Percent of individuals in new population created by crossover of two selected parents.
crossover_rate : C,
}

Expand Down
9 changes: 8 additions & 1 deletion module/move/optimization_tools/src/hybrid_optimizer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ pub enum Reason
DynastiesLimit,
}

/// Configuration for Hybrid Optimizer.
#[ derive( Debug ) ]
pub struct Config
{
Expand Down Expand Up @@ -92,6 +93,7 @@ impl Default for Config
}
}

/// Specific optimization problem for Hybrid Optimizer.
#[ derive( Debug ) ]
pub struct Problem< S : InitialProblem, C, M >
{
Expand All @@ -113,6 +115,7 @@ pub struct Problem< S : InitialProblem, C, M >

impl< S : InitialProblem, C, M > Problem< S, C, M >
{
/// Create new instance of optimization problem for Hybrid Optimizer.
pub fn new( initial : S, crossover_operator : C, mutation_operator : M ) -> Self
where TournamentSelection : SelectionOperator< < S as InitialProblem >::Person >
{
Expand Down Expand Up @@ -142,9 +145,10 @@ impl< S : InitialProblem, C, M > Problem< S, C, M >
#[ derive( Debug ) ]
pub struct HybridOptimizer< S : InitialProblem, C, M >
{

/// Configuration of Hybrid Optimizer.
config : Config,

/// Specific optimization problem.
problem : Problem< S, C, M >,
}

Expand Down Expand Up @@ -488,6 +492,7 @@ where M : MutationOperator::< Person = < S as InitialProblem >::Person > + Sync,

}

/// Starting parameters for optimal parameters search for hybrid optimization configuration.
pub fn starting_params_for_hybrid() -> Result< OptimalProblem< RangeInclusive< f64 > >, optimal_params_search::Error >
{
let opt_problem = OptimalProblem::new()
Expand All @@ -503,6 +508,7 @@ pub fn starting_params_for_hybrid() -> Result< OptimalProblem< RangeInclusive< f
Ok( opt_problem )
}

/// Starting parameters for optimal parameters search for SA optimization configuration.
pub fn starting_params_for_sa() -> Result< OptimalProblem< RangeInclusive< f64 > >, optimal_params_search::Error >
{
let opt_problem = OptimalProblem::new()
Expand All @@ -518,6 +524,7 @@ pub fn starting_params_for_sa() -> Result< OptimalProblem< RangeInclusive< f64 >
Ok( opt_problem )
}

/// Starting parameters for optimal parameters search for GA optimization configuration.
pub fn starting_params_for_ga() -> Result< OptimalProblem< RangeInclusive< f64 > >, optimal_params_search::Error >
{
let opt_problem = OptimalProblem::new()
Expand Down
108 changes: 68 additions & 40 deletions module/move/optimization_tools/src/optimal_params_search/mod.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
//! Funcions for calculation optimal config parameters.
//!

pub mod results_serialize;
pub mod nelder_mead;
pub mod sim_annealing;
use std::ops::RangeBounds;
use iter_tools::Itertools;

use crate::hybrid_optimizer::*;

use self::results_serialize::read_results;

/// Level of difficulty of sudoku board.
#[ derive( Debug, Clone, Copy, PartialEq, Eq, Hash ) ]
pub enum Level
Expand All @@ -31,12 +33,18 @@ impl Level {
}
}

///
#[ derive( Debug, Clone ) ]
pub struct OptimalParamsConfig
{
improvement_threshold : f64,
max_no_improvement_steps : usize,
max_iterations : usize,
/// Minimal value detected as improvement in objective function result.
pub improvement_threshold : f64,

/// Max amount of steps performed without detected improvement, termination condition.
pub max_no_improvement_steps : usize,

/// Limit of total iterations of optimization process, termination condition.
pub max_iterations : usize,
}

impl Default for OptimalParamsConfig
Expand All @@ -47,22 +55,31 @@ impl Default for OptimalParamsConfig
{
improvement_threshold : 0.005,
max_no_improvement_steps : 10,
max_iterations : 100,
max_iterations : 10,
}
}
}

/// Problem for optimal parameters search using Nelder-Mead algorithm.
#[ derive( Debug, Clone ) ]
pub struct OptimalProblem< R : RangeBounds< f64 > >
{
/// Containes names of parameters if provided.
pub params_names : Vec< Option< String > >,

/// Contains bounds for parameters, may be unbounded or bounded on one side.
pub bounds : Vec< Option< R > >,

/// Starting point coordinates for optimization process.
pub starting_point : Vec< Option< f64 > >,

/// Size of initial simplex for optimization.
pub simplex_size : Vec< Option< f64 > >,
}

impl< 'a, R : RangeBounds< f64 > > OptimalProblem< R >
{
/// Create new instance for optimization problem
pub fn new() -> Self
{
Self
Expand All @@ -74,6 +91,7 @@ impl< 'a, R : RangeBounds< f64 > > OptimalProblem< R >
}
}

/// Add parameter to optimal parameters search problem.
pub fn add
(
mut self,
Expand Down Expand Up @@ -129,24 +147,24 @@ where R : RangeBounds< f64 > + Sync,
let ga_crossover_operator = hybrid_problem.ga_crossover_operator.clone();
let mutation_operator = hybrid_problem.mutation_operator.clone();

let objective_function = | case : nelder_mead::Point |
let objective_function = | case : &nelder_mead::Point |
{
log::info!
(
"temp_decrease_coefficient : {:.4?}, max_mutations_per_dynasty: {}, mutation_rate: {:.2}, crossover_rate: {:.2};",
case.coords[ 0 ], case.coords[ 1 ] as usize, case.coords[ 2 ], case.coords[ 3 ]
case.coords[ 0 ], case.coords[ 1 ].into_inner() as usize, case.coords[ 2 ], case.coords[ 3 ]
);

log::info!
(
"max_stale_iterations : {:?}, population_size: {}, dynasties_limit: {};",
case.coords[ 4 ] as usize, case.coords[ 5 ] as usize, case.coords[ 6 ] as usize
case.coords[ 4 ].into_inner() as usize, case.coords[ 5 ].into_inner() as usize, case.coords[ 6 ].into_inner() as usize
);

let temp_schedule = LinearTempSchedule
{
constant : 0.0.into(),
coefficient : case.coords[ 0 ].into(),
coefficient : case.coords[ 0 ].into_inner().into(),
reset_increase_value : 1.0.into(),
};

Expand All @@ -160,16 +178,16 @@ where R : RangeBounds< f64 > + Sync,
};

let props = crate::hybrid_optimizer::PopulationModificationProportions::new()
.set_crossover_rate( case.coords[ 3 ] )
.set_mutation_rate( case.coords[ 2 ] )
.set_crossover_rate( case.coords[ 3 ].into_inner() )
.set_mutation_rate( case.coords[ 2 ].into_inner() )
;

let optimizer = HybridOptimizer::new( Config::default(), h_problem )
.set_sa_max_mutations_per_dynasty( case.coords[ 1 ] as usize )
.set_sa_max_mutations_per_dynasty( case.coords[ 1 ].into_inner() as usize )
.set_population_proportions( props )
.set_max_stale_iterations( case.coords[ 4 ] as usize )
.set_population_size( case.coords[ 5 ] as usize )
.set_dynasties_limit( case.coords[ 6 ] as usize )
.set_max_stale_iterations( case.coords[ 4 ].into_inner() as usize )
.set_population_size( case.coords[ 5 ].into_inner() as usize )
.set_dynasties_limit( case.coords[ 6 ].into_inner() as usize )
;
let ( _reason, _solution ) = optimizer.optimize();
};
Expand All @@ -181,10 +199,11 @@ where R : RangeBounds< f64 > + Sync,
res
}

/// Wrapper for optimizing objective function by execution time instead of value.
pub fn optimize_by_time< F, R >( config : OptimalParamsConfig, problem : OptimalProblem< R >, objective_function : F ) -> Result< nelder_mead::Solution, nelder_mead::Error >
where F : Fn( nelder_mead::Point ) + Sync, R : RangeBounds< f64 > + Sync
where F : Fn( &nelder_mead::Point ) + Sync, R : RangeBounds< f64 > + Sync
{
let objective_function = | case : nelder_mead::Point |
let objective_function = | case : &nelder_mead::Point |
{

let now = std::time::Instant::now();
Expand All @@ -199,39 +218,48 @@ where F : Fn( nelder_mead::Point ) + Sync, R : RangeBounds< f64 > + Sync
elapsed.as_secs_f64()
};

let mut bounds = Vec::new();
for bound in problem.bounds
{
if let Some( bound ) = bound
{
bounds.push( bound );
}
}
// let mut bounds = Vec::new();
// for bound in problem.bounds
// {
// if let Some( bound ) = bound
// {
// bounds.push( bound );
// }
// }

let mut optimizer = sim_annealing::Optimizer
{
bounds : bounds,
objective_function : objective_function,
max_iterations : 50,
};
// let mut optimizer = nelder_mead::Optimizer::new( objective_function );
// optimizer.bounds = problem.bounds;
// optimizer.set_starting_point( problem.starting_point.clone() );
// optimizer.set_simplex_size( problem.simplex_size );
// let optimizer = sim_annealing::Optimizer
// {
// bounds : bounds,
// objective_function : objective_function,
// max_iterations : 50,
// };
let mut optimizer = nelder_mead::Optimizer::new( objective_function );
optimizer.bounds = problem.bounds;
optimizer.set_starting_point( problem.starting_point.clone() );
optimizer.set_simplex_size( problem.simplex_size );

// optimizer.improvement_threshold = config.improvement_threshold;
// optimizer.max_iterations = config.max_iterations;
// optimizer.max_no_improvement_steps = config.max_no_improvement_steps;
optimizer.improvement_threshold = config.improvement_threshold;
optimizer.max_iterations = config.max_iterations;
optimizer.max_no_improvement_steps = config.max_no_improvement_steps;

optimizer.optimize()
let calculated_points = read_results();
if let Ok( calculated_points ) = calculated_points
{
optimizer.set_calculated_results( calculated_points );
}

optimizer.optimize_from_random_points()
}

/// Possible error when building NMOptimizer.
#[ derive( thiserror::Error, Debug ) ]
pub enum Error {
pub enum Error
{
/// Error for parameters with duplicate names.
#[ error( "parameter with similar name exists" ) ]
NameError,

/// Error for value located out of its bounds.
#[ error( "starting value is out of bounds" ) ]
OutOfBoundsError,
}
Loading

0 comments on commit fa5f638

Please sign in to comment.