Skip to content

Commit 262463e

Browse files
committed
Test number of callbacks calls
1 parent 6e19c04 commit 262463e

File tree

2 files changed

+226
-8
lines changed

2 files changed

+226
-8
lines changed

tests/test_lifecycle.py

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
import pygad
2+
3+
num_generations = 100
4+
5+
def number_lifecycle_callback_functions_calls(stop_criteria=None,
6+
on_generation_stop=None):
7+
actual_num_callbacks_calls = 0
8+
9+
def fitness_func(ga_instanse, solution, solution_idx):
10+
return 1
11+
12+
def on_start(ga_instance):
13+
nonlocal actual_num_callbacks_calls
14+
actual_num_callbacks_calls = actual_num_callbacks_calls + 1
15+
16+
def on_fitness(ga_instance, population_fitness):
17+
nonlocal actual_num_callbacks_calls
18+
actual_num_callbacks_calls = actual_num_callbacks_calls + 1
19+
20+
def on_parents(ga_instance, selected_parents):
21+
nonlocal actual_num_callbacks_calls
22+
actual_num_callbacks_calls = actual_num_callbacks_calls + 1
23+
24+
def on_crossover(ga_instance, offspring_crossover):
25+
nonlocal actual_num_callbacks_calls
26+
actual_num_callbacks_calls = actual_num_callbacks_calls + 1
27+
28+
def on_mutation(ga_instance, offspring_mutation):
29+
nonlocal actual_num_callbacks_calls
30+
actual_num_callbacks_calls = actual_num_callbacks_calls + 1
31+
32+
def on_generation(ga_instance):
33+
nonlocal actual_num_callbacks_calls
34+
actual_num_callbacks_calls = actual_num_callbacks_calls + 1
35+
36+
if on_generation_stop:
37+
if ga_instance.generations_completed == on_generation_stop:
38+
return "stop"
39+
40+
def on_stop(ga_instance, last_population_fitness):
41+
nonlocal actual_num_callbacks_calls
42+
actual_num_callbacks_calls = actual_num_callbacks_calls + 1
43+
44+
ga_instance = pygad.GA(num_generations=num_generations,
45+
num_parents_mating=5,
46+
fitness_func=fitness_func,
47+
sol_per_pop=10,
48+
num_genes=5,
49+
on_start=on_start,
50+
on_fitness=on_fitness,
51+
on_parents=on_parents,
52+
on_crossover=on_crossover,
53+
on_mutation=on_mutation,
54+
on_generation=on_generation,
55+
on_stop=on_stop,
56+
stop_criteria=stop_criteria,
57+
suppress_warnings=True)
58+
59+
ga_instance.run()
60+
61+
# The total number is:
62+
# 1 [for on_start()] +
63+
# num_generations [for on_fitness()] +
64+
# num_generations [for on_parents()] +
65+
# num_generations [for on_crossover()] +
66+
# num_generations [for on_mutation()] +
67+
# num_generations [for on_generation()] +
68+
# 1 [for on_stop()]
69+
# = 1 + num_generations * 5 + 1
70+
71+
# Use 'generations_completed' instead of 'num_generations' because the evolution may stops in the on_generation() callback.
72+
expected_num_callbacks_calls = 1 + ga_instance.generations_completed * 5 + 1
73+
74+
print("Expected number of callbacks calls is {expected_num_callbacks_calls}.".format(expected_num_callbacks_calls=expected_num_callbacks_calls))
75+
print("Actual number of callbacks calls is {actual_num_callbacks_calls}.".format(actual_num_callbacks_calls=actual_num_callbacks_calls))
76+
return actual_num_callbacks_calls, expected_num_callbacks_calls
77+
78+
def number_lifecycle_callback_methods_calls(stop_criteria=None,
79+
on_generation_stop=None):
80+
actual_num_callbacks_calls = 0
81+
82+
class Callbacks:
83+
def fitness_func(self, ga_instanse, solution, solution_idx):
84+
return 1
85+
86+
def on_start(self, ga_instance):
87+
nonlocal actual_num_callbacks_calls
88+
actual_num_callbacks_calls = actual_num_callbacks_calls + 1
89+
90+
def on_fitness(self, ga_instance, population_fitness):
91+
nonlocal actual_num_callbacks_calls
92+
actual_num_callbacks_calls = actual_num_callbacks_calls + 1
93+
94+
def on_parents(self, ga_instance, selected_parents):
95+
nonlocal actual_num_callbacks_calls
96+
actual_num_callbacks_calls = actual_num_callbacks_calls + 1
97+
98+
def on_crossover(self, ga_instance, offspring_crossover):
99+
nonlocal actual_num_callbacks_calls
100+
actual_num_callbacks_calls = actual_num_callbacks_calls + 1
101+
102+
def on_mutation(self, ga_instance, offspring_mutation):
103+
nonlocal actual_num_callbacks_calls
104+
actual_num_callbacks_calls = actual_num_callbacks_calls + 1
105+
106+
def on_generation(self, ga_instance):
107+
nonlocal actual_num_callbacks_calls
108+
actual_num_callbacks_calls = actual_num_callbacks_calls + 1
109+
110+
if on_generation_stop:
111+
if ga_instance.generations_completed == on_generation_stop:
112+
return "stop"
113+
114+
def on_stop(self, ga_instance, last_population_fitness):
115+
nonlocal actual_num_callbacks_calls
116+
actual_num_callbacks_calls = actual_num_callbacks_calls + 1
117+
118+
Callbacks_obj = Callbacks()
119+
ga_instance = pygad.GA(num_generations=num_generations,
120+
num_parents_mating=5,
121+
fitness_func=Callbacks_obj.fitness_func,
122+
sol_per_pop=10,
123+
num_genes=5,
124+
on_start=Callbacks_obj.on_start,
125+
on_fitness=Callbacks_obj.on_fitness,
126+
on_parents=Callbacks_obj.on_parents,
127+
on_crossover=Callbacks_obj.on_crossover,
128+
on_mutation=Callbacks_obj.on_mutation,
129+
on_generation=Callbacks_obj.on_generation,
130+
on_stop=Callbacks_obj.on_stop,
131+
stop_criteria=stop_criteria,
132+
suppress_warnings=True)
133+
134+
ga_instance.run()
135+
136+
# The total number is:
137+
# 1 [for on_start()] +
138+
# num_generations [for on_fitness()] +
139+
# num_generations [for on_parents()] +
140+
# num_generations [for on_crossover()] +
141+
# num_generations [for on_mutation()] +
142+
# num_generations [for on_generation()] +
143+
# 1 [for on_stop()]
144+
# = 1 + num_generations * 5 + 1
145+
146+
# Use 'generations_completed' instead of 'num_generations' because the evolution may stops in the on_generation() callback.
147+
expected_num_callbacks_calls = 1 + ga_instance.generations_completed * 5 + 1
148+
149+
print("Expected number of callbacks calls is {expected_num_callbacks_calls}.".format(expected_num_callbacks_calls=expected_num_callbacks_calls))
150+
print("Actual number of callbacks calls is {actual_num_callbacks_calls}.".format(actual_num_callbacks_calls=actual_num_callbacks_calls))
151+
return actual_num_callbacks_calls, expected_num_callbacks_calls
152+
153+
def test_number_lifecycle_callback_functions_calls():
154+
actual, expected = number_lifecycle_callback_functions_calls()
155+
156+
assert actual == expected
157+
158+
def test_number_lifecycle_callback_functions_calls_stop_criteria():
159+
actual, expected = number_lifecycle_callback_functions_calls(on_generation_stop=30)
160+
161+
assert actual == expected
162+
163+
def test_number_lifecycle_callback_methods_calls():
164+
actual, expected = number_lifecycle_callback_methods_calls()
165+
166+
assert actual == expected
167+
168+
def test_number_lifecycle_callback_methods_calls_stop_criteria():
169+
actual, expected = number_lifecycle_callback_methods_calls(on_generation_stop=30)
170+
171+
assert actual == expected
172+
173+
if __name__ == "__main__":
174+
print()
175+
test_number_lifecycle_callback_functions_calls()
176+
print()
177+
test_number_lifecycle_callback_functions_calls_stop_criteria()
178+
print()
179+
test_number_lifecycle_callback_methods_calls()
180+
print()
181+
test_number_lifecycle_callback_methods_calls_stop_criteria()
182+
print()

tests/test_number_fitness_function_calls.py

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@
99
sol_per_pop = 10
1010
num_parents_mating = 5
1111

12-
def number_calls_fitness_function(keep_elitism,
13-
keep_parents):
12+
def number_calls_fitness_function(keep_elitism=1,
13+
keep_parents=-1,
14+
mutation_type="random",
15+
mutation_percent_genes="default"):
1416

1517
actual_num_fitness_calls = 0
1618
def fitness_func(ga, solution, idx):
@@ -23,6 +25,8 @@ def fitness_func(ga, solution, idx):
2325
num_genes=6,
2426
num_parents_mating=num_parents_mating,
2527
fitness_func=fitness_func,
28+
mutation_type=mutation_type,
29+
mutation_percent_genes=mutation_percent_genes,
2630
keep_elitism=keep_elitism,
2731
keep_parents=keep_parents,
2832
suppress_warnings=True)
@@ -32,43 +36,69 @@ def fitness_func(ga, solution, idx):
3236
if keep_parents == 0:
3337
# 10 (for initial population) + 100*10 (for other generations) = 1010
3438
expected_num_fitness_calls = sol_per_pop + num_generations * sol_per_pop
39+
if mutation_type == "adaptive":
40+
expected_num_fitness_calls += num_generations * sol_per_pop
3541
elif keep_parents == -1:
3642
# 10 (for initial population) + 100*num_parents_mating (for other generations)
3743
expected_num_fitness_calls = sol_per_pop + num_generations * (sol_per_pop - num_parents_mating)
44+
if mutation_type == "adaptive":
45+
expected_num_fitness_calls += num_generations * (sol_per_pop - num_parents_mating)
3846
else:
3947
# 10 (for initial population) + 100*keep_parents (for other generations)
4048
expected_num_fitness_calls = sol_per_pop + num_generations * (sol_per_pop - keep_parents)
49+
if mutation_type == "adaptive":
50+
expected_num_fitness_calls += num_generations * (sol_per_pop - keep_parents)
4151
else:
4252
# 10 (for initial population) + 100*keep_elitism (for other generations)
4353
expected_num_fitness_calls = sol_per_pop + num_generations * (sol_per_pop - keep_elitism)
54+
if mutation_type == "adaptive":
55+
expected_num_fitness_calls += num_generations * (sol_per_pop - keep_elitism)
4456

4557
print("Expected number of fitness function calls is {expected_num_fitness_calls}.".format(expected_num_fitness_calls=expected_num_fitness_calls))
4658
print("Actual number of fitness function calls is {actual_num_fitness_calls}.".format(actual_num_fitness_calls=actual_num_fitness_calls))
4759
return actual_num_fitness_calls, expected_num_fitness_calls
4860

4961
def test_number_calls_fitness_function_default_keep():
50-
actual, expected = number_calls_fitness_function(keep_elitism=1,
51-
keep_parents=-1)
62+
actual, expected = number_calls_fitness_function()
5263
assert actual == expected
5364

5465
def test_number_calls_fitness_function_no_keep():
5566
actual, expected = number_calls_fitness_function(keep_elitism=0,
56-
keep_parents=0)
67+
keep_parents=0)
5768
assert actual == expected
5869

5970
def test_number_calls_fitness_function_keep_elitism():
6071
actual, expected = number_calls_fitness_function(keep_elitism=3,
61-
keep_parents=0)
72+
keep_parents=0)
6273
assert actual == expected
6374

6475
def test_number_calls_fitness_function_keep_parents():
6576
actual, expected = number_calls_fitness_function(keep_elitism=0,
66-
keep_parents=4)
77+
keep_parents=4)
6778
assert actual == expected
6879

6980
def test_number_calls_fitness_function_both_keep():
7081
actual, expected = number_calls_fitness_function(keep_elitism=3,
71-
keep_parents=4)
82+
keep_parents=4)
83+
assert actual == expected
84+
85+
def test_number_calls_fitness_function_no_keep_adaptive_mutation():
86+
actual, expected = number_calls_fitness_function(keep_elitism=0,
87+
keep_parents=0,
88+
mutation_type="adaptive",
89+
mutation_percent_genes=[10, 5])
90+
assert actual == expected
91+
92+
def test_number_calls_fitness_function_default_adaptive_mutation():
93+
actual, expected = number_calls_fitness_function(mutation_type="adaptive",
94+
mutation_percent_genes=[10, 5])
95+
assert actual == expected
96+
97+
def test_number_calls_fitness_function_both_keep_adaptive_mutation():
98+
actual, expected = number_calls_fitness_function(keep_elitism=3,
99+
keep_parents=4,
100+
mutation_type="adaptive",
101+
mutation_percent_genes=[10, 5])
72102
assert actual == expected
73103

74104
if __name__ == "__main__":
@@ -83,3 +113,9 @@ def test_number_calls_fitness_function_both_keep():
83113
print()
84114
test_number_calls_fitness_function_both_keep()
85115
print()
116+
test_number_calls_fitness_function_no_keep_adaptive_mutation()
117+
print()
118+
test_number_calls_fitness_function_default_adaptive_mutation()
119+
print()
120+
test_number_calls_fitness_function_both_keep_adaptive_mutation()
121+
print()

0 commit comments

Comments
 (0)