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 def __init__(self, n_top=None, pair_cor_cum_diff=0.015, 

39 pair_cor_max=0.7, dE=0.02, mic=False): 

40 self.pair_cor_cum_diff = pair_cor_cum_diff 

41 self.pair_cor_max = pair_cor_max 

42 self.dE = dE 

43 self.n_top = n_top or 0 

44 self.mic = mic 

45 

46 def looks_like(self, a1, a2): 

47 """ Return if structure a1 or a2 are similar or not. """ 

48 if len(a1) != len(a2): 

49 raise Exception('The two configurations are not the same size') 

50 

51 # first we check the energy criteria 

52 dE = abs(a1.get_potential_energy() - a2.get_potential_energy()) 

53 if dE >= self.dE: 

54 return False 

55 

56 # then we check the structure 

57 a1top = a1[-self.n_top:] 

58 a2top = a2[-self.n_top:] 

59 cum_diff, max_diff = self.__compare_structure__(a1top, a2top) 

60 

61 return (cum_diff < self.pair_cor_cum_diff 

62 and max_diff < self.pair_cor_max) 

63 

64 def __compare_structure__(self, a1, a2): 

65 """ Private method for calculating the structural difference. """ 

66 p1 = get_sorted_dist_list(a1, mic=self.mic) 

67 p2 = get_sorted_dist_list(a2, mic=self.mic) 

68 numbers = a1.numbers 

69 total_cum_diff = 0. 

70 max_diff = 0 

71 for n in p1.keys(): 

72 cum_diff = 0. 

73 c1 = p1[n] 

74 c2 = p2[n] 

75 assert len(c1) == len(c2) 

76 if len(c1) == 0: 

77 continue 

78 t_size = np.sum(c1) 

79 d = np.abs(c1 - c2) 

80 cum_diff = np.sum(d) 

81 max_diff = np.max(d) 

82 ntype = float(sum([i == n for i in numbers])) 

83 total_cum_diff += cum_diff / t_size * ntype / float(len(numbers)) 

84 return (total_cum_diff, max_diff) 

85 

86 

87class SequentialComparator: 

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

89 

90 Supply a list of integers if for example two comparison tests both 

91 need to be positive if two atoms objects are truly equal. 

92 Ex: 

93 methods = [a, b, c, d], logics = [0, 1, 1, 2] 

94 if a or d is positive -> return True 

95 if b and c are positive -> return True 

96 if b and not c are positive (or vice versa) -> return False 

97 """ 

98 def __init__(self, methods, logics=None): 

99 if not isinstance(methods, list): 

100 methods = [methods] 

101 if logics is None: 

102 logics = [i for i in range(len(methods))] 

103 if not isinstance(logics, list): 

104 logics = [logics] 

105 assert len(logics) == len(methods) 

106 

107 self.methods = [] 

108 self.logics = [] 

109 for m, l in zip(methods, logics): 

110 if hasattr(m, 'looks_like'): 

111 self.methods.append(m) 

112 self.logics.append(l) 

113 

114 def looks_like(self, a1, a2): 

115 mdct = dict((l, []) for l in self.logics) 

116 for m, l in zip(self.methods, self.logics): 

117 mdct[l].append(m) 

118 

119 for methods in mdct.values(): 

120 for m in methods: 

121 if not m.looks_like(a1, a2): 

122 break 

123 else: 

124 return True 

125 return False 

126 

127 

128class StringComparator: 

129 """Compares the calculated hash strings. These strings should be stored 

130 in atoms.info['key_value_pairs'][key1] and 

131 atoms.info['key_value_pairs'][key2] ... 

132 where the keys should be supplied as parameters i.e. 

133 StringComparator(key1, key2, ...) 

134 """ 

135 def __init__(self, *keys): 

136 self.keys = keys 

137 

138 def looks_like(self, a1, a2): 

139 for k in self.keys: 

140 if a1.info['key_value_pairs'][k] == a2.info['key_value_pairs'][k]: 

141 return True 

142 return False 

143 

144 

145class EnergyComparator: 

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

147 get_potential_energy(). 

148 

149 Parameters: 

150 

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

152 deemed equal. 

153 """ 

154 def __init__(self, dE=0.02): 

155 self.dE = dE 

156 

157 def looks_like(self, a1, a2): 

158 dE = abs(a1.get_potential_energy() - a2.get_potential_energy()) 

159 if dE >= self.dE: 

160 return False 

161 else: 

162 return True 

163 

164 

165class RawScoreComparator: 

166 """Compares the raw_score of the supplied individuals 

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

168 

169 Parameters: 

170 

171 dist: the difference in raw_score below which two 

172 scores are deemed equal. 

173 """ 

174 def __init__(self, dist=0.02): 

175 self.dist = dist 

176 

177 def looks_like(self, a1, a2): 

178 d = abs(get_raw_score(a1) - get_raw_score(a2)) 

179 if d >= self.dist: 

180 return False 

181 else: 

182 return True 

183 

184 

185class NoComparator: 

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

187 def looks_like(self, *args): 

188 return False 

189 

190 

191class AtomsComparator: 

192 """Compares the Atoms objects directly.""" 

193 def looks_like(self, a1, a2): 

194 return a1 == a2 

195 

196 

197class CompositionComparator: 

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

199 def looks_like(self, a1, a2): 

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