#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Comparators meant to be used in symmetry-constrained genetic
algorithm (SCGA)."""
import bisect
[docs]class GroupSizeComparator(object):
"""For each given element, compares the sorted sizes of the
user-divided groups that have the given element. Returns True
if the sizes are the same, False otherwise. Self-symmetry is
considered for particles.
Parameters
----------
groups : list of lists, default None
The atom indices in each user-divided group. Can be obtained
by `acat.build.ordering.SymmetricClusterOrderingGenerator`
or `acat.build.ordering.SymmetricSlabOrderingGenerator` or
`acat.build.adlayer.SymmetricPatternGenerator`. If not provided
here, please provide the groups in atoms.info['data']['groups']
in all intial structures. This is useful to mix structures with
different group divisions in one GA.
elements : list of strs, default None
Only take into account the elements specified in this list.
Default is to take all elements into account.
"""
def __init__(self, groups=None, elements=None):
self.groups = groups
self.elements = elements
[docs] def looks_like(self, a1, a2):
""" Return if structure a1 or a2 are similar or not. """
if self.groups is None:
groups1 = a1.info['data']['groups']
if groups1 == a2.info['data']['groups']:
groups = groups1
else:
return False
else:
groups = self.groups
size1_dict = {e: [] for e in self.elements}
size2_dict = {e: [] for e in self.elements}
for group in groups:
e1 = a1[group[0]].symbol
bisect.insort(size1_dict[e1], len(group))
e2 = a2[group[0]].symbol
bisect.insort(size2_dict[e2], len(group))
return size1_dict == size2_dict
[docs]class GroupCompositionComparator(object):
"""Compares the elemental compositions of all user-divided groups.
Returns True if the numbers are the same, False otherwise.
Self-symmetry is not considered for particles.
Parameters
----------
groups : list of lists, default None
The atom indices in each user-divided group. Can be obtained
by `acat.build.ordering.SymmetricClusterOrderingGenerator`
or `acat.build.ordering.SymmetricSlabOrderingGenerator` or
`acat.build.adlayer.SymmetricPatternGenerator`. If not provided
here, please provide the groups in atoms.info['data']['groups']
in all intial structures. This is useful to mix structures with
different group divisions in one GA.
elements : list of strs, default None
Only take into account the elements specified in this list.
Default is to take all elements into account.
tol : int, default 0
The maximum number of groups with different elements that two
structures are still considered to be look alike.
"""
def __init__(self, groups=None, elements=None, tol=0):
self.groups = groups
self.elements = elements
self.tol = tol
[docs] def looks_like(self, a1, a2):
""" Return if structure a1 or a2 are similar or not. """
if self.groups is None:
groups1 = a1.info['data']['groups']
if groups1 == a2.info['data']['groups']:
groups = groups1
else:
return False
else:
groups = self.groups
elements = self.elements
if self.elements is None:
e = list(set(a1.get_chemical_symbols()))
else:
e = self.elements
groups = self.groups.copy()
sorted_elems = sorted(set(a1.get_chemical_symbols()))
if e is not None and sorted(e) != sorted_elems:
for group in groups:
torem = []
for i in group:
if a1[i].symbol not in e:
torem.append(i)
for i in torem:
group.remove(i)
diff = [g for g in groups if a1[g[0]].symbol != a2[g[0]].symbol]
return len(diff) <= self.tol