Edit on GitHub

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
class AlgorithmParams(geneticalgorithm2.data_types.base.DictLikeGetSet):
 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

AlgorithmParams( max_num_iteration: Union[int, NoneType] = None, max_iteration_without_improv: Union[int, NoneType] = None, population_size: int = 100, mutation_probability: float = 0.1, mutation_discrete_probability: Union[float, NoneType] = None, crossover_probability: Union[float, NoneType] = None, elit_ratio: float = 0.04, parents_portion: float = 0.3, crossover_type: Union[str, Callable[[numpy.ndarray, numpy.ndarray], Tuple[numpy.ndarray, numpy.ndarray]]] = 'uniform', mutation_type: Union[str, Callable[[float, float, float], float]] = 'uniform_by_center', mutation_discrete_type: Union[str, Callable[[int, int, int], int]] = 'uniform_discrete', selection_type: Union[str, Callable[[numpy.ndarray, int], numpy.ndarray]] = 'roulette')
max_num_iteration: Union[int, NoneType] = None

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_improv: Union[int, NoneType] = 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

population_size: int = 100

determines the number of trial solutions in each iteration

mutation_probability: float = 0.1
mutation_discrete_probability: Union[float, NoneType] = None

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

crossover_probability: Union[float, NoneType] = None
elit_ratio: float = 0.04

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

parents_portion: float = 0.3

the portion of population filled by the members of the previous generation (aka parents)

crossover_type: Union[str, Callable[[numpy.ndarray, numpy.ndarray], Tuple[numpy.ndarray, numpy.ndarray]]] = 'uniform'
mutation_type: Union[str, Callable[[float, float, float], float]] = 'uniform_by_center'

mutation type for real variable

mutation_discrete_type: Union[str, Callable[[int, int, int], int]] = 'uniform_discrete'

mutation type for discrete variables

selection_type: Union[str, Callable[[numpy.ndarray, int], numpy.ndarray]] = 'roulette'
def validate(self) -> None:
 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
def get_CMS_funcs( self) -> Tuple[Callable[[numpy.ndarray, numpy.ndarray], Tuple[numpy.ndarray, numpy.ndarray]], Callable[[float, float, float], float], Callable[[int, int, int], int], Callable[[numpy.ndarray, int], numpy.ndarray]]:
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

def update(self, dct: Dict[str, Any]):
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)
@staticmethod
def from_dict(dct: Dict[str, Any]):
151    @staticmethod
152    def from_dict(dct: Dict[str, Any]):
153
154        result = AlgorithmParams()
155        result.update(dct)
156        return result