Skip to content

Commit 957e0c6

Browse files
committed
feat: Added GARandomSearchReversals
1 parent a6e184c commit 957e0c6

File tree

1 file changed

+122
-0
lines changed

1 file changed

+122
-0
lines changed

fliscopt/ga.py

+122
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,128 @@ def run(self,domain,fitness_function,seed) -> tuple:
369369
return costs[0][1], costs[0][0], scores, nfe, self.seed
370370

371371
return costs[0][1], costs[0][0], scores, nfe, self.seed
372+
373+
class GARSReversals(BaseGA):
374+
375+
"""
376+
A genetic algorithm that can perform reverse optimization, to escape local minimma inspired from [1]
377+
In this algorithm the minimzation process is reversed to a maxmimization process i/n_k times i.e process in reverse direction for step_length no. of times.
378+
Unlike GAReversals we perform a RandomSearch in reverse direction to escape local minimas.
379+
380+
References:
381+
382+
[1]https://www.microsoft.com/en-us/research/blog/genetic-algorithm-in-reverse-mode/#:~:text=%20To%20summarize%3A%20%201%20Reversing%20genetic%20algorithm,Duration%20and%20frequency%20of%20reversal%20cycle...%20More%20
383+
384+
Attributes:
385+
domain (list): List containing the upper and lower bound.i.e domain of our inputs
386+
fitness_function (function): This parameter accepts a fitness function of given optimization problem.
387+
seed (int,optional): Set the seed value of the random seed generator. Defaults to random integer value.
388+
seed_init(bool,optional): True set's the seed of only population init generator, False sets all generators
389+
init (list, optional): List for initializing the initial solution. Defaults to [].
390+
epochs (int, optional): Number of times the algorithm runs. Defaults to 100.
391+
392+
n_k (int, optional): The denominator factor i/n_k which determines the number of iterations which are multiples of n_k where reversals take place.Defaults to 250.
393+
step_length (int, optional): The number of reversals steps/iterations to perform.Defeaults to 100 reversal steps.
394+
Returns:
395+
list: List containing the best_solution,
396+
int: The final cost after running the algorithm,
397+
list: List containing all costs during all epochs.
398+
int: The number of function evaluations(NFE) after running the algorithm
399+
int: Seed value used by random generators.
400+
"""
401+
def __init__(self, domain=domain['domain'], fitness_function=fitness_function, seed=random.randint(10, 100), seed_init=True, init=[],max_time=100,
402+
population_size=100, step=1, probability_mutation=0.2, elitism=0.2,
403+
number_generations=500, search=False,n_k=250, step_length=100,) -> None:
404+
super().__init__(domain, fitness_function, seed, seed_init, init,max_time, population_size, step, probability_mutation,
405+
0.0, elitism, number_generations, search)
406+
self.n_k = n_k
407+
self.step_length = step_length
408+
409+
410+
def run(self,domain,fitness_function,seed) -> tuple:
411+
#self.__init__(domain, fitness_function, seed, self.seed_init, self.init,self.max_time)
412+
super().__init__(domain, fitness_function, seed, self.seed_init, self.init,self.max_time, self.population_size, self.step, self.probability_mutation,
413+
0.0, self.elitism, self.number_generations, self.search)
414+
population = []
415+
scores = []
416+
nfe = 0
417+
rev = 0
418+
419+
for i in range(self.population_size):
420+
if self.search == True:
421+
solution, b_c, sc, r_nfe, s = RandomSearch(
422+
).run(self.domain, self.fitness_function,self.seed)
423+
nfe += r_nfe
424+
if len(self.init) > 0:
425+
solution = self.init
426+
else:
427+
solution = [self.r_init.randint(self.domain[i][0], self.domain[i][1])
428+
for i in range(len(self.domain))]
429+
430+
population.append(solution)
431+
432+
number_elitism = int(self.elitism * self.population_size)
433+
self.start_time=time.time()
434+
for i in range(self.number_generations):
435+
if not self.fitness_function.__name__ == 'fitness_function':
436+
costs = [(self.fitness_function(individual), individual)
437+
for individual in population]
438+
else:
439+
costs = [(self.fitness_function(individual, 'FCO'), individual)
440+
for individual in population]
441+
nfe += 1
442+
if i % self.n_k == 0 and i != 0:
443+
if self.step_length == 1:
444+
costs.sort(reverse=True)
445+
rev += 1
446+
else:
447+
rev += 1
448+
for _ in range(self.step_length - 1):
449+
costs.sort(reverse=True)
450+
nfe = 0
451+
solution = [self.r_init.randint(self.domain[i][0], self.domain[i][1])
452+
for i in range(len(self.domain))]
453+
454+
if not self.fitness_function.__name__ == 'fitness_function':
455+
cost = self.fitness_function(solution)
456+
#scores.append(cost)
457+
else:
458+
cost = self.fitness_function(solution, 'FCO')
459+
#scores.append(cost)
460+
461+
nfe += 1
462+
if cost > self.best_cost:
463+
self.best_cost = cost
464+
self.best_solution = solution
465+
466+
scores.append(self.best_cost)
467+
population.append(self.best_solution)
468+
469+
print(rev) # To print the number of reversals
470+
else:
471+
heapq.heapify(costs)
472+
ordered_individuals = [individual for (cost, individual) in costs]
473+
population = ordered_individuals[0:number_elitism]
474+
if not self.fitness_function.__name__ == 'fitness_function':
475+
scores.append(self.fitness_function(population[0]))
476+
else:
477+
scores.append(self.fitness_function(population[0], 'FCO'))
478+
nfe += 1
479+
while len(population) < self.population_size:
480+
if random.random() < self.probability_mutation:
481+
i1 = random.randint(0, number_elitism)
482+
i2 = random.randint(0, number_elitism)
483+
population.append(
484+
crossover(self.domain, ordered_individuals[i1], ordered_individuals[i2]))
485+
else:
486+
m = random.randint(0, number_elitism)
487+
population.append(
488+
mutation(self.domain, self.step, ordered_individuals[m]))
489+
490+
if time.time()-self.start_time>self.max_time:
491+
return costs[0][1], costs[0][0], scores, nfe, self.seed
492+
493+
return costs[0][1], costs[0][0], scores, nfe, self.seed
372494

373495

374496
if __name__ == '__main__':

0 commit comments

Comments
 (0)