diff --git a/py/ga_optimized_random_generator.py b/py/ga_optimized_random_generator.py index 5c28c26..c66d42d 100644 --- a/py/ga_optimized_random_generator.py +++ b/py/ga_optimized_random_generator.py @@ -5,7 +5,7 @@ class GAOptimizedRandomGenerator: """Generate even random sequences according to a predefined TL ration (Ralph, 2014)""" - def __init__(self, choices, trials=64, tl=1, pool_size=100, n=2): + def __init__(self, choices, trials=64, tl=1, pool_size=100, n=3): """ Initialize the genetic algorithm optimizer for n-back sequences. :param choices: @@ -26,13 +26,12 @@ """ generation = 0 best_parent = self.__find_best_parents(1)[0] - while self.fitness(best_parent) > 0.1 and generation < 100000: + while self.cost(best_parent) > 0.1 and generation < 10000: generation += 1 self.pool = self.mutate() self.pool = self.crossover_all() best_parent = self.__find_best_parents(1)[0] - print('s', self.__calc_dist_cost(best_parent, self.trials)) - print(best_parent, ' ', self.calculate_tl_ratio(best_parent, self.n)) + print(best_parent, 'fitness=%f' % self.cost(best_parent)) return best_parent def __init_pool(self, pool_size): @@ -41,6 +40,7 @@ :param pool_size: DNA size or number of parents in the GA pool. :return: A DNA or pool in list format. """ + print("Initializing the pool...") self.pool.clear() all_comb = it.combinations_with_replacement(self.choices, self.trials) sample = random.sample(list(all_comb), pool_size) @@ -53,37 +53,35 @@ :param count: Number of desired best parents to be returned. Default is 1. :return: A list of most fit sequences. """ - sorted_pool = sorted(self.pool, key=lambda ss: self.fitness(ss)) + sorted_pool = sorted(self.pool, key=lambda ss: self.cost(ss)) return sorted_pool[:count] - def __calc_dist_cost(self, seq, trials): + def even_dist_cost(self, seq): """ Calculate fitness according to the similarity to the desired uniform distribution. :param seq: a string - :param trials: :return: """ - costs = {} + costs = {c: 0.0 for c in self.choices} for c in list(seq): - if costs.__contains__(c): - costs[c] += 1.0 - else: - costs.setdefault(c, 1.0) + costs[c] += (1.0 if costs.__contains__(c) else 0.0) + costs = {k: abs(1.0 - v*len(self.choices)/self.trials) for k,v in costs.items()} return max(list(costs.values())) - def fitness(self, seq): + def cost(self, seq): """ - Calculate overall fitness of a sequence (block of trials). It's cost, so we try to minimize this cost. + Calculate overall fitness of a sequence (block of trials). + Right now it's a cost function, so we try to minimize this cost. :param seq: :return: """ # add fitness for uniform distribution of all stimuli - return abs(self.calculate_tl_ratio(seq, self.n) - self.tl) + return abs(self.calculate_tl_ratio(seq, self.n) - self.tl) + self.even_dist_cost(seq) def crossover_all(self): """ - Do random crossover for all pairs. - :return: + Perform random crossover for all pairs. + :return: new pool """ new_pool = [] for i in range(int(self.pool_size/2)): @@ -124,8 +122,10 @@ if __name__ == '__main__': - generator = GAOptimizedRandomGenerator(['a', 'b', 'c', 'd', 'e', 'f'], trials=32) - s = generator.generate() - tl_ratio = generator.calculate_tl_ratio(s, 2) - print('GA-Optimized Sequence: %s' % s, 'with tl_ratio=%f' % tl_ratio) + generator = GAOptimizedRandomGenerator(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'], trials=16) + sq = generator.generate() + tl_ratio = generator.calculate_tl_ratio(sq, 2) + even_dist = generator.even_dist_cost(sq) + + print('GA-Optimized Sequence: %s' % sq, 'with tl_ratio=%f' % tl_ratio, 'and even_dist=%f' % even_dist)