Updates the population by selecting the top fittest candidates, and recombining them into a new generation.
(self, top=0.7, crossover=0.5, mutation=0.1, d=0.9)
| 2460 | return None or candidate |
| 2461 | |
| 2462 | def update(self, top=0.7, crossover=0.5, mutation=0.1, d=0.9): |
| 2463 | """ Updates the population by selecting the top fittest candidates, |
| 2464 | and recombining them into a new generation. |
| 2465 | """ |
| 2466 | # 1) Selection. |
| 2467 | p = sorted((self.fitness(x), x) for x in self.population) # Weakest-first. |
| 2468 | a = self._avg = float(sum(f for f, x in p)) / len(p) |
| 2469 | x = min(f for f, x in p) |
| 2470 | y = max(f for f, x in p) |
| 2471 | i = 0 |
| 2472 | while len(p) > len(self.population) * top: |
| 2473 | # Weaker candidates have a higher chance of being removed, |
| 2474 | # chance being equal to (1-fitness), starting with the weakest. |
| 2475 | if x + (y - x) * random() >= p[i][0]: |
| 2476 | p.pop(i) |
| 2477 | else: |
| 2478 | i = (i + 1) % len(p) |
| 2479 | # 2) Reproduction. |
| 2480 | g = [] |
| 2481 | while len(g) < len(self.population): |
| 2482 | # Choose randomly between recombination of parents or mutation. |
| 2483 | # Mutation avoids local optima by maintaining genetic diversity. |
| 2484 | if random() < d: |
| 2485 | i = int(round(random() * (len(p)-1))) |
| 2486 | j = choice(range(0, i) + range(i + 1, len(p))) |
| 2487 | g.append(self.crossover(p[i][1], p[j][1], d=crossover)) |
| 2488 | else: |
| 2489 | g.append(self.mutate(choice(p)[1], d=mutation)) |
| 2490 | self.population = g |
| 2491 | self.generation += 1 |
| 2492 | |
| 2493 | @property |
| 2494 | def avg(self): |