Skip to content

Commit 990f1a1

Browse files
committed
PyGAD 3.3.1
Release Date 17 February 2024 1. After the last generation and before the `run()` method completes, update the 2 instance attributes: 1) `last_generation_parents` 2) `last_generation_parents_indices`. This is to keep the list of parents up-to-date with the latest population fitness `last_generation_fitness`. ahmedfgad#275 2. 4 methods with names starting with `run_`. Their purpose is to keep the main loop inside the `run()` method clean. Check the [Other Methods](https://pygad.readthedocs.io/en/latest/pygad.html#other-methods) section for more information.
1 parent eae41c0 commit 990f1a1

File tree

3 files changed

+71
-41
lines changed

3 files changed

+71
-41
lines changed

docs/source/pygad.rst

+6-2
Original file line numberDiff line numberDiff line change
@@ -610,8 +610,12 @@ Other Methods
610610
from inside the ``run()`` method. Supported in `PyGAD
611611
3.3.1 <https://pygad.readthedocs.io/en/latest/releases.html#pygad-3-3-1>`__.
612612

613-
1. ``run_select_parents()``: Select the parents and call the callable
614-
``on_parents()`` if defined.
613+
1. ``run_select_parents(call_on_parents=True)``: Select the parents
614+
and call the callable ``on_parents()`` if defined. If
615+
``call_on_parents`` is ``True``, then the callable
616+
``on_parents()`` is called. It must be ``False`` when the
617+
``run_select_parents()`` method is called to update the parents at
618+
the end of the ``run()`` method.
615619

616620
2. ``run_crossover()``: Apply crossover and call the callable
617621
``on_crossover()`` if defined.

docs/source/releases.rst

+19
Original file line numberDiff line numberDiff line change
@@ -1532,6 +1532,25 @@ Release Date 29 January 2024
15321532
self.best_solution_generation = numpy.where(numpy.array(
15331533
self.best_solutions_fitness) == numpy.max(numpy.array(self.best_solutions_fitness)))[0][0]
15341534
1535+
.. _pygad-331:
1536+
1537+
PyGAD 3.3.1
1538+
-----------
1539+
1540+
Release Date 17 February 2024
1541+
1542+
1. After the last generation and before the ``run()`` method completes,
1543+
update the 2 instance attributes: 1) ``last_generation_parents`` 2)
1544+
``last_generation_parents_indices``. This is to keep the list of
1545+
parents up-to-date with the latest population fitness
1546+
``last_generation_fitness``.
1547+
https://github.com/ahmedfgad/GeneticAlgorithmPython/issues/275
1548+
1549+
2. 4 methods with names starting with ``run_``. Their purpose is to keep
1550+
the main loop inside the ``run()`` method clean. Check the `Other
1551+
Methods <https://pygad.readthedocs.io/en/latest/pygad.html#other-methods>`__
1552+
section for more information.
1553+
15351554
PyGAD Projects at GitHub
15361555
========================
15371556

pygad/pygad.py

+46-39
Original file line numberDiff line numberDiff line change
@@ -2067,7 +2067,8 @@ def run(self):
20672067

20682068
# Call the run_select_parents() method to update these 2 attributes according to the 'last_generation_fitness' attribute:
20692069
# 1) last_generation_parents 2) last_generation_parents_indices
2070-
self.run_select_parents()
2070+
# Set 'call_on_parents=False' to avoid calling the callable 'on_parents' because this step is not part of the cycle.
2071+
self.run_select_parents(call_on_parents=False)
20712072

20722073
# Save the fitness value of the best solution.
20732074
_, best_solution_fitness, _ = self.best_solution(
@@ -2093,7 +2094,7 @@ def run(self):
20932094
# sys.exit(-1)
20942095
raise ex
20952096

2096-
def run_select_parents(self):
2097+
def run_select_parents(self, call_on_parents=True):
20972098
"""
20982099
This method must be only callled from inside the run() method. It is not meant for use by the user.
20992100
Generally, any method with a name starting with 'run_' is meant to be only called by PyGAD from inside the 'run()' method.
@@ -2103,6 +2104,11 @@ def run_select_parents(self):
21032104
1) last_generation_parents: A NumPy array of the selected parents.
21042105
2) last_generation_parents_indices: A 1D NumPy array of the indices of the selected parents.
21052106
2107+
Parameters
2108+
----------
2109+
call_on_parents : bool, optional
2110+
If True, then the callable 'on_parents()' is called. The default is True.
2111+
21062112
Returns
21072113
-------
21082114
None.
@@ -2133,46 +2139,47 @@ def run_select_parents(self):
21332139
elif len(self.last_generation_parents_indices) != self.num_parents_mating:
21342140
raise ValueError(f"The iterable holding the selected parents indices is expected to have ({self.num_parents_mating}) values but ({len(self.last_generation_parents_indices)}) found.")
21352141

2136-
if not (self.on_parents is None):
2137-
on_parents_output = self.on_parents(self,
2138-
self.last_generation_parents)
2139-
2140-
if on_parents_output is None:
2141-
pass
2142-
elif type(on_parents_output) in [list, tuple, numpy.ndarray]:
2143-
if len(on_parents_output) == 2:
2144-
on_parents_selected_parents, on_parents_selected_parents_indices = on_parents_output
2145-
else:
2146-
raise ValueError(f"The output of on_parents() is expected to be tuple/list/numpy.ndarray of length 2 but {type(on_parents_output)} of length {len(on_parents_output)} found.")
2147-
2148-
# Validate the parents.
2149-
if on_parents_selected_parents is None:
2150-
raise ValueError("The returned outputs of on_parents() cannot be None but the first output is None.")
2151-
else:
2152-
if type(on_parents_selected_parents) in [tuple, list, numpy.ndarray]:
2153-
on_parents_selected_parents = numpy.array(on_parents_selected_parents)
2154-
if on_parents_selected_parents.shape == self.last_generation_parents.shape:
2155-
self.last_generation_parents = on_parents_selected_parents
2156-
else:
2157-
raise ValueError(f"Size mismatch between the parents retrned by on_parents() {on_parents_selected_parents.shape} and the expected parents shape {self.last_generation_parents.shape}.")
2142+
if call_on_parents:
2143+
if not (self.on_parents is None):
2144+
on_parents_output = self.on_parents(self,
2145+
self.last_generation_parents)
2146+
2147+
if on_parents_output is None:
2148+
pass
2149+
elif type(on_parents_output) in [list, tuple, numpy.ndarray]:
2150+
if len(on_parents_output) == 2:
2151+
on_parents_selected_parents, on_parents_selected_parents_indices = on_parents_output
21582152
else:
2159-
raise ValueError(f"The output of on_parents() is expected to be tuple/list/numpy.ndarray but the first output type is {type(on_parents_selected_parents)}.")
2160-
2161-
# Validate the parents indices.
2162-
if on_parents_selected_parents_indices is None:
2163-
raise ValueError("The returned outputs of on_parents() cannot be None but the second output is None.")
2164-
else:
2165-
if type(on_parents_selected_parents_indices) in [tuple, list, numpy.ndarray, range]:
2166-
on_parents_selected_parents_indices = numpy.array(on_parents_selected_parents_indices)
2167-
if on_parents_selected_parents_indices.shape == self.last_generation_parents_indices.shape:
2168-
self.last_generation_parents_indices = on_parents_selected_parents_indices
2153+
raise ValueError(f"The output of on_parents() is expected to be tuple/list/numpy.ndarray of length 2 but {type(on_parents_output)} of length {len(on_parents_output)} found.")
2154+
2155+
# Validate the parents.
2156+
if on_parents_selected_parents is None:
2157+
raise ValueError("The returned outputs of on_parents() cannot be None but the first output is None.")
2158+
else:
2159+
if type(on_parents_selected_parents) in [tuple, list, numpy.ndarray]:
2160+
on_parents_selected_parents = numpy.array(on_parents_selected_parents)
2161+
if on_parents_selected_parents.shape == self.last_generation_parents.shape:
2162+
self.last_generation_parents = on_parents_selected_parents
2163+
else:
2164+
raise ValueError(f"Size mismatch between the parents retrned by on_parents() {on_parents_selected_parents.shape} and the expected parents shape {self.last_generation_parents.shape}.")
21692165
else:
2170-
raise ValueError(f"Size mismatch between the parents indices returned by on_parents() {on_parents_selected_parents_indices.shape} and the expected crossover output {self.last_generation_parents_indices.shape}.")
2166+
raise ValueError(f"The output of on_parents() is expected to be tuple/list/numpy.ndarray but the first output type is {type(on_parents_selected_parents)}.")
2167+
2168+
# Validate the parents indices.
2169+
if on_parents_selected_parents_indices is None:
2170+
raise ValueError("The returned outputs of on_parents() cannot be None but the second output is None.")
21712171
else:
2172-
raise ValueError(f"The output of on_parents() is expected to be tuple/list/range/numpy.ndarray but the second output type is {type(on_parents_selected_parents_indices)}.")
2173-
2174-
else:
2175-
raise TypeError(f"The output of on_parents() is expected to be tuple/list/numpy.ndarray but {type(on_parents_output)} found.")
2172+
if type(on_parents_selected_parents_indices) in [tuple, list, numpy.ndarray, range]:
2173+
on_parents_selected_parents_indices = numpy.array(on_parents_selected_parents_indices)
2174+
if on_parents_selected_parents_indices.shape == self.last_generation_parents_indices.shape:
2175+
self.last_generation_parents_indices = on_parents_selected_parents_indices
2176+
else:
2177+
raise ValueError(f"Size mismatch between the parents indices returned by on_parents() {on_parents_selected_parents_indices.shape} and the expected crossover output {self.last_generation_parents_indices.shape}.")
2178+
else:
2179+
raise ValueError(f"The output of on_parents() is expected to be tuple/list/range/numpy.ndarray but the second output type is {type(on_parents_selected_parents_indices)}.")
2180+
2181+
else:
2182+
raise TypeError(f"The output of on_parents() is expected to be tuple/list/numpy.ndarray but {type(on_parents_output)} found.")
21762183

21772184
def run_crossover(self):
21782185
"""

0 commit comments

Comments
 (0)