# fmt: off
"""Crossover classes, that cross the elements in the supplied
atoms objects.
"""
import numpy as np
from ase_ga.offspring_creator import OffspringCreator
[docs]
class ElementCrossover(OffspringCreator):
"""Base class for all operators where the elements of
the atoms objects cross.
"""
def __init__(self, element_pool, max_diff_elements,
min_percentage_elements, verbose, rng=np.random):
OffspringCreator.__init__(self, verbose, rng=rng)
if not isinstance(element_pool[0], (list, np.ndarray)):
self.element_pools = [element_pool]
else:
self.element_pools = element_pool
if max_diff_elements is None:
self.max_diff_elements = [None for _ in self.element_pools]
elif isinstance(max_diff_elements, int):
self.max_diff_elements = [max_diff_elements]
else:
self.max_diff_elements = max_diff_elements
assert len(self.max_diff_elements) == len(self.element_pools)
if min_percentage_elements is None:
self.min_percentage_elements = [0 for _ in self.element_pools]
elif isinstance(min_percentage_elements, (int, float)):
self.min_percentage_elements = [min_percentage_elements]
else:
self.min_percentage_elements = min_percentage_elements
assert len(self.min_percentage_elements) == len(self.element_pools)
self.min_inputs = 2
[docs]
def get_new_individual(self, parents):
raise NotImplementedError
[docs]
class OnePointElementCrossover(ElementCrossover):
"""Crossover of the elements in the atoms objects. Point of cross
is chosen randomly.
Parameters:
element_pool: List of elements in the phase space. The elements can be
grouped if the individual consist of different types of elements.
The list should then be a list of lists e.g. [[list1], [list2]]
max_diff_elements: The maximum number of different elements in the
individual. Default is infinite. If the elements are grouped
max_diff_elements should be supplied as a list with each input
corresponding to the elements specified in the same input in
element_pool.
min_percentage_elements: The minimum percentage of any element in
the individual. Default is any number is allowed. If the elements
are grouped min_percentage_elements should be supplied as a list
with each input corresponding to the elements specified in the
same input in element_pool.
Example: element_pool=[[A,B,C,D],[x,y,z]], max_diff_elements=[3,2],
min_percentage_elements=[.25, .5]
An individual could be "D,B,B,C,x,x,x,x,z,z,z,z"
rng: Random number generator
By default numpy.random.
"""
def __init__(self, element_pool, max_diff_elements=None,
min_percentage_elements=None, verbose=False, rng=np.random):
ElementCrossover.__init__(self, element_pool, max_diff_elements,
min_percentage_elements, verbose, rng=rng)
self.descriptor = 'OnePointElementCrossover'
[docs]
def get_new_individual(self, parents):
f, m = parents
indi = self.initialize_individual(f)
indi.info['data']['parents'] = [i.info['confid'] for i in parents]
cut_choices = [i for i in range(1, len(f) - 1)]
self.rng.shuffle(cut_choices)
for cut in cut_choices:
fsyms = f.get_chemical_symbols()
msyms = m.get_chemical_symbols()
syms = fsyms[:cut] + msyms[cut:]
ok = True
for i, e in enumerate(self.element_pools):
elems = e[:]
elems_in, indices_in = zip(*[(a.symbol, a.index) for a in f
if a.symbol in elems])
max_diff_elem = self.max_diff_elements[i]
min_percent_elem = self.min_percentage_elements[i]
if min_percent_elem == 0:
min_percent_elem = 1. / len(elems_in)
if max_diff_elem is None:
max_diff_elem = len(elems_in)
syms_in = [syms[i] for i in indices_in]
for s in set(syms_in):
percentage = syms_in.count(s) / float(len(syms_in))
if percentage < min_percent_elem:
ok = False
break
num_diff = len(set(syms_in))
if num_diff > max_diff_elem:
ok = False
break
if not ok:
break
if ok:
break
# Sufficient or does some individuals appear
# below min_percentage_elements
for a in f[:cut] + m[cut:]:
indi.append(a)
parent_message = ':Parents {} {}'.format(f.info['confid'],
m.info['confid'])
return (self.finalize_individual(indi),
self.descriptor + parent_message)
[docs]
class TwoPointElementCrossover(ElementCrossover):
"""Crosses two individuals by choosing two cross points
at random"""
def __init__(self, element_pool, max_diff_elements=None,
min_percentage_elements=None, verbose=False):
ElementCrossover.__init__(self, element_pool,
max_diff_elements,
min_percentage_elements, verbose)
self.descriptor = 'TwoPointElementCrossover'
[docs]
def get_new_individual(self, parents):
raise NotImplementedError