Skip to content

ga_instance.best_solution() performs all solutions in instance #79

Closed

Description

@Stoops-ML

The best_solution() method of a PyGAD.GA() instance runs all the solutions within the instance. This seems unnecessary as the instance could save the best solution on the fly and therefore not need to run all the solutions in the instance.

Activity

  1. Stoops-ML commented on Dec 30, 2021

    @Stoops-ML
    Author

    I didn't notice the input save_best_solutions=False to PyGAD.GA(). Apolagies

  2. whubaichuan commented on Feb 21, 2023

    @whubaichuan

    @Stoops-ML hi, what do you mean here? run all the solutions mean all the generation's solutions? or just the last generated whole solution?

  3. ahmedfgad commented on Feb 22, 2023

    @ahmedfgad
    Owner

    Hi @whubaichuan,

    @Stoops-ML means that we do not have to re-calculate the fitness of the best solutions in the best_solution() method. As these solutions got their fitness values calculated previously, we can re-use them instead of calling the fitness function again.

    As @Stoops-ML mentioned, we can use the save_best_solutions parameter. When it is set to True, then the best solutions in addition to their fitness values are saved into these 2 instance attributes:

    1. best_solutions
    2. best_solutions_fitness
  4. whubaichuan commented on Feb 22, 2023

    @whubaichuan

    @ahmedfgad hi, thanks for your reply.

    In my testing,

    1. it seems that best_solution() does not calculate all the solutions in the last generation when keep_elitsm is higher than 0 (for example =1), which is conflict with this document
    2. It seems that the best_solutions_fitness can be valid even save_best_solution is False
    
    def on_start(ga_instance):
    
        print("Initial Generation")
        print(ga_instance.population)
    
    
    def fitness_func(solution,solution_idx):
        return sum(solution)
    
    def on_generation(ga):
        print("Generation", ga.generations_completed)
        print(ga.population)
    
    if __name__ == '__main__':
    
    
        num_generations = 3
        num_parents_mating = 2
        fitness_function = fitness_func
        sol_per_pop = 4
        num_genes = 3
        parent_selection_type = "rank"
        keep_elitism = 1
    
        gene_space = [[1,2,3,4],[1,2,3,4],[1,2,3,4]]
    
        ga_instance = pygad.GA(num_generations=num_generations,
                            num_parents_mating=num_parents_mating,
                            fitness_func=fitness_function,
                            sol_per_pop=sol_per_pop,
                            num_genes=num_genes,
                                #callback_generation=callback_gen,
                                gene_space = gene_space,
                            parent_selection_type=parent_selection_type,
                                keep_elitism=keep_elitism,
                                random_seed=2,
                            on_start=on_start,
                            on_generation=on_generation,
                                gene_type=int,
                                save_best_solutions=False,
                            parallel_processing=["process", 4])
        
        ga_instance.run()
        print(ga_instance.population)
        print(ga_instance.last_generation_fitness)
        print(ga_instance.best_solutions_fitness)
    
        solution, solution_fitness, solution_idx = ga_instance.best_solution()
        print("Parameters of the best solution : {solution}".format(solution=solution))
        print("Fitness value of the best solution = {solution_fitness}".format(solution_fitness=solution_fitness))
        print("Index of the best solution : {solution_idx}".format(solution_idx=solution_idx))
    
    
  5. ahmedfgad commented on Feb 22, 2023

    @ahmedfgad
    Owner

    @whubaichuan,

    Thanks for your notes.

    For the first point, the documentation will be updated to include these details about the cal_pop_fitness() method:

    The cal_pop_fitness() method calculates and returns the fitness values of the solutions in the current population.
    This function is optimized to save time by making fewer calls the fitness function. It follows this process:

    1. If the save_solutions parameter is set to True, then it checks if the solution is already explored and saved in the solutions instance attribute. If so, then it just retrieves its fitness from the solutions_fitness instance attribute without calling the fitness function.
    2. If save_solutions is set to False or if it is True but the solution was not explored yet, then the cal_pop_fitness() method checks if the keep_elitism parameter is set to a positive integer. If so, then it checks if the solution is saved into the last_generation_elitism instance attribute. If so, then it retrieves its fitness from the previous_generation_fitness instance attribute.
    3. If neither of the above 3 conditions apply (1. save_solutions is set to False or 2. if it is True but the solution was not explored yet or 3. keep_elitism is set to zero), then the cal_pop_fitness() method checks if the keep_parents parameter is set to -1 or a positive integer. If so, then it checks if the solution is saved into the last_generation_parents instance attribute. If so, then it retrieves its fitness from the previous_generation_fitness instance attribute.
    4. If neither of the above 4 conditions apply, then we have to call the fitness function to calculate the fitness for the solution. This is by calling the function assigned to the fitness_func parameter.

    This function takes into consideration:

    1. The parallel_processing parameter to check whether parallel processing is in effect.
    2. The fitness_batch_size parameter to check if the fitness should be calculated in batches of solutions.
      It returns a vector of the solutions' fitness values.

    For the second point, you are right. Even if save_best_solutions=False, the fitness values of the best solutions are saved into the best_solutions_fitness attribute. Note that this happens with minor increase in the computational time.

  6. added a commit that references this issue on Feb 22, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

questionFurther information is requested

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions