8000 Resue best solution fitness · Shawmo/GeneticAlgorithmPython@d3b9ec0 · GitHub
[go: up one dir, main page]

Skip to content

Commit d3b9ec0

Browse files
committed
Resue best solution fitness
1 parent fd031d3 commit d3b9ec0

File tree

2 files changed

+109
-4
lines changed

2 files changed

+109
-4
lines changed

docs/source/pygad.rst

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4136,6 +4136,107 @@ and also saved in the text file.
41364136
2023-04-03 19:04:27 INFO: Generation = 10
41374137
2023-04-03 19:04:27 INFO: Fitness = 0.000389832593101348
41384138
4139+
Solve Non-Deterministic Problems
4140+
================================
4141+
4142+
PyGAD can be used to solve both deterministic and non-deterministic
4143+
problems. Deterministic are those that return the same fitness for the
4144+
same solution. For non-deterministic problems, a different fitness value
4145+
would be returned for the same solution.
4146+
4147+
By default, PyGAD settings are set to solve deterministic problems.
4148+
PyGAD can save the explored solutions and their fitness to reuse in the
4149+
future. These instances attributes can save the solutions:
4150+
4151+
1. ``solutions``: Exists if ``save_solutions=True``.
4152+
4153+
2. ``best_solutions``: Exists if ``save_best_solutions=True``.
4154+
4155+
3. ``last_generation_elitism``: Exists if ``keep_elitism`` > 0.
4156+
4157+
4. ``last_generation_parents``: Exists if ``keep_parents`` > 0 or
4158+
``keep_parents=-1``.
4159+
4160+
To configure PyGAD for non-deterministic problems, we have to disable
4161+
saving the previous solutions. This is by setting these parameters:
4162+
4163+
1. ``keep_elisitm=0``
4164+
4165+
2. ``keep_parents=0``
4166+
4167+
3. ``keep_solutions=False``
4168+
4169+
4. ``keep_best_solutions=False``
4170+
4171+
.. code:: python
4172+
4173+
import pygad
4174+
...
4175+
ga_instance = pygad.GA(...,
4176+
keep_elitism=0,
4177+
keep_parents=0,
4178+
save_solutions=False,
4179+
save_best_solutions=False,
4180+
...)
4181+
4182+
This way PyGAD will not save any explored solution and thus the fitness
4183+
function have to be called for each individual solution.
4184+
4185+
Reuse the Fitness instead of Calling the Fitness Function
4186+
=========================================================
4187+
4188+
It may happen that a previously explored solution in generation X is
4189+
explored again in another generation Y (where Y > X). For some problems,
4190+
calling the fitness function takes much time.
4191+
4192+
For deterministic problems, it is better to not call the fitness
4193+
function for an already explored solutions. Instead, reuse the fitness
4194+
of the old solution. PyGAD supports some options to help you save time
4195+
calling the fitness function for a previously explored solution.
4196+
4197+
The parameters explored in this section can be set in the constructor of
4198+
the ``pygad.GA`` class.
4199+
4200+
The ``cal_pop_fitness()`` method of the ``pygad.GA`` class checks these
4201+
parameters to see if there is a possibility of reusing the fitness
4202+
instead of calling the fitness function.
4203+
4204+
.. _1-savesolutions:
4205+
4206+
1. ``save_solutions``
4207+
---------------------
4208+
4209+
It defaults to ``False``. If set to ``True``, then the population of
4210+
each generation is saved into the ``solutions`` attribute of the
4211+
``pygad.GA`` instance. In other words, every single solution is saved in
4212+
the ``solutions`` attribute.
4213+
4214+
.. _2-savebestsolutions:
4215+
4216+
2. ``save_best_solutions``
4217+
--------------------------
4218+
4219+
It defaults to ``False``. If ``True``, then it only saves the best
4220+
solution in every generation.
4221+
4222+
.. _3-keepelitism:
4223+
4224+
3. ``keep_elitism``
4225+
-------------------
4226+
4227+
It accepts an integer and defaults to 1. If set to a positive integer,
4228+
then it keeps the elitism of one generation available in the next
4229+
generation.
4230+
4231+
.. _4-keepparents:
4232+
4233+
4. ``keep_parents``
4234+
-------------------
4235+
4236+
It accepts an integer and defaults to -1. It set to ``-1`` or a positive
4237+
integer, then it keeps the parents of one generation available in the
4238+
next generation.
4239+
41394240
Batch Fitness Calculation
41404241
=========================
41414242

pygad/pygad.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1646,11 +1646,15 @@ def cal_pop_fitness(self):
16461646
# The functions numpy.any()/numpy.all()/numpy.where()/numpy.equal() are very slow.
16471647
# So, list membership operator 'in' is used to check if the solution exists in the 'self.solutions' list.
16481648
# Make sure that both the solution and 'self.solutions' are of type 'list' not 'numpy.ndarray'.
1649-
# if (self.save_solutions) and (len(self.solutions) > 0) and (numpy.any(numpy.all(self.solutions == numpy.array(sol), axis=1))):
1650-
# if (self.save_solutions) and (len(self.solutions) > 0) and (numpy.any(numpy.all(numpy.equal(self.solutions, numpy.array(sol)), axis=1))):
1649+
# if (self.save_solutions) and (len(self.solutions) > 0) and (numpy.any(numpy.all(self.solutions == numpy.array(sol), axis=1)))
1650+
# if (self.save_solutions) and (len(self.solutions) > 0) and (numpy.any(numpy.all(numpy.equal(self.solutions, numpy.array(sol)), axis=1)))
1651+
# print("BBBBBB", len(self.best_solutions))
16511652
if (self.save_solutions) and (len(self.solutions) > 0) and (list(sol) in self.solutions):
16521653
solution_idx = self.solutions.index(list(sol))
16531654
fitness = self.solutions_fitness[solution_idx]
1655+
elif (self.save_best_solutions) and (len(self.best_solutions) > 0) and (list(sol) in self.best_solutions):
1656+
solution_idx = self.best_solutions.index(list(sol))
1657+
fitness = self.best_solutions_fitness[solution_idx]
16541658
elif (self.keep_elitism > 0) and (self.last_generation_elitism is not None) and (len(self.last_generation_elitism) > 0) and (list(sol) in last_generation_elitism_as_list):
16551659
# Return the index of the elitism from the elitism array 'self.last_generation_elitism'.
16561660
# This is not its index within the population. It is just its index in the 'self.last_generation_elitism' array.
@@ -1859,7 +1863,7 @@ def run(self):
18591863

18601864
# Appending the best solution in the initial population to the best_solutions list.
18611865
if self.save_best_solutions:
1862-
self.best_solutions.append(best_solution)
1866+
self.best_solutions.append(list(best_solution))
18631867

18641868
for generation in range(generation_first_idx, generation_last_idx):
18651869
if not (self.on_fitness is None):
@@ -2085,7 +2089,7 @@ def run(self):
20852089

20862090
# Appending the best solution in the current generation to the best_solutions list.
20872091
if self.save_best_solutions:
2088-
self.best_solutions.append(best_solution)
2092+
self.best_solutions.append(list(best_solution))
20892093

20902094
# If the on_generation attribute is not None, then cal the callback function after the generation.
20912095
if not (self.on_generation is None):

0 commit comments

Comments
 (0)
0