diff --git a/algorithms/genetic_algorithm.py b/algorithms/genetic_algorithm.py index eca66bce88f396e0692464d913bc8a337ab9cc08..97f27bf6085d6d0630cbbceceeeb24227fcbec56 100644 --- a/algorithms/genetic_algorithm.py +++ b/algorithms/genetic_algorithm.py @@ -1,28 +1,97 @@ +import cmath +import random +from copy import deepcopy +from math import cos, degrees, inf, log10, pi, radians, sin + +from utils.pattern import compute_pattern + +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)] + self.fitness = float("nan") + + @staticmethod + def new_gene(bit_count): + return random.randrange(0, 2 ** bit_count) + + class GeneticAlgorithm(BaseAlgorithm): - def __init__(self, options): """ Finds nulls by running a genetic algorithm on all possible discrete values. """ - BaseAlgorithm.__init__(self, options): - self.null_degrees = options.null_degrees - self.main_ang = options.main_ang - self.check_parameters() - - def check_parameters(self): - super().check_parameters() - - def solve(self): - # Initiate Chromosomes - # Rank Chromosomes + def __init__(self, options): + BaseAlgorithm.__init__(self, options) + self.main_ang = options.main_ang + self.sample_size = options.sample_size + self.null_degrees = options.null_degrees + self.gen_to_repeat = options.gen_to_repeat + self.bit_count = options.bit_count + self.bit_resolution = options.bit_resolution + self.mutation_factor = options.mutation_factor + + self.check_parameters() + self.chromosomes = [ + Chromosome(self.N, self.bit_count) for i in range(self.sample_size) + ] + self.update_fitness() + self.sort_fitness() + + def check_parameters(self): + super().check_parameters() + + def solve(self): + for generation in range(self.gen_to_repeat): + for ii in range(self.sample_size // 2, self.sample_size, 2): + p1, p2 = random.sample(range(self.sample_size // 2), 2) + self.crossover(p1, p2, ii, ii + 1) + self.mutate_sample() + self.update_fitness() + self.sort_fitness() - # Select Chromosomes + # print(["{:.2f}".format(x.fitness) for x in self.chromosomes[:15]]) + return [ + cmath.exp(1j * self.get_angle(bits)) for bits in self.chromosomes[0].gene + ] - # Cross-over + 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) - # Mutation (keeping the best chromosome) + def update_fitness(self, use_exact_angle=True): + for chromosome in self.chromosomes: + values = [ + -20 * log10(abs(x)) + for x in compute_pattern( + N=self.N, + k=self.k, + weights=[ + cmath.exp(1j * self.get_angle(bits)) for bits in chromosome.gene + ], + degrees=self.null_degrees, + ) + ] + chromosome.fitness = min(values) - # Loop back + def sort_fitness(self): + self.chromosomes.sort(key=lambda x: x.fitness, reverse=True) - # keep + def get_angle(self, bits): + return ( + (bits - (2 ** self.bit_count - 1) / 2) * 2 * pi / (2 ** self.bit_resolution) + ) + def crossover(self, p1, p2, c1, c2): + self.chromosomes[c1] = deepcopy(self.chromosomes[p1]) + self.chromosomes[c2] = deepcopy(self.chromosomes[p2]) + for i in range(self.N): + if random.random() >= 0.5: + self.chromosomes[c1].gene[i], self.chromosomes[c2].gene[i] = ( + self.chromosomes[c1].gene[i], + self.chromosomes[c2].gene[i], + )