Skip to content

delay_after_gen warning #283

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wassimj opened this issue Mar 21, 2024 · 4 comments
Open

delay_after_gen warning #283

wassimj opened this issue Mar 21, 2024 · 4 comments
Labels
enhancement New feature or request

Comments

@wassimj
Copy link

wassimj commented Mar 21, 2024

Hello,
I am using PyGAD version: 3.3.1 on Windows with python 3.10 within jupyter notebook.

When I run my GA, I am getting the following user warning. This is not something I am setting. It seems to emanate from the internal pygad code. How can I avoid having this warning displayed? Thank you

C:\Users\wassimj\AppData\Local\Programs\Python\Python310\lib\site-packages\pygad\pygad.py:1139: UserWarning:

The 'delay_after_gen' parameter is deprecated starting from PyGAD 3.3.0. To delay or pause the evolution after each generation, assign a callback function/method to the 'on_generation' parameter to adds some time delay.
@ahmedfgad ahmedfgad added the enhancement New feature or request label Mar 27, 2024
@ahmedfgad
Copy link
Owner

Thanks, @wassimj. This is a bug. This warning should only be printed if you used the delay_after_gen parameter.

It will be fixed soon.

ahmedfgad added a commit that referenced this issue Mar 27, 2024
@gnypit
Copy link

gnypit commented Jun 1, 2024

Hi all,

I've encountered this as well. What's interesting is that if I re-run the code, the warning disapears and the overall performance of the genetic algorithm improves. For example, the first time the warning comes out, the final result is correct, but the fitness history shows that fitness values varied quite strongly - sometimes fitness wasn't even a non-decreasing function, in spite of a non-zero value in the keep_parents parameter (keeping the so-called 'elite'). Since I haven't been able to reproduce it yet, that's probably for another time.

Back to the delay_after_gen issue; this is a simple code for a packaging problem - a thief has to choose what to steal with a limited maximum weight he can carry. Items at the house have different values and weights. The goal is to choose the most expensive things without surpassing the weight limit. The code is as follows:

import pygad
import matplotlib.pyplot as plt

house = {
    1: ['clock', 100, 7],
    2: ['landscape painting', 300, 7],
    3: ['portrait painting', 200, 6],
    4: ['radio', 40, 2],
    5: ['laptop', 500, 5],
    6: ['night lamp', 70, 6],
    7: ['silver cutlery', 100, 1],
    8: ['porcelain', 250, 3],
    9: ['bronze figurine', 300, 10],
    10: ['leather handbag', 280, 3],
    11: ['vacuum cleaner', 300, 15]
}

def fitness_function(genetic_algorithm_instance, solution, solution_index):
    weight = 0
    fitness = 0

    for i in range(len(solution)):
        if solution[i] == 1:
            fitness += domek[i+1][1]
            weight += domek[i+1][2]
            if weight > 25:
                fitness += -2000

    return fitness

ga_instance = pygad.GA(
    gene_space=[0, 1],
    num_generations=100,
    num_parents_mating=15,
    fitness_func=fitness_function,
    sol_per_pop=40,
    num_genes=11,
    parent_selection_type="tournament",
    keep_parents=5,
    crossover_type="single_point",
    mutation_type="random",
    mutation_probability=0.02
)

# At this point a UserWarning appears

ga_instance.run()

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))

prediction = []
money= 0
weight = 0
for i in range(len(solution)):
    if solution[i] == 1:
        prediction.append(domek[i+1][0])
        money += house[i+1][1]
        weight += house[i+1][2]

print("Predicted output based on the best solution : {prediction}".format(prediction=prediction))
print(money)
print(weight)

# Ensure any previous plots are cleared
plt.clf()

# Plot the fitness values vs. generations with proper labels (ga_instance.plot_fitness() gives two plots)
plt.plot(ga_instance.best_solutions_fitness, label='Best Fitness')
plt.xlabel("Generation")
plt.ylabel("Fitness")
plt.title("Fitness over Generations")
plt.legend()
plt.show()

The waring I get is this:

C:\Users\Jakub\AppData\Local\Programs\Python\Python311\Lib\site-packages\pygad\pygad.py:1139: UserWarning: The 'delay_after_gen' parameter is deprecated starting from PyGAD 3.3.0. To delay or pause the evolution after each generation, assign a callback function/method to the 'on_generation' parameter to adds some time delay.
warnings.warn("The 'delay_after_gen' parameter is deprecated starting from PyGAD 3.3.0. To delay or pause the evolution after each generation, assign a callback function/method to the 'on_generation' parameter to adds some time delay.")

If I re-run the code, result and overall performance improve, while 1the warnings disappear. Fitness plots:
output_thief_01
output_thief_02

Another matter altogether is that it seems to me that in spite of restarting the kernel, pygad somehow remembers something about previous runs, because while the file is open in PyCharm, the 'bad' fitness history doesn't actually repeat. To be more precise, only at the first run with the kernel still operating does the algorithm get stuck in a local maximum of the fitness function. So that's curious on it's own - I apologise, if my lack of understanding and knowledge is showing.

I debugged the 'ga_instance' initialisation with pydev debugger (build 241.14494.241). The 'delay_after_gen' was indeed set by default to 0.0; after a while I got to this section of the pygad.py file:

# Validate delay_after_gen
            if type(delay_after_gen) in GA.supported_int_float_types:
                if not self.suppress_warnings:
                    warnings.warn("The 'delay_after_gen' parameter is deprecated starting from PyGAD 3.3.0. To delay or pause the evolution after each generation, assign a callback function/method to the 'on_generation' parameter to adds some time delay.")
                if delay_after_gen >= 0.0:
                    self.delay_after_gen = delay_after_gen
                else:
                    self.valid_parameters = False
                    raise ValueError(f"The value passed to the 'delay_after_gen' parameter must be a non-negative number. The value passed is ({delay_after_gen}) of type {type(delay_after_gen)}.")
            else:
                self.valid_parameters = False
                raise TypeError(f"The value passed to the 'delay_after_gen' parameter must be of type int or float but {type(delay_after_gen)} found.")

Since delay_after_gen is by default set to 0.0, it is a float value and it passes the if type(delay_after_gen) in GA.supported_int_float_types: check. Then, since (again, by default) the self.suppress_warnings is set to False, the warning from the first nested conditional clause is run:
Zrzut ekranu 2024-06-01 152734
Zrzut ekranu 2024-06-01 152753
Zrzut ekranu 2024-06-01 152859

My idea is to change the default value of the delay_after_gen parameter to None. I guess (haven't checked that yet tbh) that it is not included in the supported types of variables for the parameter. What's the most interesting though, is that when I re-run the code with the same kernel operating, the same default values are assigned to both delay_after_gen and self.suppress_warnings, the waring doesn't show and while debugging I didn't get to the section of the code above ^ what else does it depend on? I didn't notice any other change, and the delay_after_gen falls under the try clause right in the __init__() constructor of the GA class. Since it is not nested in another conditional clause or anything else like that, what's the difference between the first run and the other runs?

Just in case: I'm using Python 3.12.3 and pygad 3.3.1.

@Kk-Ling
Copy link

Kk-Ling commented Jul 5, 2024

suppress_warnings=True

@ahmedfgad
Copy link
Owner

The delay_after_gen parameter is going to be removed. A delay can be introduced inside the on_generation callback.

ahmedfgad added a commit that referenced this issue Jan 7, 2025
1. The `delay_after_gen` parameter is removed from the `pygad.GA` class constructor. As a result, it is no longer an attribute of the `pygad.GA` class instances. To add a delay after each generation, apply it inside the `on_generation` callback. #283
2. In the `single_point_crossover()` method of the `pygad.utils.crossover.Crossover` class, all the random crossover points are returned before the `for` loop. This is by calling the `numpy.random.randint()` function only once before the loop to generate all the K points (where K is the offspring size). This is compared to calling the `numpy.random.randint()` function inside the `for` loop K times, once for each individual offspring.
3. Bug fix in the `examples/example_custom_operators.py` script. #285
4. While making prediction using the `pygad.torchga.predict()` function, no gradients are calculated.
5. The `gene_type` parameter of the `pygad.helper.unique.Unique.unique_int_gene_from_range()` method accepts the type of the current gene only instead of the full gene_type list.
6. Created a new method called `unique_float_gene_from_range()` inside the `pygad.helper.unique.Unique` class to find a unique floating-point number from a range.
7. Fix a bug in the `pygad.helper.unique.Unique.unique_gene_by_space()` method to return the numeric value only instead of a NumPy array.
8. Refactoring the `pygad/helper/unique.py` script to remove duplicate codes and reformatting the docstrings.
9. The plot_pareto_front_curve() method added to the pygad.visualize.plot.Plot class to visualize the Pareto front for multi-objective problems. It only supports 2 objectives. #279
10. Fix a bug converting a nested NumPy array to a nested list. #300
11. The `Matplotlib` library is only imported when a method inside the `pygad/visualize/plot.py` script is used. This is more efficient than using `import matplotlib.pyplot` at the module level as this causes it to be imported when `pygad` is imported even when it is not needed. #292
12. Fix a bug when minus sign (-) is used inside the `stop_criteria` parameter (e.g. `stop_criteria=["saturate_10", "reach_-0.5"]`). #296
13. Make sure `self.best_solutions` is a list of lists inside the `cal_pop_fitness` method. #293
14. Fix a bug where the `cal_pop_fitness()` method was using the `previous_generation_fitness` attribute to return the parents fitness. This instance attribute was not using the fitness of the latest population, instead the fitness of the population before the last one. The issue is solved by updating the `previous_generation_fitness` attribute to the latest population fitness before the GA completes. #291
@ahmedfgad ahmedfgad mentioned this issue Jan 7, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants