Edit on GitHub

geneticalgorithm2.data_types.generation

  1from typing import Union, Dict, Literal, Tuple, Optional
  2from typing_extensions import TypeAlias
  3
  4import os.path
  5from dataclasses import dataclass
  6
  7import numpy as np
  8
  9from ..utils.aliases import array2D, array1D, PathLike
 10from ..utils.files import mkdir_of_file
 11from ..utils.funcs import union_to_matrix
 12
 13from .base import DictLikeGetSet
 14
 15GenerationConvertible: TypeAlias = Union[
 16    'Generation',
 17    str,
 18    Dict[Literal['population', 'scores'], Union[array2D, array1D]],
 19    array2D,
 20    Tuple[
 21        Optional[array2D],
 22        Optional[array1D]
 23    ]
 24]
 25"""
 26The forms convertible to `Generation` object:
 27    - `Generation` object
 28    - path to saved generation
 29    - dict {'population': pop_matrix, 'scores': scores_vector}
 30    - wide population matrix
 31    - pair (pop_matrix, scores_vector)
 32"""
 33
 34
 35@dataclass
 36class Generation(DictLikeGetSet):
 37    """wrapper on generation object (pair of samples matrix and samples scores vector)"""
 38    variables: Optional[array2D] = None
 39    scores: Optional[array1D] = None
 40
 41    def _check_dims(self) -> None:
 42        if self.variables is not None:
 43            assert len(self.variables.shape) == 2, (
 44                f"'variables' must be matrix with shape (objects, dimensions), not {self.variables.shape}"
 45            )
 46            if self.scores is not None:
 47                assert len(self.scores.shape) == 1, f"'scores' must be 1D-array, not with shape {self.scores.shape}"
 48                assert self.variables.shape[0] == self.scores.size, (
 49                    f"count of objects ({self.variables.shape[0]}) "
 50                    f"must be equal to count of scores ({self.scores.size})"
 51                )
 52
 53    @property
 54    def size(self) -> int:
 55        return self.scores.size
 56
 57    @property
 58    def dim_size(self) -> int:
 59        return self.variables.shape[1]
 60
 61    def as_wide_matrix(self) -> array2D:
 62        # should not be used in main code -- was needed for old versions
 63        return union_to_matrix(self.variables, self.scores)
 64
 65    def save(self, path: PathLike):
 66        mkdir_of_file(path)
 67        np.savez(path, population=self.variables, scores=self.scores)
 68
 69    @staticmethod
 70    def load(path: PathLike):
 71        try:
 72            st = np.load(path)
 73        except Exception as err:
 74            raise Exception(
 75                f"if generation object is a string, "
 76                f"it must be path to npz file with needed content, but raised exception {repr(err)}"
 77            )
 78
 79        assert 'population' in st and 'scores' in st, (
 80            "saved generation object must contain 'population' and 'scores' fields"
 81        )
 82
 83        return Generation(variables=st['population'], scores=st['scores'])
 84
 85    @staticmethod
 86    def from_object(
 87        dim: int,
 88        obj: GenerationConvertible
 89    ):
 90        """class constructor"""
 91
 92        if isinstance(obj, np.ndarray):
 93
 94            assert len(obj.shape) == 2 and (obj.shape[1] == dim or obj.shape[1] == dim + 1), (
 95                f"if start_generation is numpy array, "
 96                f"it must be with shape (samples, dim) or (samples, dim+1), not {obj.shape}"
 97            )
 98
 99            generation = Generation(obj, None) if obj.shape[1] == dim else Generation.from_pop_matrix(obj)
100
101        elif isinstance(obj, tuple):
102
103            assert len(obj) == 2, (
104                f"if start_generation is tuple, "
105                f"it must be tuple with 2 components, not {len(obj)}"
106            )
107
108            variables, scores = obj
109
110            assert (variables is None or scores is None) or (variables.shape[0] == scores.size), (
111                "start_generation object must contain variables and scores components "
112                "which are None or 2D- and 1D-arrays with same shape"
113            )
114
115            generation = Generation(variables=variables, scores=scores)
116
117        elif isinstance(obj, dict):
118            assert (
119                ('variables' in obj and 'scores' in obj) and
120                (
121                    (obj['variables'] is None or obj['scores'] is None) or
122                    (obj['variables'].shape[0] == obj['scores'].size)
123                )
124            ), (
125                "start_generation object must contain 'variables' and 'scores' keys "
126                "which are None or 2D- and 1D-arrays with same shape"
127            )
128
129            generation = Generation(variables=obj['variables'], scores=obj['scores'])
130
131        elif isinstance(obj, Generation):
132            generation = Generation(variables=obj['variables'], scores=obj['scores'])
133        else:
134
135            path = str(obj)
136            if not os.path.exists(path):
137                raise TypeError(
138                    f"invalid type of generation! "
139                    f"Must be in (Union[str, Dict[str, np.ndarray], Generation, np.ndarray, "
140                    f"Tuple[Optional[np.ndarray], Optional[np.ndarray]]]), "
141                    f"not {type(obj)}"
142                )
143            generation = Generation.load(path)
144
145        generation._check_dims()
146
147        if generation.variables is not None:
148            assert generation.dim_size == dim, (
149                f"generation dimension size {generation.dim_size} does not equal to target size {dim}"
150            )
151
152        return generation
153
154    @staticmethod
155    def from_pop_matrix(pop: array2D):
156        import warnings
157        warnings.warn(
158            "depricated! pop matrix style will be removed at version 7, "
159            "use samples and scores separetly"
160        )
161        return Generation(
162            variables=pop[:, :-1],
163            scores=pop[:, -1]
164        )
GenerationConvertible: typing_extensions.TypeAlias = typing.Union[ForwardRef('Generation'), str, typing.Dict[typing.Literal['population', 'scores'], numpy.ndarray], numpy.ndarray, typing.Tuple[typing.Union[numpy.ndarray, NoneType], typing.Union[numpy.ndarray, NoneType]]]

The forms convertible to Generation object: - Generation object - path to saved generation - dict {'population': pop_matrix, 'scores': scores_vector} - wide population matrix - pair (pop_matrix, scores_vector)

class Generation(geneticalgorithm2.data_types.base.DictLikeGetSet):
 38class Generation(DictLikeGetSet):
 39    """wrapper on generation object (pair of samples matrix and samples scores vector)"""
 40    variables: Optional[array2D] = None
 41    scores: Optional[array1D] = None
 42
 43    def _check_dims(self) -> None:
 44        if self.variables is not None:
 45            assert len(self.variables.shape) == 2, (
 46                f"'variables' must be matrix with shape (objects, dimensions), not {self.variables.shape}"
 47            )
 48            if self.scores is not None:
 49                assert len(self.scores.shape) == 1, f"'scores' must be 1D-array, not with shape {self.scores.shape}"
 50                assert self.variables.shape[0] == self.scores.size, (
 51                    f"count of objects ({self.variables.shape[0]}) "
 52                    f"must be equal to count of scores ({self.scores.size})"
 53                )
 54
 55    @property
 56    def size(self) -> int:
 57        return self.scores.size
 58
 59    @property
 60    def dim_size(self) -> int:
 61        return self.variables.shape[1]
 62
 63    def as_wide_matrix(self) -> array2D:
 64        # should not be used in main code -- was needed for old versions
 65        return union_to_matrix(self.variables, self.scores)
 66
 67    def save(self, path: PathLike):
 68        mkdir_of_file(path)
 69        np.savez(path, population=self.variables, scores=self.scores)
 70
 71    @staticmethod
 72    def load(path: PathLike):
 73        try:
 74            st = np.load(path)
 75        except Exception as err:
 76            raise Exception(
 77                f"if generation object is a string, "
 78                f"it must be path to npz file with needed content, but raised exception {repr(err)}"
 79            )
 80
 81        assert 'population' in st and 'scores' in st, (
 82            "saved generation object must contain 'population' and 'scores' fields"
 83        )
 84
 85        return Generation(variables=st['population'], scores=st['scores'])
 86
 87    @staticmethod
 88    def from_object(
 89        dim: int,
 90        obj: GenerationConvertible
 91    ):
 92        """class constructor"""
 93
 94        if isinstance(obj, np.ndarray):
 95
 96            assert len(obj.shape) == 2 and (obj.shape[1] == dim or obj.shape[1] == dim + 1), (
 97                f"if start_generation is numpy array, "
 98                f"it must be with shape (samples, dim) or (samples, dim+1), not {obj.shape}"
 99            )
100
101            generation = Generation(obj, None) if obj.shape[1] == dim else Generation.from_pop_matrix(obj)
102
103        elif isinstance(obj, tuple):
104
105            assert len(obj) == 2, (
106                f"if start_generation is tuple, "
107                f"it must be tuple with 2 components, not {len(obj)}"
108            )
109
110            variables, scores = obj
111
112            assert (variables is None or scores is None) or (variables.shape[0] == scores.size), (
113                "start_generation object must contain variables and scores components "
114                "which are None or 2D- and 1D-arrays with same shape"
115            )
116
117            generation = Generation(variables=variables, scores=scores)
118
119        elif isinstance(obj, dict):
120            assert (
121                ('variables' in obj and 'scores' in obj) and
122                (
123                    (obj['variables'] is None or obj['scores'] is None) or
124                    (obj['variables'].shape[0] == obj['scores'].size)
125                )
126            ), (
127                "start_generation object must contain 'variables' and 'scores' keys "
128                "which are None or 2D- and 1D-arrays with same shape"
129            )
130
131            generation = Generation(variables=obj['variables'], scores=obj['scores'])
132
133        elif isinstance(obj, Generation):
134            generation = Generation(variables=obj['variables'], scores=obj['scores'])
135        else:
136
137            path = str(obj)
138            if not os.path.exists(path):
139                raise TypeError(
140                    f"invalid type of generation! "
141                    f"Must be in (Union[str, Dict[str, np.ndarray], Generation, np.ndarray, "
142                    f"Tuple[Optional[np.ndarray], Optional[np.ndarray]]]), "
143                    f"not {type(obj)}"
144                )
145            generation = Generation.load(path)
146
147        generation._check_dims()
148
149        if generation.variables is not None:
150            assert generation.dim_size == dim, (
151                f"generation dimension size {generation.dim_size} does not equal to target size {dim}"
152            )
153
154        return generation
155
156    @staticmethod
157    def from_pop_matrix(pop: array2D):
158        import warnings
159        warnings.warn(
160            "depricated! pop matrix style will be removed at version 7, "
161            "use samples and scores separetly"
162        )
163        return Generation(
164            variables=pop[:, :-1],
165            scores=pop[:, -1]
166        )

wrapper on generation object (pair of samples matrix and samples scores vector)

Generation( variables: Union[numpy.ndarray, NoneType] = None, scores: Union[numpy.ndarray, NoneType] = None)
variables: Union[numpy.ndarray, NoneType] = None
scores: Union[numpy.ndarray, NoneType] = None
size: int
55    @property
56    def size(self) -> int:
57        return self.scores.size
dim_size: int
59    @property
60    def dim_size(self) -> int:
61        return self.variables.shape[1]
def as_wide_matrix(self) -> numpy.ndarray:
63    def as_wide_matrix(self) -> array2D:
64        # should not be used in main code -- was needed for old versions
65        return union_to_matrix(self.variables, self.scores)
def save(self, path: Union[str, os.PathLike]):
67    def save(self, path: PathLike):
68        mkdir_of_file(path)
69        np.savez(path, population=self.variables, scores=self.scores)
@staticmethod
def load(path: Union[str, os.PathLike]):
71    @staticmethod
72    def load(path: PathLike):
73        try:
74            st = np.load(path)
75        except Exception as err:
76            raise Exception(
77                f"if generation object is a string, "
78                f"it must be path to npz file with needed content, but raised exception {repr(err)}"
79            )
80
81        assert 'population' in st and 'scores' in st, (
82            "saved generation object must contain 'population' and 'scores' fields"
83        )
84
85        return Generation(variables=st['population'], scores=st['scores'])
@staticmethod
def from_object( dim: int, obj: Union[Generation, str, Dict[Literal['population', 'scores'], numpy.ndarray], numpy.ndarray, Tuple[Union[numpy.ndarray, NoneType], Union[numpy.ndarray, NoneType]]]):
 87    @staticmethod
 88    def from_object(
 89        dim: int,
 90        obj: GenerationConvertible
 91    ):
 92        """class constructor"""
 93
 94        if isinstance(obj, np.ndarray):
 95
 96            assert len(obj.shape) == 2 and (obj.shape[1] == dim or obj.shape[1] == dim + 1), (
 97                f"if start_generation is numpy array, "
 98                f"it must be with shape (samples, dim) or (samples, dim+1), not {obj.shape}"
 99            )
100
101            generation = Generation(obj, None) if obj.shape[1] == dim else Generation.from_pop_matrix(obj)
102
103        elif isinstance(obj, tuple):
104
105            assert len(obj) == 2, (
106                f"if start_generation is tuple, "
107                f"it must be tuple with 2 components, not {len(obj)}"
108            )
109
110            variables, scores = obj
111
112            assert (variables is None or scores is None) or (variables.shape[0] == scores.size), (
113                "start_generation object must contain variables and scores components "
114                "which are None or 2D- and 1D-arrays with same shape"
115            )
116
117            generation = Generation(variables=variables, scores=scores)
118
119        elif isinstance(obj, dict):
120            assert (
121                ('variables' in obj and 'scores' in obj) and
122                (
123                    (obj['variables'] is None or obj['scores'] is None) or
124                    (obj['variables'].shape[0] == obj['scores'].size)
125                )
126            ), (
127                "start_generation object must contain 'variables' and 'scores' keys "
128                "which are None or 2D- and 1D-arrays with same shape"
129            )
130
131            generation = Generation(variables=obj['variables'], scores=obj['scores'])
132
133        elif isinstance(obj, Generation):
134            generation = Generation(variables=obj['variables'], scores=obj['scores'])
135        else:
136
137            path = str(obj)
138            if not os.path.exists(path):
139                raise TypeError(
140                    f"invalid type of generation! "
141                    f"Must be in (Union[str, Dict[str, np.ndarray], Generation, np.ndarray, "
142                    f"Tuple[Optional[np.ndarray], Optional[np.ndarray]]]), "
143                    f"not {type(obj)}"
144                )
145            generation = Generation.load(path)
146
147        generation._check_dims()
148
149        if generation.variables is not None:
150            assert generation.dim_size == dim, (
151                f"generation dimension size {generation.dim_size} does not equal to target size {dim}"
152            )
153
154        return generation

class constructor

@staticmethod
def from_pop_matrix(pop: numpy.ndarray):
156    @staticmethod
157    def from_pop_matrix(pop: array2D):
158        import warnings
159        warnings.warn(
160            "depricated! pop matrix style will be removed at version 7, "
161            "use samples and scores separetly"
162        )
163        return Generation(
164            variables=pop[:, :-1],
165            scores=pop[:, -1]
166        )