Skip to content
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

Execute on_crossover callback even if crossover_type is None #138

Closed
borisarloff opened this issue Oct 18, 2022 · 3 comments
Closed

Execute on_crossover callback even if crossover_type is None #138

borisarloff opened this issue Oct 18, 2022 · 3 comments
Labels
enhancement New feature or request

Comments

@borisarloff
Copy link

borisarloff commented Oct 18, 2022

Currently, the code does not call self.on_crossover(self, self.last_generation_offspring_crossover) on line 1373 when self.crossover_type is None. A callback before executing the mutation segment would be however useful for customizing the mutation type and probability on an offspring-by-offspring basis, even when no crossover is required.

I am requesting that the on_crossover provisioned method be called with a modified or unmodified offspring, regardless to crossover type value. A similar logic would also apply to on_mutation specified callback function.

IMO: In general, any specified callbacks should be exercised throughout the framework pipeline when provisioned, regardless of internal processing. The user should be able to rely on the callbacks being triggered, and then perhaps ignore them as applicable.

Please see additional information added on 10/20/2022, below.

@borisarloff
Copy link
Author

code.zip
I have attached a zip file containing a monkey-patch modified pygad v2.18.1 code (pygad_2_18_1_barloff.py) and excerpts of my code (gaengine_excerpt.py).

The modifications to pygad are annotated as "#barloff" for easy search. This modifications (1) allow to pass the user's Class instance reference for persistence flow of computations; (2) this Class instance reference is now declared in the on_ GA call implementations, set to None as default at the user side, and (3) specifically for on_crossover and on_mutation references now are always executed, even when crossover_type and mutation_type are None.

The gaengine_excerpt.py module exemplifies the usefulness of these proposed changes. Highlights are annotated by PyDoc comments as "NOTE:" in each case. The onParent(), onFitness(), and onCrossover() static methods are provisioned to on_parent, on_fitness, and on_crossover parameters of GA class constructor. A new "cls_inst" parameter is used to pass on the "self" reference.

The advantage is that a user is able to interact with the GA process from within its own Class instance implementation. The extra parameter cls_inst needed by the on_ callbacks can be ignored, if not needed.

In the gaengine_excerpt.py implementation, one assumption is made that the fitness list sequentially corresponds to the offspring returned by on_crossover. Please advice if that is not the case, a code review seems to indicate that it is the case.

Request that the proposed changes be adopted in a future release of pygad.py module.

@borisarloff
Copy link
Author

Pull request #143 was submitted and it is pending addressing this issue by @borisarloff.

ahmedfgad added a commit that referenced this issue Feb 14, 2023
Release Date: 14 February 2023

1. Remove `numpy.int` and `numpy.float` from the list of supported data types. #151 #152
2. Call the `on_crossover()` callback function even if `crossover_type` is `None`. #138
3. Call the `on_mutation()` callback function even if `mutation_type` is `None`. #138
@ahmedfgad
Copy link
Owner

ahmedfgad commented Feb 22, 2023

@borisarloff,

This feature is supported in PyGAD 2.18.3:

  • The on_crossover() and on_mutation() callback functions/methods are called even if crossover_type=None or mutation_type=None.

The new release will support:

  • Passing class methods as the fitness and callbacks. The first argument in these methods is a reference to the calling class instance.

Thanks for your suggestions.

ahmedfgad added a commit that referenced this issue Feb 22, 2023
PyGAD 2.19.0 Release Notes
1. A new `summary()` method is supported to return a Keras-like summary of the PyGAD lifecycle.
2. A new optional parameter called `fitness_batch_size` is supported to calculate the fitness function in batches. If it is assigned the value `1` or `None` (default), then the normal flow is used where the fitness function is called for each individual solution. If the `fitness_batch_size` parameter is assigned a value satisfying this condition `1 < fitness_batch_size <= sol_per_pop`, then the solutions are grouped into batches of size `fitness_batch_size` and the fitness function is called once for each batch. In this case, the fitness function must return a list/tuple/numpy.ndarray with a length equal to the number of solutions passed. #136.
3. The `cloudpickle` library (https://github.com/cloudpipe/cloudpickle) is used instead of the `pickle` library to pickle the `pygad.GA` objects. This solves the issue of having to redefine the functions (e.g. fitness function). The `cloudpickle` library is added as a dependancy in the `requirements.txt` file. #159
4. Support of assigning methods to these parameters: `fitness_func`, `crossover_type`, `mutation_type`, `parent_selection_type`, `on_start`, `on_fitness`, `on_parents`, `on_crossover`, `on_mutation`, `on_generation`, and `on_stop`. #92 #138
5. Validating the output of the parent selection, crossover, and mutation functions.
6. The built-in parent selection operators return the parent's indices as a NumPy array.
7. The outputs of the parent selection, crossover, and mutation operators must be NumPy arrays.
8. Fix an issue when `allow_duplicate_genes=True`. #39
9. Fix an issue creating scatter plots of the solutions' fitness.
10. Sampling from a `set()` is no longer supported in Python 3.11. Instead, sampling happens from a `list()`. Thanks `Marco Brenna` for pointing to this issue.
11. The lifecycle is updated to reflect that the new population's fitness is calculated at the end of the lifecycle not at the beginning. #154 (comment)
12. There was an issue when `save_solutions=True` that causes the fitness function to be called for solutions already explored and have their fitness pre-calculated. #160
13. A new instance attribute named `last_generation_elitism_indices` added to hold the indices of the selected elitism. This attribute helps to re-use the fitness of the elitism instead of calling the fitness function.
14. Fewer calls to the `best_solution()` method which in turns saves some calls to the fitness function.
15. Some updates in the documentation to give more details about the `cal_pop_fitness()` method. #79 (comment)
@ahmedfgad ahmedfgad added the enhancement New feature or request label Feb 25, 2023
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

2 participants