From 7d99bf7623c33a98f641be989d16c93793b1beed Mon Sep 17 00:00:00 2001
From: Winnus <winnus@posteo.de>
Date: Fri, 22 Sep 2023 15:52:59 +0200
Subject: [PATCH] Added processing time output to each epoch

---
 README.md                                     |  7 +++
 src/PyGMA.py                                  | 46 ++++++-------------
 src/components/config_func_op.py              |  0
 .../config_logical_gate_construction.py       | 14 +++---
 ...e_construction_no_explicit_input_coding.py |  0
 src/components/evolutionary_phase.py          |  8 ++++
 src/components/genetic_operators.py           |  1 -
 src/config-Copy1.py                           |  0
 src/core/controller.py                        | 15 ++++--
 src/core/mpi_worker.py                        |  3 +-
 10 files changed, 50 insertions(+), 44 deletions(-)
 mode change 100644 => 100755 src/components/config_func_op.py
 mode change 100644 => 100755 src/components/config_logical_gate_construction.py
 mode change 100644 => 100755 src/components/config_logical_gate_construction_no_explicit_input_coding.py
 mode change 100644 => 100755 src/config-Copy1.py

diff --git a/README.md b/README.md
index 5137f13..cc018a3 100755
--- a/README.md
+++ b/README.md
@@ -79,3 +79,10 @@ By conductiong the experiment the a fitness value is generated for the gene.
 This should be returned.
 
 
+
+
+# Todos
+- implement a call to the experiment that handles how to save the fittest gene when evolution is finished.
+    - this has to be user defined and will save the gene into a users usable format for further use
+
+
diff --git a/src/PyGMA.py b/src/PyGMA.py
index 3096eb2..bf0e970 100755
--- a/src/PyGMA.py
+++ b/src/PyGMA.py
@@ -2,16 +2,6 @@ import numpy as np
 from config import CONFIG, check_config
 from core.controller import Controller
 
-
-# remove here
-def bool2int(x):
-    r = 0
-    for i, b in enumerate(x):
-        if b:
-            r += b << i
-    return r
-
-
 def mpi_worker(config):
     # instantiate the worker
     from core.mpi_worker import MPI_worker
@@ -28,16 +18,18 @@ def controller():
     """
     # Init controller
     GAController = Controller(CONFIG, rng_seed=9)
-    print("Init controller")
-    print(GAController.populations)
-    print(GAController.populations[0].genetic_operator_stack)
-    print(GAController.populations[0].individuals[0].genome)
+
+    # print information
+    print("Init Evolution controller:")
+    print(f'populations: {len(GAController.populations)}')
+    for i, pop in zip(range(len(GAController.populations)), GAController.populations):
+        print(f'Genetic Operators p{i}: {len(pop.genetic_operator_stack)}')
 
     # evolution for all phases
     GAController.evolve()
-    print("Evolve finished")
+    print("Evolve finished (fitness or epoch criterion met)")
 
-    print("First best genes")
+ 
     # todo find the population with the best gene
     # get max fitness values
     # this is started inside the controller in get_fittest_individuals()
@@ -46,24 +38,14 @@ def controller():
     m = max(fit_per_pop)
     i = np.where(fit_per_pop == m)
 
-    print(GAController.populations[0].individuals[0].fitness)
-    print(GAController.populations[0].individuals[1].fitness)
+    print(f'Population fitness:{fit_per_pop}')
 
-    print(GAController.populations[0].individuals[0].genome)
-    gene = GAController.populations[0].individuals[0].genome
-    numbers = np.split(gene, 3)
-    # convert the numbers to ints
-    x = bool2int(numbers[0])
-    y = bool2int(numbers[1])
-    z = bool2int(numbers[2])
 
-    print(f"{x} , {y}, {z}")
-    print(x * 2 + y * 3 + z)
+#     print(GAController.populations[0].individuals[0].fitness)
+#     print(GAController.populations[0].individuals[1].fitness)
 
-    # epoch loop
-    # GAController.epoch()
-    # print('Epoch')
-    # print(GAController.populations[0].individuals[0].genome)
+#     print(GAController.populations[0].individuals[0].genome)
+#     gene = GAController.populations[0].individuals[0].genome
 
     return
 
@@ -89,7 +71,7 @@ def main():
             rank = comm.Get_rank()
 
             # if mpi worker switch to worker mode
-            if rank != 0:
+            if rank > 0:
                 mpi_worker(config)
 
             # rank 0 is the controller
diff --git a/src/components/config_func_op.py b/src/components/config_func_op.py
old mode 100644
new mode 100755
diff --git a/src/components/config_logical_gate_construction.py b/src/components/config_logical_gate_construction.py
old mode 100644
new mode 100755
index 9f9988b..d0d46c4
--- a/src/components/config_logical_gate_construction.py
+++ b/src/components/config_logical_gate_construction.py
@@ -64,9 +64,9 @@ def n_bit_multiplication_inout(n):
 # Populations
 # Genetic Phases
 
-OP_MUTATION = Mutation_operator(0.05, 10)
-OP_CROSSOVER = Single_point_crossover_operator(10)
-OP_REMOVAL = Removal_operator(20)
+OP_MUTATION = Mutation_operator(0.05, 45)
+OP_CROSSOVER = Single_point_crossover_operator(45)
+OP_REMOVAL = Removal_operator(90)
 
 
 
@@ -90,7 +90,7 @@ exp_inputs, exp_expected_outputs = n_bit_multiplication_inout(num_bits)
 
 num_logical_inputs = num_bits+num_bits # we have two numbers each having n bits
 num_logical_outputs = num_bits+num_bits# we need to save the result
-num_logical_gates = 96 # how many logic gates we want to use
+num_logical_gates = 128 # how many logic gates we want to use
 
 PHASE0_EXPERIMENT = Logical_circuit_experiment(exp_inputs, exp_expected_outputs, num_logical_inputs, num_logical_outputs, num_logical_gates)
 
@@ -121,7 +121,7 @@ CONFIG_CIRCUIT_EVOLVE = {
     # mpiexec -n 3 python PyGMA.py
     # or use threads if not specified via slurm
     # mpiexec -n 3 --use-hwthread-cpus python PyGMA.py
-    'use_mpi': False,
+    'use_mpi': True,
     
     # use mpi4py.futures
     # this will dynamically spawn workers.
@@ -149,7 +149,7 @@ CONFIG_CIRCUIT_EVOLVE = {
         # pop 0
         [OP_REMOVAL, OP_CROSSOVER, OP_CROSSOVER],
         # pop 1
-        [OP_REMOVAL, OP_CROSSOVER, OP_MUTATION],
+        [OP_REMOVAL, OP_MUTATION, OP_MUTATION],
         # pop 2
         [OP_REMOVAL, OP_CROSSOVER, OP_MUTATION]
     ],
@@ -158,7 +158,7 @@ CONFIG_CIRCUIT_EVOLVE = {
     # 'num_populations': 3,
 
     # how many individuals per population
-    'num_individuals': 24,
+    'num_individuals': 94,
 
     # start genome lenght for each individual
     'genome_length': PHASE0_EXPERIMENT.needed_gene_length()[0],
diff --git a/src/components/config_logical_gate_construction_no_explicit_input_coding.py b/src/components/config_logical_gate_construction_no_explicit_input_coding.py
old mode 100644
new mode 100755
diff --git a/src/components/evolutionary_phase.py b/src/components/evolutionary_phase.py
index 693fb93..020196f 100755
--- a/src/components/evolutionary_phase.py
+++ b/src/components/evolutionary_phase.py
@@ -23,6 +23,14 @@ class Evolutionary_phase:
     def completed(self, population_fitness: list, epochs: int) -> bool:
         """
          Called to check if the phase has reached its end. 
+         You have to return true or false depending on if the pase is finished or not.
+         For example:
+         max_fitness = max(population_fitness)
+         if max_fitness > 90.9:
+            return True
+         if epochs > 900:
+            return True
+         return False
         """
         raise NotImplementedError
 
diff --git a/src/components/genetic_operators.py b/src/components/genetic_operators.py
index 44581bd..58a6498 100755
--- a/src/components/genetic_operators.py
+++ b/src/components/genetic_operators.py
@@ -54,7 +54,6 @@ class Set_zeros(Genetic_operator):
         for gene in old_population:
             gene[self.indexes] = 0
             new_population.append(gene)
-        print(len(new_population))
         return new_population
 
 class Mutation_all_operator(Genetic_operator):
diff --git a/src/config-Copy1.py b/src/config-Copy1.py
old mode 100644
new mode 100755
diff --git a/src/core/controller.py b/src/core/controller.py
index eb4b74e..5b98dec 100755
--- a/src/core/controller.py
+++ b/src/core/controller.py
@@ -1,6 +1,7 @@
 from core.population import Population
 from concurrent.futures import ProcessPoolExecutor
 import numpy as np
+import time as time
 # FIXME:
 # implement correct print functions in the classes
 # such that print(object) can be called
@@ -304,7 +305,7 @@ class Controller:
             # ----------
             # init phase
             # ----------
-            print(f'Evolutionary phase {evolutionary_phase}')
+            print(f'Evolutionary phase: {evolutionary_phase}')
             # reset epochs
             self.epochs = 0
 
@@ -328,13 +329,17 @@ class Controller:
                 # apply genetic operator stack on each population
                 # NOTE: after applying the stack the fitness values
                 # will be zero again for all individuals
+                t_op_stack = time.time()
                 self.__apply_operator_stack()
+                t_op_stack = time.time() - t_op_stack
 
                 # instantiate individuals (get them a fitness value)
                 # tell them the phase index becaus the worker processes
                 # (mpi and local) will have their instance of the
                 # phase list and with it know the experiments.
+                t_evaluate = time.time()
                 self.__instantiate_individuals(phase_index=p_index)
+                t_evaluate = time.time() - t_evaluate
 
                 # check if algorithm stagnates
                 # IMP
@@ -346,8 +351,12 @@ class Controller:
                 # epoch done
                 # ----------
                 # information
-                print(
-                    f'epoch {self.epochs}, Pop max fitness {self.get_population_fitness()}')
+                print(f'epoch {self.epochs}')
+                print('--------------')
+                print(f't_op({t_op_stack})')
+                print(f't_eval({t_evaluate})')
+                print(f'Pop max fitness {self.get_population_fitness()}')
+                print()
 
                 # increment counter
                 self.epochs += 1
diff --git a/src/core/mpi_worker.py b/src/core/mpi_worker.py
index ba3b5a2..3c453da 100755
--- a/src/core/mpi_worker.py
+++ b/src/core/mpi_worker.py
@@ -9,12 +9,13 @@ class MPI_worker():
         self.comm = MPI.COMM_WORLD
         self.rank = self.comm.Get_rank()
         self.sleep_time = sleep_time
+        print(f'initialized MPI worker with rank {self.rank}')
 
     def start(self):
         # worker
         # loop until stop is signalled
         while True:
-            # print(f'worker {rank} loop')
+            print(f'worker {self.rank} loop')
             # signaling that we are waiting for new work
             self.comm.isend(None, dest=0, tag=mpi_tags.IDLE)
 
-- 
GitLab