Hide keyboard shortcuts

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 

3 

4 

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 

20 

21 

22class InteratomicDistanceComparator: 

23 

24 """ An implementation of the comparison criteria described in 

25 L.B. Vilhelmsen and B. Hammer, PRL, 108, 126101 (2012) 

26 

27 Parameters: 

28 

29 n_top: The number of atoms being optimized by the GA. 

30 Default 0 - meaning all atoms. 

31 

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 """ 

38 

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 

46 

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') 

51 

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 

56 

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) 

61 

62 return (cum_diff < self.pair_cor_cum_diff 

63 and max_diff < self.pair_cor_max) 

64 

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) 

86 

87 

88class SequentialComparator: 

89 """Use more than one comparison class and test them all in sequence. 

90 

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 """ 

99 

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) 

108 

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) 

115 

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) 

120 

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 

128 

129 

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 """ 

137 

138 def __init__(self, *keys): 

139 self.keys = keys 

140 

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 

146 

147 

148class EnergyComparator: 

149 """Compares the energy of the supplied atoms objects using 

150 get_potential_energy(). 

151 

152 Parameters: 

153 

154 dE: the difference in energy below which two energies are 

155 deemed equal. 

156 """ 

157 

158 def __init__(self, dE=0.02): 

159 self.dE = dE 

160 

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 

167 

168 

169class RawScoreComparator: 

170 """Compares the raw_score of the supplied individuals 

171 objects using a1.info['key_value_pairs']['raw_score']. 

172 

173 Parameters: 

174 

175 dist: the difference in raw_score below which two 

176 scores are deemed equal. 

177 """ 

178 

179 def __init__(self, dist=0.02): 

180 self.dist = dist 

181 

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 

188 

189 

190class NoComparator: 

191 """Returns False always. If you don't want any comparator.""" 

192 

193 def looks_like(self, *args): 

194 return False 

195 

196 

197class AtomsComparator: 

198 """Compares the Atoms objects directly.""" 

199 

200 def looks_like(self, a1, a2): 

201 return a1 == a2 

202 

203 

204class CompositionComparator: 

205 """Compares the composition of the Atoms objects.""" 

206 

207 def looks_like(self, a1, a2): 

208 return a1.get_chemical_formula() == a2.get_chemical_formula()