geneticalgorithm2.data_types.algorithm_params
1from typing import Dict, Any, List, Optional, Union, Callable, Tuple, Literal 2 3 4from dataclasses import dataclass 5import warnings 6 7from .base import DictLikeGetSet 8from ..utils.funcs import can_be_prob 9 10from ..crossovers import Crossover, CrossoverFunc 11from ..mutations import Mutations, MutationIntFunc, MutationFloatFunc 12from ..selections import Selection, SelectionFunc 13 14 15_algorithm_params_slots = { 16 'max_num_iteration', 17 'max_iteration_without_improv', 18 'population_size', 19 'mutation_probability', 20 'mutation_discrete_probability', 21 'elit_ratio', 22 'crossover_probability', 23 'parents_portion', 24 'crossover_type', 25 'mutation_type', 26 'mutation_discrete_type', 27 'selection_type' 28} 29 30 31@dataclass 32class AlgorithmParams(DictLikeGetSet): 33 """Base optimization parameters container""" 34 35 max_num_iteration: Optional[int] = None 36 """ 37 max iterations count of the algorithm 38 39 If this parameter's value is `None` the algorithm sets maximum number of iterations automatically 40 as a function of the dimension, boundaries, and population size. 41 The user may enter any number of iterations that they want. 42 It is highly recommended that the user themselves determines 43 the `max_num_iterations` and not to use `None` 44 """ 45 46 max_iteration_without_improv: Optional[int] = None 47 """ 48 max iteration without progress 49 50 if the algorithms does not improve 51 the objective function over the number of successive iterations 52 determined by this parameter, 53 then GA stops and report the best found solution 54 before the `max_num_iterations` to be met 55 """ 56 57 population_size: int = 100 58 """ 59 determines the number of trial solutions in each iteration 60 """ 61 62 mutation_probability: float = 0.1 63 mutation_discrete_probability: Optional[float] = None 64 """ 65 works like `mutation_probability` but for discrete variables. 66 67 If `None`, will be assigned to `mutation_probability` value; 68 so just don't specify this parameter 69 if u don't need special mutation behavior for discrete variables 70 """ 71 72 # deprecated 73 crossover_probability: Optional[float] = None 74 75 elit_ratio: float = 0.04 76 """ 77 determines the number of elites in the population. 78 79 For example, when population size is 100 and `elit_ratio` is 0.01 80 then there is 4 elite units in the population. 81 If this parameter is set to be zero then `GeneticAlgorithm2` implements 82 a standard genetic algorithm instead of elitist GA 83 """ 84 parents_portion: float = 0.3 85 """ 86 the portion of population filled by the members of the previous generation (aka parents) 87 """ 88 89 crossover_type: Union[str, CrossoverFunc] = 'uniform' 90 mutation_type: Union[str, MutationFloatFunc] = 'uniform_by_center' 91 """mutation type for real variable""" 92 mutation_discrete_type: Union[str, MutationIntFunc] = 'uniform_discrete' 93 """mutation type for discrete variables""" 94 selection_type: Union[str, SelectionFunc] = 'roulette' 95 96 def validate(self) -> None: 97 98 assert int(self.population_size) > 0, f"population size must be integer and >0, not {self.population_size}" 99 assert (can_be_prob(self.parents_portion)), "parents_portion must be in range [0,1]" 100 assert (can_be_prob(self.mutation_probability)), "mutation_probability must be in range [0,1]" 101 assert (can_be_prob(self.elit_ratio)), "elit_ratio must be in range [0,1]" 102 103 if self.max_iteration_without_improv is not None and self.max_iteration_without_improv < 1: 104 warnings.warn( 105 f"max_iteration_without_improv is {self.max_iteration_without_improv} but must be None or int > 0" 106 ) 107 self.max_iteration_without_improv = None 108 109 def get_CMS_funcs(self) -> Tuple[ 110 CrossoverFunc, 111 MutationFloatFunc, 112 MutationIntFunc, 113 SelectionFunc 114 ]: 115 """ 116 Returns: 117 gotten (crossover, mutation, discrete mutation, selection) as necessary functions 118 """ 119 120 result: List[Callable] = [] 121 for name, value, dct in ( 122 ('crossover', self.crossover_type, Crossover.crossovers_dict()), 123 ('mutation', self.mutation_type, Mutations.mutations_dict()), 124 ('mutation_discrete', self.mutation_discrete_type, Mutations.mutations_discrete_dict()), 125 ('selection', self.selection_type, Selection.selections_dict()) 126 ): 127 if isinstance(value, str): 128 if value not in dct: 129 raise ValueError( 130 f"unknown name of {name}: '{value}', must be from {tuple(dct.keys())} or a custom function" 131 ) 132 result.append(dct[value]) 133 else: 134 assert callable(value), f"{name} must be string or callable" 135 result.append(value) 136 137 return tuple(result) 138 139 def update(self, dct: Dict[str, Any]): 140 for name, value in dct.items(): 141 if name not in _algorithm_params_slots: 142 raise AttributeError( 143 f"name '{name}' does not exists in AlgorithmParams fields: " 144 f"{', '.join(sorted(_algorithm_params_slots))}" 145 ) 146 for name, value in dct.items(): # perform update in separate loop only if all is valid 147 setattr(self, name, value) 148 149 @staticmethod 150 def from_dict(dct: Dict[str, Any]): 151 152 result = AlgorithmParams() 153 result.update(dct) 154 return result
34class AlgorithmParams(DictLikeGetSet): 35 """Base optimization parameters container""" 36 37 max_num_iteration: Optional[int] = None 38 """ 39 max iterations count of the algorithm 40 41 If this parameter's value is `None` the algorithm sets maximum number of iterations automatically 42 as a function of the dimension, boundaries, and population size. 43 The user may enter any number of iterations that they want. 44 It is highly recommended that the user themselves determines 45 the `max_num_iterations` and not to use `None` 46 """ 47 48 max_iteration_without_improv: Optional[int] = None 49 """ 50 max iteration without progress 51 52 if the algorithms does not improve 53 the objective function over the number of successive iterations 54 determined by this parameter, 55 then GA stops and report the best found solution 56 before the `max_num_iterations` to be met 57 """ 58 59 population_size: int = 100 60 """ 61 determines the number of trial solutions in each iteration 62 """ 63 64 mutation_probability: float = 0.1 65 mutation_discrete_probability: Optional[float] = None 66 """ 67 works like `mutation_probability` but for discrete variables. 68 69 If `None`, will be assigned to `mutation_probability` value; 70 so just don't specify this parameter 71 if u don't need special mutation behavior for discrete variables 72 """ 73 74 # deprecated 75 crossover_probability: Optional[float] = None 76 77 elit_ratio: float = 0.04 78 """ 79 determines the number of elites in the population. 80 81 For example, when population size is 100 and `elit_ratio` is 0.01 82 then there is 4 elite units in the population. 83 If this parameter is set to be zero then `GeneticAlgorithm2` implements 84 a standard genetic algorithm instead of elitist GA 85 """ 86 parents_portion: float = 0.3 87 """ 88 the portion of population filled by the members of the previous generation (aka parents) 89 """ 90 91 crossover_type: Union[str, CrossoverFunc] = 'uniform' 92 mutation_type: Union[str, MutationFloatFunc] = 'uniform_by_center' 93 """mutation type for real variable""" 94 mutation_discrete_type: Union[str, MutationIntFunc] = 'uniform_discrete' 95 """mutation type for discrete variables""" 96 selection_type: Union[str, SelectionFunc] = 'roulette' 97 98 def validate(self) -> None: 99 100 assert int(self.population_size) > 0, f"population size must be integer and >0, not {self.population_size}" 101 assert (can_be_prob(self.parents_portion)), "parents_portion must be in range [0,1]" 102 assert (can_be_prob(self.mutation_probability)), "mutation_probability must be in range [0,1]" 103 assert (can_be_prob(self.elit_ratio)), "elit_ratio must be in range [0,1]" 104 105 if self.max_iteration_without_improv is not None and self.max_iteration_without_improv < 1: 106 warnings.warn( 107 f"max_iteration_without_improv is {self.max_iteration_without_improv} but must be None or int > 0" 108 ) 109 self.max_iteration_without_improv = None 110 111 def get_CMS_funcs(self) -> Tuple[ 112 CrossoverFunc, 113 MutationFloatFunc, 114 MutationIntFunc, 115 SelectionFunc 116 ]: 117 """ 118 Returns: 119 gotten (crossover, mutation, discrete mutation, selection) as necessary functions 120 """ 121 122 result: List[Callable] = [] 123 for name, value, dct in ( 124 ('crossover', self.crossover_type, Crossover.crossovers_dict()), 125 ('mutation', self.mutation_type, Mutations.mutations_dict()), 126 ('mutation_discrete', self.mutation_discrete_type, Mutations.mutations_discrete_dict()), 127 ('selection', self.selection_type, Selection.selections_dict()) 128 ): 129 if isinstance(value, str): 130 if value not in dct: 131 raise ValueError( 132 f"unknown name of {name}: '{value}', must be from {tuple(dct.keys())} or a custom function" 133 ) 134 result.append(dct[value]) 135 else: 136 assert callable(value), f"{name} must be string or callable" 137 result.append(value) 138 139 return tuple(result) 140 141 def update(self, dct: Dict[str, Any]): 142 for name, value in dct.items(): 143 if name not in _algorithm_params_slots: 144 raise AttributeError( 145 f"name '{name}' does not exists in AlgorithmParams fields: " 146 f"{', '.join(sorted(_algorithm_params_slots))}" 147 ) 148 for name, value in dct.items(): # perform update in separate loop only if all is valid 149 setattr(self, name, value) 150 151 @staticmethod 152 def from_dict(dct: Dict[str, Any]): 153 154 result = AlgorithmParams() 155 result.update(dct) 156 return result
Base optimization parameters container
max iterations count of the algorithm
If this parameter's value is None
the algorithm sets maximum number of iterations automatically
as a function of the dimension, boundaries, and population size.
The user may enter any number of iterations that they want.
It is highly recommended that the user themselves determines
the max_num_iterations
and not to use None
max iteration without progress
if the algorithms does not improve
the objective function over the number of successive iterations
determined by this parameter,
then GA stops and report the best found solution
before the max_num_iterations
to be met
works like mutation_probability
but for discrete variables.
If None
, will be assigned to mutation_probability
value;
so just don't specify this parameter
if u don't need special mutation behavior for discrete variables
determines the number of elites in the population.
For example, when population size is 100 and elit_ratio
is 0.01
then there is 4 elite units in the population.
If this parameter is set to be zero then GeneticAlgorithm2
implements
a standard genetic algorithm instead of elitist GA
the portion of population filled by the members of the previous generation (aka parents)
mutation type for real variable
mutation type for discrete variables
98 def validate(self) -> None: 99 100 assert int(self.population_size) > 0, f"population size must be integer and >0, not {self.population_size}" 101 assert (can_be_prob(self.parents_portion)), "parents_portion must be in range [0,1]" 102 assert (can_be_prob(self.mutation_probability)), "mutation_probability must be in range [0,1]" 103 assert (can_be_prob(self.elit_ratio)), "elit_ratio must be in range [0,1]" 104 105 if self.max_iteration_without_improv is not None and self.max_iteration_without_improv < 1: 106 warnings.warn( 107 f"max_iteration_without_improv is {self.max_iteration_without_improv} but must be None or int > 0" 108 ) 109 self.max_iteration_without_improv = None
111 def get_CMS_funcs(self) -> Tuple[ 112 CrossoverFunc, 113 MutationFloatFunc, 114 MutationIntFunc, 115 SelectionFunc 116 ]: 117 """ 118 Returns: 119 gotten (crossover, mutation, discrete mutation, selection) as necessary functions 120 """ 121 122 result: List[Callable] = [] 123 for name, value, dct in ( 124 ('crossover', self.crossover_type, Crossover.crossovers_dict()), 125 ('mutation', self.mutation_type, Mutations.mutations_dict()), 126 ('mutation_discrete', self.mutation_discrete_type, Mutations.mutations_discrete_dict()), 127 ('selection', self.selection_type, Selection.selections_dict()) 128 ): 129 if isinstance(value, str): 130 if value not in dct: 131 raise ValueError( 132 f"unknown name of {name}: '{value}', must be from {tuple(dct.keys())} or a custom function" 133 ) 134 result.append(dct[value]) 135 else: 136 assert callable(value), f"{name} must be string or callable" 137 result.append(value) 138 139 return tuple(result)
Returns:
gotten (crossover, mutation, discrete mutation, selection) as necessary functions
141 def update(self, dct: Dict[str, Any]): 142 for name, value in dct.items(): 143 if name not in _algorithm_params_slots: 144 raise AttributeError( 145 f"name '{name}' does not exists in AlgorithmParams fields: " 146 f"{', '.join(sorted(_algorithm_params_slots))}" 147 ) 148 for name, value in dct.items(): # perform update in separate loop only if all is valid 149 setattr(self, name, value)