Skip to content

ga_instance.continue() after pygad.load() #123

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

Closed
FeBe95 opened this issue Aug 1, 2022 · 6 comments
Closed

ga_instance.continue() after pygad.load() #123

FeBe95 opened this issue Aug 1, 2022 · 6 comments
Labels
enhancement New feature or request

Comments

@FeBe95
Copy link

FeBe95 commented Aug 1, 2022

Suggestion:

I created a GA with pygad where the fitness function needs a lot of time to calculate (several minutes per fitness calculation). When I am manually stopping it by hitting Ctrl+C I let the code continue and save the current state with ga_instance.save("file"). When I start another run I load the last state with ga_instance.load("file") and call ga_instance.run() to continue with the current population. It would be neat to be able to continue the calculations without loosing ga_instance.solutions etc., so when I call plot_fitness() my old data is still there. Currently with ga_instance.run() everything gets reset to [].

Sample Code:

try:
  ga_instance = pygad.load("file")
  ga_instance.continue()
except FileNotFoundError:
  ga_instance = pygad.GA([...])
  ga_instance.run()

ga_instance.save("file")
ga_instance.plot_fitness()
@ahmedfgad ahmedfgad added the enhancement New feature or request label Aug 2, 2022
@ahmedfgad
Copy link
Owner

@FeBe95, thanks for opening the issue.

To get your idea, you mean saving the state when the execution is stopped by force? That is it does not stop by itself but you stop the execution manually, in the middle, by pressing CTRL+C for example?

@FeBe95
Copy link
Author

FeBe95 commented Aug 2, 2022

Stopping by force would be one option. Another possible situation is that you have stop_criteria set (reach/saturate) and later want to continue the run with another value for stop_criteria.

ga_instance = pygad.load("file")
ga_instance.stop_criteria = None
ga_instance.continue() # instead of .run() which resets old stats
ga_instance.plot_fitness()

The problem is that calling run() on a loaded ga_instance would reset self.best_solutions, self.best_solutions_fitness, self.solutions and self.solutions_fitness (see line 1262 - 1625 of pygad.py). It just starts a new GA with the loaded parameters and saved population as the initial population, as far as i understood.
To put it simply: I want to be able to continue a loaded ga_instance without loosing the old solution values etc. A final plot_fitness() would show all data, not only the data from the second run.

I hope you understand the issue better now. Feel free to ask again if something is unclear.

@ahmedfgad
Copy link
Owner

ahmedfgad commented Aug 2, 2022

Thanks for the clarification. Note that the following variables are reset to empty values each time the run() method runs.

self.best_solutions = []
self.best_solutions_fitness = []
self.solutions = []
self.solutions_fitness = []

I will consider updating the code to not lose these variables with each call to the run() method.

With the current situation, please note that the plot_fitness() method will recreate the same plot. This is an example.

import pygad
import numpy

function_inputs = [4,-2,3.5,5,-11,-4.7]
desired_output = 44

def fitness_func(solution, solution_idx):
    output = numpy.sum(solution*function_inputs)
    fitness = 1.0 / (numpy.abs(output - desired_output) + 0.000001)
    return fitness

ga_instance = pygad.GA(num_generations=100,
                       num_parents_mating=10,
                       sol_per_pop=20,
                       num_genes=len(function_inputs),
                       fitness_func=fitness_func)

ga_instance.run()

ga_instance.plot_fitness()

filename = 'genetic'
ga_instance.save(filename=filename)

To recreate the same plot, use this code. Note that you have to define all the functions used when loading the file.

import pygad
import numpy

function_inputs = [4,-2,3.5,5,-11,-4.7]
desired_output = 44

def fitness_func(solution, solution_idx):
    output = numpy.sum(solution*function_inputs)
    fitness = 1.0 / (numpy.abs(output - desired_output) + 0.000001)
    return fitness

filename = 'genetic'

loaded_ga_instance = pygad.load(filename=filename)
loaded_ga_instance.plot_fitness()

You can find that the same plot is created.

Please let me know if we can help in anyway.

@FeBe95
Copy link
Author

FeBe95 commented Aug 2, 2022

I will consider updating the code to not lose these variables with each call to the run() method.

That's exactly what I thought of. I don't know if anything else needs to be adjusted here in order to be able to continue a calculation. Not losing these values would be a good first improvement.

With the code you provided I am able to recreate the plot. If I call run() right after load() like so

loaded_ga_instance = pygad.load(filename=filename)
loaded_ga_instance.run()
loaded_ga_instance.plot_fitness()

The data is reset. I might be able to improve the fitness further by calling run() again but the resulting plot does not include the loaded old fitness values. Even if you called run() twice in the same file (without saving/loading) the plot is missing the first data (see below). I think you understand what I mean. Sorry if I am being repetitive 😄

Output of plot_fitness() in second script you provided after calling run() again or after calling run() twice in the same script:

Plot

fitness123

@ahmedfgad
Copy link
Owner

You are totally right!

So, there are 2 things:

  1. When you just plot the fitness without running the code, then it recreates the previous plot exactly.
  2. If you called the run() method, then the last plot is discarded and a new plot is created.

These are interesting features to support in a future release of PyGAD. Thanks for opening this issue which yields great stuff to support.

Please let me know if you have something in mind now or later.

@davidmurray
Copy link

Hi, this would be useful for me too as my fitness function involves really long transportation network simulations. A way to pause the calculations and restart them later would be really great. Is there any progress on this? Thanks!

ahmedfgad added a commit that referenced this issue Sep 9, 2022
1. Raise an exception if the sum of fitness values is zero while either roulette wheel or stochastic universal parent selection is used. #129
2. Initialize the value of the `run_completed` property to `False`. #122
3. The values of these properties are no longer reset with each call to the `run()` method `self.best_solutions, self.best_solutions_fitness, self.solutions, self.solutions_fitness`: #123. Now, the user can have the flexibility of calling the `run()` method more than once while extending the data collected after each generation. Another advantage happens when the instance is loaded and the `run()` method is called, as the old fitness value are shown on the graph alongside with the new fitness values. Read more in this section: [Continue without Loosing Progress](https://pygad.readthedocs.io/en/latest/README_pygad_ReadTheDocs.html#continue-without-loosing-progress)
4. Thanks [Prof. Fernando Jiménez Barrionuevo](http://webs.um.es/fernan) (Dept. of Information and Communications Engineering, University of Murcia, Murcia, Spain) for editing this [comment](https://github.com/ahmedfgad/GeneticAlgorithmPython/blob/5315bbec02777df96ce1ec665c94dece81c440f4/pygad.py#L73) in the code. 5315bbe
5. A bug fixed when `crossover_type=None`.
6. Support of elitism selection through a new parameter named `keep_elitism`. It defaults to 1 which means for each generation keep only the best solution in the next generation. If assigned 0, then it has no effect. Read more in this section: [Elitism Selection](https://pygad.readthedocs.io/en/latest/README_pygad_ReadTheDocs.html#elitism-selection). #74
7. A new instance attribute named `last_generation_elitism` added to hold the elitism in the last generation.
8. A new parameter called `random_seed` added to accept a seed for the random function generators. Credit to this issue #70 and [Prof. Fernando Jiménez Barrionuevo](http://webs.um.es/fernan). Read more in this section: [Random Seed](https://pygad.readthedocs.io/en/latest/README_pygad_ReadTheDocs.html#random-seed).
9. Editing the `pygad.TorchGA` module to make sure the tensor data is moved from GPU to CPU. Thanks to Rasmus Johansson for opening this pull request: ahmedfgad/TorchGA#2
ahmedfgad added a commit that referenced this issue Sep 9, 2022
1. Raise an exception if the sum of fitness values is zero while either roulette wheel or stochastic universal parent selection is used. #129
2. Initialize the value of the `run_completed` property to `False`. #122
3. The values of these properties are no longer reset with each call to the `run()` method `self.best_solutions, self.best_solutions_fitness, self.solutions, self.solutions_fitness`: #123. Now, the user can have the flexibility of calling the `run()` method more than once while extending the data collected after each generation. Another advantage happens when the instance is loaded and the `run()` method is called, as the old fitness value are shown on the graph alongside with the new fitness values. Read more in this section: [Continue without Loosing Progress](https://pygad.readthedocs.io/en/latest/README_pygad_ReadTheDocs.html#continue-without-loosing-progress)
4. Thanks [Prof. Fernando Jiménez Barrionuevo](http://webs.um.es/fernan) (Dept. of Information and Communications Engineering, University of Murcia, Murcia, Spain) for editing this [comment](https://github.com/ahmedfgad/GeneticAlgorithmPython/blob/5315bbec02777df96ce1ec665c94dece81c440f4/pygad.py#L73) in the code. 5315bbe
5. A bug fixed when `crossover_type=None`.
6. Support of elitism selection through a new parameter named `keep_elitism`. It defaults to 1 which means for each generation keep only the best solution in the next generation. If assigned 0, then it has no effect. Read more in this section: [Elitism Selection](https://pygad.readthedocs.io/en/latest/README_pygad_ReadTheDocs.html#elitism-selection). #74
7. A new instance attribute named `last_generation_elitism` added to hold the elitism in the last generation.
8. A new parameter called `random_seed` added to accept a seed for the random function generators. Credit to this issue #70 and [Prof. Fernando Jiménez Barrionuevo](http://webs.um.es/fernan). Read more in this section: [Random Seed](https://pygad.readthedocs.io/en/latest/README_pygad_ReadTheDocs.html#random-seed).
9. Editing the `pygad.TorchGA` module to make sure the tensor data is moved from GPU to CPU. Thanks to Rasmus Johansson for opening this pull request: ahmedfgad/TorchGA#2
@FeBe95 FeBe95 closed this as completed Oct 20, 2022
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

3 participants