diff --git a/algorithms/genetic_bucket_algorithm.py b/algorithms/genetic_bucket_algorithm.py index 121df27096cf57d464dbac6c286357d9ff98cb3a..98c7df7d07f900b1b407c6a07b1ec88c7d75ed75 100644 --- a/algorithms/genetic_bucket_algorithm.py +++ b/algorithms/genetic_bucket_algorithm.py @@ -1,7 +1,7 @@ -import cmath -import random -from copy import deepcopy -from math import cos, degrees, inf, log10, pi, radians, sin +from random import randrange, random, choice +from cmath import exp, phase +from math import log10, pi +from typing import List from utils.pattern import compute_pattern @@ -9,13 +9,17 @@ from .base_algorithm import BaseAlgorithm class Chromosome: - def __init__(self, N, bit_count): - self.gene = [Chromosome.new_gene(bit_count) for i in range(N)] + def __init__(self, n, bit_count): + self.gene = [Chromosome.new_gene(bit_count) for _ in range(n)] self.fitness = float("nan") + self.needs_update = True + + def get_score(self): + return -20 * log10(abs(self.fitness)) @staticmethod def new_gene(bit_count): - return random.randrange(0, 2 ** bit_count) + return randrange(0, 2 ** bit_count) class GeneticBucketAlgorithm(BaseAlgorithm): @@ -23,8 +27,10 @@ class GeneticBucketAlgorithm(BaseAlgorithm): discrete values. """ + chromosomes: List[Chromosome] + def __init__(self, options): - BaseAlgorithm.__init__(self, options) + super().__init__(self, options) self.main_ang = options.main_ang self.sample_size = options.sample_size self.null_degrees = options.null_degrees @@ -32,6 +38,7 @@ class GeneticBucketAlgorithm(BaseAlgorithm): self.bit_count = options.bit_count self.bit_resolution = options.bit_resolution self.mutation_factor = options.mutation_factor + self.chromosomes = [] self.buckets = [[]] * 8 self.check_parameters() @@ -41,43 +48,58 @@ class GeneticBucketAlgorithm(BaseAlgorithm): assert len(self.null_degrees) == 1 def solve(self): - self.intialize_sample() - self.update_fitness() - self.sort_fitness() - for generation in range(self.gen_to_repeat): - for ii in range(self.sample_size // 2, self.sample_size - 1, 2): - bucket_idx = random.randrange(4) - p1 = random.choice(self.buckets[bucket_idx]) - p2 = random.choice(self.buckets[bucket_idx + 4]) + self.initialize_sample() + self.organize_sample() - self.crossover(p1, p2, ii, ii + 1) - # self.mutate_sample() - self.update_fitness() - self.sort_fitness() - - return self.make_weights(self.chromosomes[0]) - - def mutate_sample(self): - for chromosome in self.chromosomes[1:]: # for all except the best chromosome - for idx in range(self.N): - if random.random() <= self.mutation_factor: - chromosome.gene[idx] = Chromosome.new_gene(self.bit_count) + for generation in range(self.gen_to_repeat): + self.create_children() + self.mutate_sample() + self.organize_sample() + + return (self.make_weights(self.chromosomes[0]), self.chromosomes[0].get_score()) + + def create_children(self): + for ii in range(self.sample_size // 2, self.sample_size - 1, 2): + bucket_idx = randrange(8) + p1 = choice(self.buckets[bucket_idx]) + p2 = min( + self.buckets[7 - bucket_idx], key=lambda x: abs(x.fitness + p1.fitness) + ) + self.crossover(p1, p2, ii, ii + 1) - def update_fitness(self, use_exact_angle=True): + def organize_sample(self): + # Update fitness + for chromosome in self.chromosomes: + if chromosome.needs_update: + chromosome.fitness = compute_pattern( + N=self.N, + k=self.k, + weights=self.make_weights(chromosome), + degrees=self.null_degrees, + use_absolute_value=False, + )[0] + chromosome.needs_update = False + + # Sort sample by fitness + self.chromosomes.sort(key=lambda x: x.get_score(), reverse=True) + + # Allocate chromosomes to their respective buckets self.buckets = [[]] * 8 for chromosome in self.chromosomes: - chromosome.fitness = compute_pattern( - N=self.N, - k=self.k, - weights=self.make_weights(chromosome), - degrees=self.null_degrees, - use_absolute_value=False, - )[0] - bucket_idx = int(((cmath.phase(chromosome.fitness) + pi) / (2 * pi)) * 8) % 8 + bucket_idx = int(((phase(chromosome.fitness) + pi) / (2 * pi)) * 8) % 8 self.buckets[bucket_idx].append(chromosome) - def sort_fitness(self): - self.chromosomes.sort(key=lambda x: -20 * log10(abs(x.fitness)), reverse=True) + def mutate_sample(self): + # For all except the best chromosome + for original in range(1, self.sample_size): + mutated = original + self.sample_size - 1 + self.chromosomes[mutated].needs_update = True + + for ii in range(self.N): + if random() <= self.mutation_factor: + self.chromosomes[mutated].gene[ii] = Chromosome.new_gene(self.bit_count) + else: + self.chromosomes[mutated].gene[ii] = self.chromosomes[original].gene[ii] def make_weights(self, chromosome): weights = [] @@ -88,7 +110,7 @@ class GeneticBucketAlgorithm(BaseAlgorithm): * pi / (2 ** self.bit_resolution) ) - weights.append(cmath.exp(1j * angle)) + weights.append(exp(1j * angle)) return weights def crossover(self, p1, p2, c1, c2): @@ -98,7 +120,7 @@ class GeneticBucketAlgorithm(BaseAlgorithm): self.chromosomes[c1].gene[ii] = (g1 + g2) // 2 self.chromosomes[c2].gene[ii] = (g1 + g2 + 1) // 2 - def intialize_sample(self): + def initialize_sample(self): self.chromosomes = [ - Chromosome(self.N, self.bit_count) for i in range(self.sample_size) + Chromosome(self.N, self.bit_count) for _ in range(self.sample_size * 2 - 1) ]