Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 13e5713

Browse files
Update previous_generation_fitness once GA completes
1 parent 58a2ecb commit 13e5713

File tree

2 files changed

+108
-81
lines changed

2 files changed

+108
-81
lines changed

‎pygad/pygad.py

Lines changed: 47 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1211,7 +1211,7 @@ def __init__(self,
12111211
self.valid_parameters = False
12121212
raise ValueError(f"In the 'stop_criteria' parameter, the supported stop words are {self.supported_stop_words} but '{stop_word}' found.")
12131213

1214-
if number.replace(".", "").isnumeric():
1214+
if number.replace(".", "").replace("-", "").isnumeric():
12151215
number = float(number)
12161216
else:
12171217
self.valid_parameters = False
@@ -1306,6 +1306,8 @@ def __init__(self,
13061306
# A list holding the offspring after applying mutation in the last generation.
13071307
self.last_generation_offspring_mutation = None
13081308
# Holds the fitness values of one generation before the fitness values saved in the last_generation_fitness attribute. Added in PyGAD 2.16.2.
1309+
# They are used inside the cal_pop_fitness() method to fetch the fitness of the parents in one generation before the latest generation.
1310+
# This is to avoid re-calculating the fitness for such parents again.
13091311
self.previous_generation_fitness = None
13101312
# Added in PyGAD 2.18.0. A NumPy array holding the elitism of the current generation according to the value passed in the 'keep_elitism' parameter. It works only if the 'keep_elitism' parameter has a non-zero value.
13111313
self.last_generation_elitism = None
@@ -1640,14 +1642,12 @@ def cal_pop_fitness(self):
16401642
# 'last_generation_parents_as_list' is the list version of 'self.last_generation_parents'
16411643
# It is used to return the parent index using the 'in' membership operator of Python lists. This is much faster than using 'numpy.where()'.
16421644
if self.last_generation_parents is not None:
1643-
last_generation_parents_as_list = [
1644-
list(gen_parent) for gen_parent in self.last_generation_parents]
1645+
last_generation_parents_as_list = self.last_generation_parents.tolist()
16451646

16461647
# 'last_generation_elitism_as_list' is the list version of 'self.last_generation_elitism'
16471648
# It is used to return the elitism index using the 'in' membership operator of Python lists. This is much faster than using 'numpy.where()'.
16481649
if self.last_generation_elitism is not None:
1649-
last_generation_elitism_as_list = [
1650-
list(gen_elitism) for gen_elitism in self.last_generation_elitism]
1650+
last_generation_elitism_as_list = self.last_generation_elitism.tolist()
16511651

16521652
pop_fitness = ["undefined"] * len(self.population)
16531653
if self.parallel_processing is None:
@@ -1659,6 +1659,12 @@ def cal_pop_fitness(self):
16591659
# Make sure that both the solution and 'self.solutions' are of type 'list' not 'numpy.ndarray'.
16601660
# if (self.save_solutions) and (len(self.solutions) > 0) and (numpy.any(numpy.all(self.solutions == numpy.array(sol), axis=1)))
16611661
# if (self.save_solutions) and (len(self.solutions) > 0) and (numpy.any(numpy.all(numpy.equal(self.solutions, numpy.array(sol)), axis=1)))
1662+
1663+
# Make sure self.best_solutions is a list of lists before proceeding.
1664+
# Because the second condition expects that best_solutions is a list of lists.
1665+
if type(self.best_solutions) is numpy.ndarray:
1666+
self.best_solutions = self.best_solutions.tolist()
1667+
16621668
if (self.save_solutions) and (len(self.solutions) > 0) and (list(sol) in self.solutions):
16631669
solution_idx = self.solutions.index(list(sol))
16641670
fitness = self.solutions_fitness[solution_idx]
@@ -1867,13 +1873,13 @@ def run(self):
18671873

18681874
# self.best_solutions: Holds the best solution in each generation.
18691875
if type(self.best_solutions) is numpy.ndarray:
1870-
self.best_solutions = list(self.best_solutions)
1876+
self.best_solutions = self.best_solutions.tolist()
18711877
# self.best_solutions_fitness: A list holding the fitness value of the best solution for each generation.
18721878
if type(self.best_solutions_fitness) is numpy.ndarray:
18731879
self.best_solutions_fitness = list(self.best_solutions_fitness)
18741880
# self.solutions: Holds the solutions in each generation.
18751881
if type(self.solutions) is numpy.ndarray:
1876-
self.solutions = list(self.solutions)
1882+
self.solutions = self.solutions.tolist()
18771883
# self.solutions_fitness: Holds the fitness of the solutions in each generation.
18781884
if type(self.solutions_fitness) is numpy.ndarray:
18791885
self.solutions_fitness = list(self.solutions_fitness)
@@ -1913,34 +1919,8 @@ def run(self):
19131919
self.best_solutions.append(list(best_solution))
19141920

19151921
for generation in range(generation_first_idx, generation_last_idx):
1916-
if not (self.on_fitness is None):
1917-
on_fitness_output = self.on_fitness(self,
1918-
self.last_generation_fitness)
19191922

1920-
if on_fitness_output is None:
1921-
pass
1922-
else:
1923-
if type(on_fitness_output) in [tuple, list, numpy.ndarray, range]:
1924-
on_fitness_output = numpy.array(on_fitness_output)
1925-
if on_fitness_output.shape == self.last_generation_fitness.shape:
1926-
self.last_generation_fitness = on_fitness_output
1927-
else:
1928-
raise ValueError(f"Size mismatch between the output of on_fitness() {on_fitness_output.shape} and the expected fitness output {self.last_generation_fitness.shape}.")
1929-
else:
1930-
raise ValueError(f"The output of on_fitness() is expected to be tuple/list/range/numpy.ndarray but {type(on_fitness_output)} found.")
1931-
1932-
# Appending the fitness value of the best solution in the current generation to the best_solutions_fitness attribute.
1933-
self.best_solutions_fitness.append(best_solution_fitness)
1934-
1935-
# Appending the solutions in the current generation to the solutions list.
1936-
if self.save_solutions:
1937-
# self.solutions.extend(self.population.copy())
1938-
population_as_list = self.population.copy()
1939-
population_as_list = [list(item)
1940-
for item in population_as_list]
1941-
self.solutions.extend(population_as_list)
1942-
1943-
self.solutions_fitness.extend(self.last_generation_fitness)
1923+
self.run_loop_head(best_solution_fitness)
19441924

19451925
# Call the 'run_select_parents()' method to select the parents.
19461926
# It edits these 2 instance attributes:
@@ -1964,7 +1944,6 @@ def run(self):
19641944
# 1) population: A NumPy array of the population of solutions/chromosomes.
19651945
self.run_update_population()
19661946

1967-
19681947
# The generations_completed attribute holds the number of the last completed generation.
19691948
self.generations_completed = generation + 1
19701949

@@ -2078,13 +2057,45 @@ def run(self):
20782057
# Converting the 'best_solutions' list into a NumPy array.
20792058
self.best_solutions = numpy.array(self.best_solutions)
20802059

2060+
# Update previous_generation_fitness because it is used to get the fitness of the parents.
2061+
self.previous_generation_fitness = self.last_generation_fitness.copy()
2062+
20812063
# Converting the 'solutions' list into a NumPy array.
20822064
# self.solutions = numpy.array(self.solutions)
20832065
except Exception as ex:
20842066
self.logger.exception(ex)
20852067
# sys.exit(-1)
20862068
raise ex
20872069

2070+
def run_loop_head(self, best_solution_fitness):
2071+
if not (self.on_fitness is None):
2072+
on_fitness_output = self.on_fitness(self,
2073+
self.last_generation_fitness)
2074+
2075+
if on_fitness_output is None:
2076+
pass
2077+
else:
2078+
if type(on_fitness_output) in [tuple, list, numpy.ndarray, range]:
2079+
on_fitness_output = numpy.array(on_fitness_output)
2080+
if on_fitness_output.shape == self.last_generation_fitness.shape:
2081+
self.last_generation_fitness = on_fitness_output
2082+
else:
2083+
raise ValueError(f"Size mismatch between the output of on_fitness() {on_fitness_output.shape} and the expected fitness output {self.last_generation_fitness.shape}.")
2084+
else:
2085+
raise ValueError(f"The output of on_fitness() is expected to be tuple/list/range/numpy.ndarray but {type(on_fitness_output)} found.")
2086+
2087+
# Appending the fitness value of the best solution in the current generation to the best_solutions_fitness attribute.
2088+
self.best_solutions_fitness.append(best_solution_fitness)
2089+
2090+
# Appending the solutions in the current generation to the solutions list.
2091+
if self.save_solutions:
2092+
# self.solutions.extend(self.population.copy())
2093+
population_as_list = self.population.copy()
2094+
population_as_list = [list(item) for item in population_as_list]
2095+
self.solutions.extend(population_as_list)
2096+
2097+
self.solutions_fitness.extend(self.last_generation_fitness)
2098+
20882099
def run_select_parents(self, call_on_parents=True):
20892100
"""
20902101
This method must be only callled from inside the run() method. It is not meant for use by the user.
@@ -2333,6 +2344,7 @@ def best_solution(self, pop_fitness=None):
23332344
-best_solution_fitness: Fitness value of the best solution.
23342345
-best_match_idx: Index of the best solution in the current population.
23352346
"""
2347+
23362348
try:
23372349
if pop_fitness is None:
23382350
# If the 'pop_fitness' parameter is not passed, then we have to call the 'cal_pop_fitness()' method to calculate the fitness of all solutions in the lastest population.

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /