From c9a1f6cbda44b97738ced26181db34f70115bcc6 Mon Sep 17 00:00:00 2001
From: Sepehr Madani <ssepehrmadani@gmail.com>
Date: Sun, 26 Jul 2020 11:39:14 -0400
Subject: [PATCH] Implemented AM-GM crossover

---
 algorithms/genetic_bucket_algorithm.py | 55 +++++++++++++++-----------
 1 file changed, 32 insertions(+), 23 deletions(-)

diff --git a/algorithms/genetic_bucket_algorithm.py b/algorithms/genetic_bucket_algorithm.py
index 87bc1e0..63e0558 100644
--- a/algorithms/genetic_bucket_algorithm.py
+++ b/algorithms/genetic_bucket_algorithm.py
@@ -18,7 +18,7 @@ class Chromosome:
         return random.randrange(0, 2 ** bit_count)
 
 
-class GeneticAlgorithm(BaseAlgorithm):
+class GeneticBucketAlgorithm(BaseAlgorithm):
     """ Finds nulls by running a genetic algorithm on all possible 
     discrete values.
     """
@@ -32,11 +32,13 @@ class GeneticAlgorithm(BaseAlgorithm):
         self.bit_count = options.bit_count
         self.bit_resolution = options.bit_resolution
         self.mutation_factor = options.mutation_factor
+        self.buckets = [[]] * 8
 
         self.check_parameters()
 
     def check_parameters(self):
         super().check_parameters()
+        assert len(self.null_degrees) == 1
 
     def solve(self):
         self.intialize_sample()
@@ -44,13 +46,15 @@ class GeneticAlgorithm(BaseAlgorithm):
         self.sort_fitness()
         for generation in range(self.gen_to_repeat):
             for ii in range(self.sample_size // 2, self.sample_size - 1, 2):
-                p1, p2 = random.sample(range(self.sample_size // 2), 2)
+                bucket_idx = random.randrange(4)
+                p1 = random.choice(self.buckets[bucket_idx])
+                p2 = random.choice(self.buckets[bucket_idx + 4])
+
                 self.crossover(p1, p2, ii, ii + 1)
-            self.mutate_sample()
+            # self.mutate_sample()
             self.update_fitness()
             self.sort_fitness()
 
-            # print(["{:.2f}".format(x.fitness) for x in self.chromosomes[:15]])
         return self.make_weights(self.chromosomes[0])
 
     def mutate_sample(self):
@@ -61,36 +65,41 @@ class GeneticAlgorithm(BaseAlgorithm):
 
     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=self.make_weights(chromosome),
-                    degrees=self.null_degrees,
+            chromosome.fitness = -20 * log10(
+                abs(
+                    compute_pattern(
+                        N=self.N,
+                        k=self.k,
+                        weights=self.make_weights(chromosome),
+                        degrees=self.null_degrees,
+                        use_absolute_value=False,
+                    )[0]
                 )
-            ]
-            chromosome.fitness = min(values)
+            )
+            bucket_idx = int(((cmath.phase(chromosome.fitness) + pi) / (2 * pi)) * 8) % 8
+            self.buckets[bucket_idx].append(chromosome)
 
     def sort_fitness(self):
-        self.chromosomes.sort(key=lambda x: x.fitness, reverse=True)
+        self.chromosomes.sort(key=lambda x: abs(x.fitness), reverse=True)
 
     def make_weights(self, chromosome):
         weights = []
         for bits in chromosome.gene:
-            angle = (bits - (2 ** self.bit_count - 1) / 2) * 2 * pi / (2 ** self.bit_resolution)
+            angle = (
+                (bits - (2 ** self.bit_count - 1) / 2)
+                * 2
+                * pi
+                / (2 ** self.bit_resolution)
+            )
             weights.append(cmath.exp(1j * angle))
         return weights
 
     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],
-                )
+        for ii in range(self.N):
+            g1 = p1.gene[ii]
+            g2 = p2.gene[ii]
+            self.chromosomes[c1].gene[ii] = (g1 + g2) // 2
+            self.chromosomes[c2].gene[ii] = (g1 + g2 + 1) // 2
 
     def intialize_sample(self):
         self.chromosomes = [
-- 
GitLab