|
4 | 4 | then create problem instances and solve them with calls to the various search
|
5 | 5 | functions."""
|
6 | 6 |
|
7 |
| -import bisect |
8 |
| -import math |
9 |
| -import random |
10 |
| -import string |
11 |
| -import sys |
12 |
| -from collections import defaultdict |
13 |
| - |
14 |
| -from fuzzywuzzy import fuzz |
15 |
| - |
16 |
| -from grid import distance |
17 | 7 | from utils import (
|
18 |
| - is_in, argmin, argmax_random_tie, probability, |
19 |
| - memoize, print_table, DataFile, Stack, |
| 8 | + is_in, argmin, argmax, argmax_random_tie, probability, weighted_sampler, |
| 9 | + weighted_sample_with_replacement, memoize, print_table, DataFile, Stack, |
20 | 10 | FIFOQueue, PriorityQueue, name
|
21 | 11 | )
|
| 12 | +from grid import distance |
| 13 | + |
| 14 | +from collections import defaultdict |
| 15 | +import math |
| 16 | +import random |
| 17 | +import sys |
| 18 | +import bisect |
22 | 19 |
|
23 | 20 | infinity = float('inf')
|
24 | 21 |
|
@@ -572,94 +569,74 @@ def LRTA_cost(self, s, a, s1, H):
|
572 | 569 | # Genetic Algorithm
|
573 | 570 |
|
574 | 571 |
|
575 |
| -class GAState: |
576 |
| - def __init__(self, length): |
577 |
| - self.string = ''.join(random.choice(string.ascii_letters) |
578 |
| - for _ in range(length)) |
579 |
| - self.fitness = -1 |
580 |
| - |
581 |
| - |
582 |
| -def ga(in_str=None, population=20, generations=10000): |
583 |
| - in_str_len = len(in_str) |
584 |
| - individuals = init_individual(population, in_str_len) |
585 |
| - |
586 |
| - for generation in range(generations): |
587 |
| - |
588 |
| - individuals = fitness(individuals, in_str) |
589 |
| - individuals = selection(individuals) |
590 |
| - individuals = crossover(individuals, population, in_str_len) |
591 |
| - |
592 |
| - if any(individual.fitness >= 90 for individual in individuals): |
593 |
| - """ |
594 |
| - individuals[0] is the individual with the highest fitness, |
595 |
| - because individuals is sorted in the selection function. |
596 |
| - Thus we return the individual with the highest fitness value, |
597 |
| - among the individuals whose fitness is equal to or greater |
598 |
| - than 90. |
599 |
| - """ |
600 |
| - |
601 |
| - return individuals[0] |
602 |
| - |
603 |
| - individuals = mutation(individuals, in_str_len) |
604 |
| - |
605 |
| - """ |
606 |
| - sufficient number of generations have passed and the individuals |
607 |
| - could not evolve to match the desired fitness value. |
608 |
| - thus we return the fittest individual among the individuals. |
609 |
| - Since individuals are sorted according to their fitness |
610 |
| - individuals[0] is the fittest. |
611 |
| - """ |
612 |
| - return individuals[0] |
613 |
| - |
614 |
| - |
615 |
| -def init_individual(population, length): |
616 |
| - return [GAState(length) for _ in range(population)] |
617 |
| - |
618 |
| - |
619 |
| -def fitness(individuals, in_str): |
620 |
| - for individual in individuals: |
621 |
| - individual.fitness = fuzz.ratio(individual.string, in_str) # noqa |
622 |
| - |
623 |
| - return individuals |
624 |
| - |
625 |
| - |
626 |
| -def selection(individuals): |
627 |
| - individuals = sorted( |
628 |
| - individuals, key=lambda individual: individual.fitness, reverse=True) |
629 |
| - |
630 |
| - individuals = individuals[:int(0.2 * len(individuals))] |
631 |
| - return individuals |
632 |
| - |
633 |
| - |
634 |
| -def crossover(individuals, population, in_str_len): |
635 |
| - offspring = [] |
636 |
| - for _ in range(int((population - len(individuals)) / 2)): |
637 |
| - parent1 = random.choice(individuals) |
638 |
| - parent2 = random.choice(individuals) |
639 |
| - child1 = GAState(in_str_len) |
640 |
| - child2 = GAState(in_str_len) |
641 |
| - split = random.randint(0, in_str_len) |
642 |
| - child1.string = parent1.string[0:split] + parent2.string[ |
643 |
| - split:in_str_len] |
644 |
| - child2.string = parent2.string[0:split] + parent1.string[ |
645 |
| - split:in_str_len] |
646 |
| - offspring.append(child1) |
647 |
| - offspring.append(child2) |
648 |
| - |
649 |
| - individuals.extend(offspring) |
650 |
| - return individuals |
651 |
| - |
652 |
| - |
653 |
| -def mutation(individuals, in_str_len): |
654 |
| - for individual in individuals: |
655 |
| - |
656 |
| - for idx, param in enumerate(individual.string): |
657 |
| - if random.uniform(0.0, 1.0) <= 0.1: |
658 |
| - individual.string = individual.string[0:idx] \ |
659 |
| - + random.choice(string.ascii_letters) \ |
660 |
| - + individual.string[idx + 1:in_str_len] |
661 |
| - |
662 |
| - return individuals |
| 572 | +def genetic_search(problem, fitness_fn, ngen=1000, pmut=0.1, n=20): |
| 573 | + """Call genetic_algorithm on the appropriate parts of a problem. |
| 574 | + This requires the problem to have states that can mate and mutate, |
| 575 | + plus a value method that scores states.""" |
| 576 | + |
| 577 | + # NOTE: This is not tested and might not work. |
| 578 | + # TODO: Use this function to make Problems work with genetic_algorithm. |
| 579 | + |
| 580 | + s = problem.initial_state |
| 581 | + states = [problem.result(s, a) for a in problem.actions(s)] |
| 582 | + random.shuffle(states) |
| 583 | + return genetic_algorithm(states[:n], problem.value, ngen, pmut) |
| 584 | + |
| 585 | + |
| 586 | +def genetic_algorithm(population, fitness_fn, gene_pool=['0', '1'], f_thres=None, ngen=1000, pmut=0.1): |
| 587 | + """[Figure 4.8]""" |
| 588 | + for i in range(ngen): |
| 589 | + new_population = [] |
| 590 | + fitnesses = map(fitness_fn, population) |
| 591 | + random_selection = weighted_sampler(population, fitnesses) |
| 592 | + for j in range(len(population)): |
| 593 | + x = random_selection() |
| 594 | + y = random_selection() |
| 595 | + child = reproduce(x, y) |
| 596 | + if random.uniform(0, 1) < pmut: |
| 597 | + child = mutate(child, gene_pool) |
| 598 | + new_population.append(child) |
| 599 | + |
| 600 | + population = new_population |
| 601 | + |
| 602 | + if f_thres: |
| 603 | + fittest_individual = argmax(population, key=fitness_fn) |
| 604 | + if fitness_fn(fittest_individual) >= f_thres: |
| 605 | + return fittest_individual |
| 606 | + |
| 607 | + return argmax(population, key=fitness_fn) |
| 608 | + |
| 609 | + |
| 610 | +def init_population(pop_number, gene_pool, state_length): |
| 611 | + """Initializes population for genetic algorithm |
| 612 | + pop_number : Number of individuals in population |
| 613 | + gene_pool : List of possible values for individuals |
| 614 | + (char only) |
| 615 | + state_length: The length of each individual""" |
| 616 | + g = len(gene_pool) |
| 617 | + population = [] |
| 618 | + for i in range(pop_number): |
| 619 | + new_individual = ''.join([gene_pool[random.randrange(0, g)] |
| 620 | + for j in range(state_length)]) |
| 621 | + population.append(new_individual) |
| 622 | + |
| 623 | + return population |
| 624 | + |
| 625 | + |
| 626 | +def reproduce(x, y): |
| 627 | + n = len(x) |
| 628 | + c = random.randrange(1, n) |
| 629 | + return x[:c] + y[c:] |
| 630 | + |
| 631 | + |
| 632 | +def mutate(x, gene_pool): |
| 633 | + n = len(x) |
| 634 | + g = len(gene_pool) |
| 635 | + c = random.randrange(0, n) |
| 636 | + r = random.randrange(0, g) |
| 637 | + |
| 638 | + new_gene = gene_pool[r] |
| 639 | + return x[:c] + new_gene + x[c+1:] |
663 | 640 |
|
664 | 641 | # _____________________________________________________________________________
|
665 | 642 | # The remainder of this file implements examples for the search algorithms.
|
|
0 commit comments