Coverage for /builds/ase/ase/ase/ga/convergence.py: 26.42%
53 statements
« prev ^ index » next coverage.py v7.5.3, created at 2025-08-02 00:12 +0000
« prev ^ index » next coverage.py v7.5.3, created at 2025-08-02 00:12 +0000
1# fmt: off
3"""Classes that determine convergence of an algorithm run
4based on population stagnation or max raw score reached"""
5from ase.ga import get_raw_score
8class Convergence:
9 """
10 Base class for all convergence object to be based on.
11 It is necessary to supply the population instance, to be
12 able to obtain current and former populations.
13 """
15 def __init__(self, population_instance):
16 self.pop = population_instance
17 self.pops = {}
19 def converged(self):
20 """This function is called to find out if the algorithm
21 run has converged, it should return True or False.
22 Overwrite this in the inherited class."""
23 raise NotImplementedError
25 def populate_pops(self, to_gen):
26 """Populate the pops dictionary with how the population
27 looked after i number of generations."""
28 for i in range(to_gen):
29 if i not in self.pops.keys():
30 self.pops[i] = self.pop.get_population_after_generation(i)
33class GenerationRepetitionConvergence(Convergence):
34 """Returns True if the latest finished population is stagnated for
35 number_of_generations.
37 Parameters:
39 number_of_generations: int
40 How many generations need to be equal before convergence.
42 number_of_individuals: int
43 How many of the fittest individuals should be included in the
44 convergence test. Default is -1 meaning all in the population.
46 max_generations: int
47 The maximum number of generations the GA is allowed to run.
48 Default is indefinite.
49 """
51 def __init__(self, population_instance, number_of_generations,
52 number_of_individuals=-1, max_generations=100000000):
53 Convergence.__init__(self, population_instance)
54 self.numgens = number_of_generations
55 self.numindis = number_of_individuals
56 self.maxgen = max_generations
58 def converged(self):
59 size = self.pop.pop_size
60 cur_gen_num = self.pop.dc.get_generation_number(size)
62 if cur_gen_num >= self.maxgen:
63 return True
65 if cur_gen_num <= 1:
66 return False
68 cur_pop = self.pop.get_current_population()
69 newest = max(
70 i.info['key_value_pairs']['generation']
71 for i in cur_pop[: self.numindis]
72 )
73 if newest + self.numgens > cur_gen_num:
74 return False
76 self.populate_pops(cur_gen_num)
78 duplicate_gens = 1
79 latest_pop = self.pops[cur_gen_num - 1]
80 for i in range(cur_gen_num - 2, -1, -1):
81 test_pop = self.pops[i]
82 if test_pop[:self.numindis] == latest_pop[:self.numindis]:
83 duplicate_gens += 1
84 if duplicate_gens >= self.numgens:
85 return True
86 return False
89class RawScoreConvergence(Convergence):
90 """Returns True if the supplied max_raw_score has been reached"""
92 def __init__(self, population_instance, max_raw_score, eps=1e-3):
93 Convergence.__init__(self, population_instance)
94 self.max_raw_score = max_raw_score
95 self.eps = eps
97 def converged(self):
98 cur_pop = self.pop.get_current_population()
99 if abs(get_raw_score(cur_pop[0]) - self.max_raw_score) <= self.eps:
100 return True
101 return False
104class NeverConvergence:
105 """Test class that never converges."""
107 def __init__(self):
108 pass
110 def converged(self):
111 return False