Edit on GitHub

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]:
16def get_copies(x: array1D, y: array1D) -> Tuple[array1D, array1D]:
17    return x.copy(), y.copy()
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]]]:
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        }
@staticmethod
def one_point() -> Callable[[numpy.ndarray, numpy.ndarray], Tuple[numpy.ndarray, numpy.ndarray]]:
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
@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]]:
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
@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]]:
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
@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