geneticalgorithm2.crossovers
1from typing import Callable, Tuple, Dict 2 3import random 4import numpy as np 5 6from .utils.aliases import TypeAlias, array1D 7 8CrossoverFunc: TypeAlias = Callable[[array1D, array1D], Tuple[array1D, array1D]] 9""" 10Function (parent1, parent2) -> (child1, child2) 11""" 12 13 14def get_copies(x: array1D, y: array1D) -> Tuple[array1D, array1D]: 15 return x.copy(), y.copy() 16 17 18class Crossover: 19 """ 20 Crossover functions static class 21 22 Crossover creates 2 children from 2 parents someway, usually mixing the parents 23 """ 24 25 @staticmethod 26 def crossovers_dict() -> Dict[str, CrossoverFunc]: 27 return { 28 n: getattr(Crossover, n)() 29 for n in ( 30 'one_point', 31 'two_point', 32 'uniform', 33 'segment', 34 'shuffle', 35 ) 36 } 37 38 @staticmethod 39 def one_point() -> CrossoverFunc: 40 41 def func(x: array1D, y: array1D): 42 ofs1, ofs2 = get_copies(x, y) 43 44 ran = np.random.randint(0, x.size) 45 46 ofs1[:ran] = y[:ran] 47 ofs2[:ran] = x[:ran] 48 49 return ofs1, ofs2 50 return func 51 52 @staticmethod 53 def two_point() -> CrossoverFunc: 54 55 def func(x: array1D, y: array1D): 56 ofs1, ofs2 = get_copies(x, y) 57 58 ran1 = np.random.randint(0, x.size) 59 ran2 = np.random.randint(ran1, x.size) 60 61 ofs1[ran1:ran2] = y[ran1:ran2] 62 ofs2[ran1:ran2] = x[ran1:ran2] 63 64 return ofs1, ofs2 65 return func 66 67 @staticmethod 68 def uniform() -> CrossoverFunc: 69 70 def func(x: array1D, y: array1D): 71 ofs1, ofs2 = get_copies(x, y) 72 73 ran = np.random.random(x.size) < 0.5 74 ofs1[ran] = y[ran] 75 ofs2[ran] = x[ran] 76 77 return ofs1, ofs2 78 79 return func 80 81 @staticmethod 82 def segment(prob: int = 0.6) -> CrossoverFunc: 83 84 def func(x: array1D, y: array1D): 85 86 ofs1, ofs2 = get_copies(x, y) 87 88 p = np.random.random(x.size) < prob 89 90 for i, val in enumerate(p): 91 if val: 92 ofs1[i], ofs2[i] = ofs2[i], ofs1[i] 93 94 return ofs1, ofs2 95 96 return func 97 98 @staticmethod 99 def shuffle() -> CrossoverFunc: 100 101 def func(x: array1D, y: array1D): 102 103 ofs1, ofs2 = get_copies(x, y) 104 105 index = np.random.choice(np.arange(0, x.size), x.size, replace=False) 106 107 ran = np.random.randint(0, x.size) 108 109 for i in range(ran): 110 ind = index[i] 111 ofs1[ind] = y[ind] 112 ofs2[ind] = x[ind] 113 114 return ofs1, ofs2 115 116 return func 117 118 @staticmethod 119 def uniform_window(window: int = 7) -> CrossoverFunc: 120 121 base_uniform = Crossover.uniform() 122 123 def func(x: np.ndarray, y: np.ndarray): 124 125 if x.size % window != 0: 126 raise ValueError(f"dimension {x.size} cannot be divided by window {window}") 127 128 items = int(x.size/window) 129 130 zip_x, zip_y = base_uniform(np.zeros(items), np.ones(items)) 131 132 ofs1 = np.empty(x.size) 133 ofs2 = np.empty(x.size) 134 for i in range(items): 135 sls = slice(i*window, (i+1)*window, 1) 136 if zip_x[i] == 0: 137 ofs1[sls] = x[sls] 138 ofs2[sls] = y[sls] 139 else: 140 ofs2[sls] = x[sls] 141 ofs1[sls] = y[sls] 142 143 return ofs1, ofs2 144 145 return func 146 147 # 148 # 149 # ONLY FOR REAL VARIABLES 150 # 151 # 152 153 @staticmethod 154 def arithmetic() -> CrossoverFunc: 155 156 def func(x: array1D, y: array1D): 157 b = random.random() 158 a = 1-b 159 return a*x + b*y, a*y + b*x 160 161 return func 162 163 @staticmethod 164 def mixed(alpha: float = 0.5) -> CrossoverFunc: 165 166 def func(x: array1D, y: array1D): 167 168 a = np.empty(x.size) 169 b = np.empty(y.size) 170 171 x_min = np.minimum(x, y) 172 x_max = np.maximum(x, y) 173 delta = alpha*(x_max-x_min) 174 175 for i in range(x.size): 176 a[i] = np.random.uniform(x_min[i] - delta[i], x_max[i] + delta[i]) 177 b[i] = np.random.uniform(x_min[i] + delta[i], x_max[i] - delta[i]) 178 179 return a, b 180 181 return func 182
CrossoverFunc: typing_extensions.TypeAlias =
typing.Callable[[numpy.ndarray, numpy.ndarray], typing.Tuple[numpy.ndarray, numpy.ndarray]]
Function (parent1, parent2) -> (child1, child2)
def
get_copies( x: numpy.ndarray, y: numpy.ndarray) -> Tuple[numpy.ndarray, numpy.ndarray]:
class
Crossover:
20class Crossover: 21 """ 22 Crossover functions static class 23 24 Crossover creates 2 children from 2 parents someway, usually mixing the parents 25 """ 26 27 @staticmethod 28 def crossovers_dict() -> Dict[str, CrossoverFunc]: 29 return { 30 n: getattr(Crossover, n)() 31 for n in ( 32 'one_point', 33 'two_point', 34 'uniform', 35 'segment', 36 'shuffle', 37 ) 38 } 39 40 @staticmethod 41 def one_point() -> CrossoverFunc: 42 43 def func(x: array1D, y: array1D): 44 ofs1, ofs2 = get_copies(x, y) 45 46 ran = np.random.randint(0, x.size) 47 48 ofs1[:ran] = y[:ran] 49 ofs2[:ran] = x[:ran] 50 51 return ofs1, ofs2 52 return func 53 54 @staticmethod 55 def two_point() -> CrossoverFunc: 56 57 def func(x: array1D, y: array1D): 58 ofs1, ofs2 = get_copies(x, y) 59 60 ran1 = np.random.randint(0, x.size) 61 ran2 = np.random.randint(ran1, x.size) 62 63 ofs1[ran1:ran2] = y[ran1:ran2] 64 ofs2[ran1:ran2] = x[ran1:ran2] 65 66 return ofs1, ofs2 67 return func 68 69 @staticmethod 70 def uniform() -> CrossoverFunc: 71 72 def func(x: array1D, y: array1D): 73 ofs1, ofs2 = get_copies(x, y) 74 75 ran = np.random.random(x.size) < 0.5 76 ofs1[ran] = y[ran] 77 ofs2[ran] = x[ran] 78 79 return ofs1, ofs2 80 81 return func 82 83 @staticmethod 84 def segment(prob: int = 0.6) -> CrossoverFunc: 85 86 def func(x: array1D, y: array1D): 87 88 ofs1, ofs2 = get_copies(x, y) 89 90 p = np.random.random(x.size) < prob 91 92 for i, val in enumerate(p): 93 if val: 94 ofs1[i], ofs2[i] = ofs2[i], ofs1[i] 95 96 return ofs1, ofs2 97 98 return func 99 100 @staticmethod 101 def shuffle() -> CrossoverFunc: 102 103 def func(x: array1D, y: array1D): 104 105 ofs1, ofs2 = get_copies(x, y) 106 107 index = np.random.choice(np.arange(0, x.size), x.size, replace=False) 108 109 ran = np.random.randint(0, x.size) 110 111 for i in range(ran): 112 ind = index[i] 113 ofs1[ind] = y[ind] 114 ofs2[ind] = x[ind] 115 116 return ofs1, ofs2 117 118 return func 119 120 @staticmethod 121 def uniform_window(window: int = 7) -> CrossoverFunc: 122 123 base_uniform = Crossover.uniform() 124 125 def func(x: np.ndarray, y: np.ndarray): 126 127 if x.size % window != 0: 128 raise ValueError(f"dimension {x.size} cannot be divided by window {window}") 129 130 items = int(x.size/window) 131 132 zip_x, zip_y = base_uniform(np.zeros(items), np.ones(items)) 133 134 ofs1 = np.empty(x.size) 135 ofs2 = np.empty(x.size) 136 for i in range(items): 137 sls = slice(i*window, (i+1)*window, 1) 138 if zip_x[i] == 0: 139 ofs1[sls] = x[sls] 140 ofs2[sls] = y[sls] 141 else: 142 ofs2[sls] = x[sls] 143 ofs1[sls] = y[sls] 144 145 return ofs1, ofs2 146 147 return func 148 149 # 150 # 151 # ONLY FOR REAL VARIABLES 152 # 153 # 154 155 @staticmethod 156 def arithmetic() -> CrossoverFunc: 157 158 def func(x: array1D, y: array1D): 159 b = random.random() 160 a = 1-b 161 return a*x + b*y, a*y + b*x 162 163 return func 164 165 @staticmethod 166 def mixed(alpha: float = 0.5) -> CrossoverFunc: 167 168 def func(x: array1D, y: array1D): 169 170 a = np.empty(x.size) 171 b = np.empty(y.size) 172 173 x_min = np.minimum(x, y) 174 x_max = np.maximum(x, y) 175 delta = alpha*(x_max-x_min) 176 177 for i in range(x.size): 178 a[i] = np.random.uniform(x_min[i] - delta[i], x_max[i] + delta[i]) 179 b[i] = np.random.uniform(x_min[i] + delta[i], x_max[i] - delta[i]) 180 181 return a, b 182 183 return func
Crossover functions static class
Crossover creates 2 children from 2 parents someway, usually mixing the parents
@staticmethod
def
crossovers_dict() -> Dict[str, Callable[[numpy.ndarray, numpy.ndarray], Tuple[numpy.ndarray, numpy.ndarray]]]:
@staticmethod
def
one_point() -> Callable[[numpy.ndarray, numpy.ndarray], Tuple[numpy.ndarray, numpy.ndarray]]:
@staticmethod
def
two_point() -> Callable[[numpy.ndarray, numpy.ndarray], Tuple[numpy.ndarray, numpy.ndarray]]:
54 @staticmethod 55 def two_point() -> CrossoverFunc: 56 57 def func(x: array1D, y: array1D): 58 ofs1, ofs2 = get_copies(x, y) 59 60 ran1 = np.random.randint(0, x.size) 61 ran2 = np.random.randint(ran1, x.size) 62 63 ofs1[ran1:ran2] = y[ran1:ran2] 64 ofs2[ran1:ran2] = x[ran1:ran2] 65 66 return ofs1, ofs2 67 return func
@staticmethod
def
uniform() -> Callable[[numpy.ndarray, numpy.ndarray], Tuple[numpy.ndarray, numpy.ndarray]]:
@staticmethod
def
segment( prob: int = 0.6) -> Callable[[numpy.ndarray, numpy.ndarray], Tuple[numpy.ndarray, numpy.ndarray]]:
83 @staticmethod 84 def segment(prob: int = 0.6) -> CrossoverFunc: 85 86 def func(x: array1D, y: array1D): 87 88 ofs1, ofs2 = get_copies(x, y) 89 90 p = np.random.random(x.size) < prob 91 92 for i, val in enumerate(p): 93 if val: 94 ofs1[i], ofs2[i] = ofs2[i], ofs1[i] 95 96 return ofs1, ofs2 97 98 return func
@staticmethod
def
shuffle() -> Callable[[numpy.ndarray, numpy.ndarray], Tuple[numpy.ndarray, numpy.ndarray]]:
100 @staticmethod 101 def shuffle() -> CrossoverFunc: 102 103 def func(x: array1D, y: array1D): 104 105 ofs1, ofs2 = get_copies(x, y) 106 107 index = np.random.choice(np.arange(0, x.size), x.size, replace=False) 108 109 ran = np.random.randint(0, x.size) 110 111 for i in range(ran): 112 ind = index[i] 113 ofs1[ind] = y[ind] 114 ofs2[ind] = x[ind] 115 116 return ofs1, ofs2 117 118 return func
@staticmethod
def
uniform_window( window: int = 7) -> Callable[[numpy.ndarray, numpy.ndarray], Tuple[numpy.ndarray, numpy.ndarray]]:
120 @staticmethod 121 def uniform_window(window: int = 7) -> CrossoverFunc: 122 123 base_uniform = Crossover.uniform() 124 125 def func(x: np.ndarray, y: np.ndarray): 126 127 if x.size % window != 0: 128 raise ValueError(f"dimension {x.size} cannot be divided by window {window}") 129 130 items = int(x.size/window) 131 132 zip_x, zip_y = base_uniform(np.zeros(items), np.ones(items)) 133 134 ofs1 = np.empty(x.size) 135 ofs2 = np.empty(x.size) 136 for i in range(items): 137 sls = slice(i*window, (i+1)*window, 1) 138 if zip_x[i] == 0: 139 ofs1[sls] = x[sls] 140 ofs2[sls] = y[sls] 141 else: 142 ofs2[sls] = x[sls] 143 ofs1[sls] = y[sls] 144 145 return ofs1, ofs2 146 147 return func
@staticmethod
def
arithmetic() -> Callable[[numpy.ndarray, numpy.ndarray], Tuple[numpy.ndarray, numpy.ndarray]]:
@staticmethod
def
mixed( alpha: float = 0.5) -> Callable[[numpy.ndarray, numpy.ndarray], Tuple[numpy.ndarray, numpy.ndarray]]:
165 @staticmethod 166 def mixed(alpha: float = 0.5) -> CrossoverFunc: 167 168 def func(x: array1D, y: array1D): 169 170 a = np.empty(x.size) 171 b = np.empty(y.size) 172 173 x_min = np.minimum(x, y) 174 x_max = np.maximum(x, y) 175 delta = alpha*(x_max-x_min) 176 177 for i in range(x.size): 178 a[i] = np.random.uniform(x_min[i] - delta[i], x_max[i] + delta[i]) 179 b[i] = np.random.uniform(x_min[i] + delta[i], x_max[i] - delta[i]) 180 181 return a, b 182 183 return func