pythonoptimizationgenetic-algorithmpygad

get the generation number and pass it to the fitness function PyGAD


I am trying to set up the fitness function in PyGAD but inside this I am calling and external process for each solution(individual) get the resulting data and perform the fitness. So I'll have a folder for each generation and each individual for each process execution, I can get the individual number with the solution_idx but I want the generation number so i can create the folders with each index (generation and individual). The format for a fitness function in PyGAD documentation is:

def fitness_func(solution, solution_idx):
    #perform fitness
    return fitness

For what I want it would look something like this:

def fitness_func(solution, solution_idx,ga_instance):

    generation=ga_instance.generation_completed

    data=function2callprocess(solution,solution_idx,generation)

    fitness=fitness(data)

    return fitness

so the function2callprocess needs the solution index and generation number to keep track of the GA so it creates the folders or each run of the process.

I wonder if this is possible or anybody had any suggestions.


Solution

  • Thanks for using PyGAD, Maria :)

    Based on your question, you need to pass 3 parameters instead of just 2 to the fitness function where the third parameter is the current generation number.

    Because PyGAD expects the fitness function to only have 2 parameters, you cannot pass a third parameter. But it is super easy to work around it.

    You already know that the current generation number can be accessed through the generations_completed property of the pygad.GA instance. If the instance is ga_instance, then we can access this parameter using:

    ga_instance.generations_completed
    

    Based on the fact that the fitness function is only called after an instance of the pygad.GA class is created, then we can use this instance inside the fitness function to access the generations_completed attribute.

    This is how the fitness function should be. The trick is accessing the global variable ga_instance which is defined outside the fitness function. You can read this article for more information about global variables in Python.

    def fitness_func(solution, solution_idx):
        global ga_instance
    
        print("generations_completed", ga_instance.generations_completed)
    
        # fitness = ...
    
        return fitness
    

    This is a complete example which generates random fitness values. Just edit the fitness function to calculate the fitness properly.

    import pygad
    import random
    
    def function2callprocess(solution, solution_idx, generation_number):
        return random.random()
    
    def fitness_func(solution, solution_idx):
        global ga_instance
    
        print("generations_completed", ga_instance.generations_completed)
    
        generation = ga_instance.generations_completed
    
        data = function2callprocess(solution, solution_idx, generation)
    
        # fitness = ...
        fitness = data
    
        return fitness
    
    last_fitness = 0
    def on_generation(ga_instance):
        global last_fitness
        print("Generation = {generation}".format(generation=ga_instance.generations_completed))
        print("Fitness    = {fitness}".format(fitness=ga_instance.best_solution(pop_fitness=ga_instance.last_generation_fitness)[1]))
        print("Change     = {change}".format(change=ga_instance.best_solution(pop_fitness=ga_instance.last_generation_fitness)[1] - last_fitness))
        last_fitness = ga_instance.best_solution(pop_fitness=ga_instance.last_generation_fitness)[1]
    
    ga_instance = pygad.GA(num_generations=5,
                           num_parents_mating=5,
                           sol_per_pop=10,
                           num_genes=2,
                           fitness_func=fitness_func,
                           on_generation=on_generation)
    
    ga_instance.run()
    
    ga_instance.plot_fitness()
    
    solution, solution_fitness, solution_idx = ga_instance.best_solution(ga_instance.last_generation_fitness)
    print("Solution", solution)
    print("Fitness value of the best solution = {solution_fitness}".format(solution_fitness=solution_fitness))