|
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