-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathPopulation.py
130 lines (102 loc) · 4.17 KB
/
Population.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
import Gene as gn
import Schedule as scdule
import random
import numpy as np
class Population:
def __init__(self, events) -> None:
self.events = events
self.genes = []
self.calculate_fitness()
def set_genes(self,genes):
self.genes = genes
return self
def generate(self, size):
for i in range(0,size):
self.genes.append(gn.Gene(scdule.generate_schedule(self.events)))
return self
def best_fitness(self):
return self.genes[0].fitness
def calculate_fitness(self):
for gene in self.genes:
gene.calculate_fitness()
def sort(self):
self.genes.sort(key=lambda x: x.fitness, reverse=False)
def elitism(self, elitism_factor):
elitism_abs = int(len(self.genes)*elitism_factor+1)
elite = Population(self.events).set_genes(self.genes[:elitism_abs])
rest = Population(self.events).set_genes(self.genes[elitism_abs:])
return elite, rest
def wheel_selection(self, elite_population):
worst_fitness = self.genes[len(self.genes)-1].fitness
population = elite_population.genes + self.genes
fitness_wheel = []
wheel_total = 0
for gene in population:
gen_val = worst_fitness - gene.fitness
wheel_total += gen_val
fitness_wheel.append(gen_val)
p1_raw = random.randint(0, wheel_total)
p2_raw = random.randint(0, wheel_total)
p1, p2 = (0, 0)
while p1_raw > 0:
p1_raw -= fitness_wheel[p1]
p1 += 1
while p2_raw > 0:
p2_raw -= fitness_wheel[p2]
p2 += 1
return p1, p2
def mate(self, elite_population, selection_type):
for gene in self.genes:
p1, p2 = None, None
# Fully Random
if selection_type == 0:
p1, p2 = np.random.choice(elite_population.genes + self.genes, 2)
# Elitistic Random
elif selection_type == 1:
p1, p2 = np.random.choice(elite_population.genes, 2)
# Wheel Selection
elif selection_type == 2:
i1, i2 = self.wheel_selection(elite_population)
total_pop = elite_population.genes + self.genes
p1 = total_pop[i1]
p2 = total_pop[i2]
# Elitistic Exponential
elif selection_type == 3:
i1 = self.generate_idx_exp(len(elite_population.genes + self.genes))
i2 = self.generate_idx_exp(len(elite_population.genes + self.genes))
total_pop = elite_population.genes + self.genes
p1 = total_pop[i1]
p2 = total_pop[i2]
# crossover - by events: take some instances from one parent and some from the other
p1.schedule.instances.sort(key=lambda x: x.title, reverse=True)
p2.schedule.instances.sort(key=lambda x: x.title, reverse=True)
instances = list()
for i in range(0, len(self.events)):
if random.randint(0, 100) > 50:
instances.append(p1.schedule.instances[i])
else:
instances.append(p2.schedule.instances[i])
gene.set_schedule(scdule.Schedule(instances))
@staticmethod
def generate_idx_exp(gen_range):
prob = random.random()
max_value = 0
for i in range(1, gen_range + 1):
max_value += (1 / 2) ** i
if prob <= max_value:
return i - 1
return Population.generate_idx_exp(gen_range)
def purge(self, size):
for i in range(0,size):
self.genes.pop(0)
self.generate(size)
def mutation(self, mutation_rate):
for gene in self.genes:
if random.randint(0, 100) < (mutation_rate * 100):
gene.mutate(self.events)
@classmethod
def combine (cls, elite, common):
population = Population(elite.events)
population.set_genes(elite.genes)
population.genes += common.genes
return population