Coverage for /builds/ase/ase/ase/ga/standard_comparators.py : 87.27%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1import numpy as np
2from ase.ga import get_raw_score
5def get_sorted_dist_list(atoms, mic=False):
6 """ Utility method used to calculate the sorted distance list
7 describing the cluster in atoms. """
8 numbers = atoms.numbers
9 unique_types = set(numbers)
10 pair_cor = dict()
11 for n in unique_types:
12 i_un = [i for i in range(len(atoms)) if atoms[i].number == n]
13 d = []
14 for i, n1 in enumerate(i_un):
15 for n2 in i_un[i + 1:]:
16 d.append(atoms.get_distance(n1, n2, mic))
17 d.sort()
18 pair_cor[n] = np.array(d)
19 return pair_cor
22class InteratomicDistanceComparator:
24 """ An implementation of the comparison criteria described in
25 L.B. Vilhelmsen and B. Hammer, PRL, 108, 126101 (2012)
27 Parameters:
29 n_top: The number of atoms being optimized by the GA.
30 Default 0 - meaning all atoms.
32 pair_cor_cum_diff: The limit in eq. 2 of the letter.
33 pair_cor_max: The limit in eq. 3 of the letter
34 dE: The limit of eq. 1 of the letter
35 mic: Determines if distances are calculated
36 using the minimum image convention
37 """
39 def __init__(self, n_top=None, pair_cor_cum_diff=0.015,
40 pair_cor_max=0.7, dE=0.02, mic=False):
41 self.pair_cor_cum_diff = pair_cor_cum_diff
42 self.pair_cor_max = pair_cor_max
43 self.dE = dE
44 self.n_top = n_top or 0
45 self.mic = mic
47 def looks_like(self, a1, a2):
48 """ Return if structure a1 or a2 are similar or not. """
49 if len(a1) != len(a2):
50 raise Exception('The two configurations are not the same size')
52 # first we check the energy criteria
53 dE = abs(a1.get_potential_energy() - a2.get_potential_energy())
54 if dE >= self.dE:
55 return False
57 # then we check the structure
58 a1top = a1[-self.n_top:]
59 a2top = a2[-self.n_top:]
60 cum_diff, max_diff = self.__compare_structure__(a1top, a2top)
62 return (cum_diff < self.pair_cor_cum_diff
63 and max_diff < self.pair_cor_max)
65 def __compare_structure__(self, a1, a2):
66 """ Private method for calculating the structural difference. """
67 p1 = get_sorted_dist_list(a1, mic=self.mic)
68 p2 = get_sorted_dist_list(a2, mic=self.mic)
69 numbers = a1.numbers
70 total_cum_diff = 0.
71 max_diff = 0
72 for n in p1.keys():
73 cum_diff = 0.
74 c1 = p1[n]
75 c2 = p2[n]
76 assert len(c1) == len(c2)
77 if len(c1) == 0:
78 continue
79 t_size = np.sum(c1)
80 d = np.abs(c1 - c2)
81 cum_diff = np.sum(d)
82 max_diff = np.max(d)
83 ntype = float(sum([i == n for i in numbers]))
84 total_cum_diff += cum_diff / t_size * ntype / float(len(numbers))
85 return (total_cum_diff, max_diff)
88class SequentialComparator:
89 """Use more than one comparison class and test them all in sequence.
91 Supply a list of integers if for example two comparison tests both
92 need to be positive if two atoms objects are truly equal.
93 Ex:
94 methods = [a, b, c, d], logics = [0, 1, 1, 2]
95 if a or d is positive -> return True
96 if b and c are positive -> return True
97 if b and not c are positive (or vice versa) -> return False
98 """
100 def __init__(self, methods, logics=None):
101 if not isinstance(methods, list):
102 methods = [methods]
103 if logics is None:
104 logics = [i for i in range(len(methods))]
105 if not isinstance(logics, list):
106 logics = [logics]
107 assert len(logics) == len(methods)
109 self.methods = []
110 self.logics = []
111 for m, l in zip(methods, logics):
112 if hasattr(m, 'looks_like'):
113 self.methods.append(m)
114 self.logics.append(l)
116 def looks_like(self, a1, a2):
117 mdct = dict((logic, []) for logic in self.logics)
118 for m, logic in zip(self.methods, self.logics):
119 mdct[logic].append(m)
121 for methods in mdct.values():
122 for m in methods:
123 if not m.looks_like(a1, a2):
124 break
125 else:
126 return True
127 return False
130class StringComparator:
131 """Compares the calculated hash strings. These strings should be stored
132 in atoms.info['key_value_pairs'][key1] and
133 atoms.info['key_value_pairs'][key2] ...
134 where the keys should be supplied as parameters i.e.
135 StringComparator(key1, key2, ...)
136 """
138 def __init__(self, *keys):
139 self.keys = keys
141 def looks_like(self, a1, a2):
142 for k in self.keys:
143 if a1.info['key_value_pairs'][k] == a2.info['key_value_pairs'][k]:
144 return True
145 return False
148class EnergyComparator:
149 """Compares the energy of the supplied atoms objects using
150 get_potential_energy().
152 Parameters:
154 dE: the difference in energy below which two energies are
155 deemed equal.
156 """
158 def __init__(self, dE=0.02):
159 self.dE = dE
161 def looks_like(self, a1, a2):
162 dE = abs(a1.get_potential_energy() - a2.get_potential_energy())
163 if dE >= self.dE:
164 return False
165 else:
166 return True
169class RawScoreComparator:
170 """Compares the raw_score of the supplied individuals
171 objects using a1.info['key_value_pairs']['raw_score'].
173 Parameters:
175 dist: the difference in raw_score below which two
176 scores are deemed equal.
177 """
179 def __init__(self, dist=0.02):
180 self.dist = dist
182 def looks_like(self, a1, a2):
183 d = abs(get_raw_score(a1) - get_raw_score(a2))
184 if d >= self.dist:
185 return False
186 else:
187 return True
190class NoComparator:
191 """Returns False always. If you don't want any comparator."""
193 def looks_like(self, *args):
194 return False
197class AtomsComparator:
198 """Compares the Atoms objects directly."""
200 def looks_like(self, a1, a2):
201 return a1 == a2
204class CompositionComparator:
205 """Compares the composition of the Atoms objects."""
207 def looks_like(self, a1, a2):
208 return a1.get_chemical_formula() == a2.get_chemical_formula()