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 """
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
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')
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
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)
61 return (cum_diff < self.pair_cor_cum_diff
62 and max_diff < self.pair_cor_max)
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)
87class SequentialComparator:
88 """Use more than one comparison class and test them all in sequence.
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)
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)
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)
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
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
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
145class EnergyComparator:
146 """Compares the energy of the supplied atoms objects using
147 get_potential_energy().
149 Parameters:
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
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
165class RawScoreComparator:
166 """Compares the raw_score of the supplied individuals
167 objects using a1.info['key_value_pairs']['raw_score'].
169 Parameters:
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
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
185class NoComparator:
186 """Returns False always. If you don't want any comparator."""
187 def looks_like(self, *args):
188 return False
191class AtomsComparator:
192 """Compares the Atoms objects directly."""
193 def looks_like(self, a1, a2):
194 return a1 == a2
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()