r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1"""Base module for all operators that create offspring."""

2import numpy as np

4from ase import Atoms

7class OffspringCreator:

8 """Base class for all procreation operators

10 Parameters:

12 verbose: Be verbose and print some stuff

14 rng: Random number generator

15 By default numpy.random.

16 """

18 def __init__(self, verbose=False, num_muts=1, rng=np.random):

19 self.descriptor = 'OffspringCreator'

20 self.verbose = verbose

21 self.min_inputs = 0

22 self.num_muts = num_muts

23 self.rng = rng

25 def get_min_inputs(self):

26 """Returns the number of inputs required for a mutation,

27 this is to know how many candidates should be selected

28 from the population."""

29 return self.min_inputs

31 def get_new_individual(self, parents):

32 """Function that returns a new individual.

33 Overwrite in subclass."""

34 raise NotImplementedError

36 def finalize_individual(self, indi):

37 """Call this function just before returning the new individual"""

38 indi.info['key_value_pairs']['origin'] = self.descriptor

40 return indi

42 @classmethod

43 def initialize_individual(cls, parent, indi=None):

44 """Initializes a new individual that inherits some parameters

45 from the parent, and initializes the info dictionary.

46 If the new individual already has more structure it can be

47 supplied in the parameter indi."""

48 if indi is None:

49 indi = Atoms(pbc=parent.get_pbc(), cell=parent.get_cell())

50 else:

51 indi = indi.copy()

52 # key_value_pairs for numbers and strings

53 indi.info['key_value_pairs'] = {'extinct': 0}

54 # data for lists and the like

55 indi.info['data'] = {}

57 return indi

60class OperationSelector:

61 """Class used to randomly select a procreation operation

62 from a list of operations.

64 Parameters:

66 probabilities: A list of probabilities with which the different

67 mutations should be selected. The norm of this list

68 does not need to be 1.

70 oplist: The list of operations to select from.

72 rng: Random number generator

73 By default numpy.random.

74 """

76 def __init__(self, probabilities, oplist, rng=np.random):

77 assert len(probabilities) == len(oplist)

78 self.oplist = oplist

79 self.rho = np.cumsum(probabilities)

80 self.rng = rng

82 def __get_index__(self):

83 v = self.rng.random() * self.rho[-1]

84 for i in range(len(self.rho)):

85 if self.rho[i] > v:

86 return i

88 def get_new_individual(self, candidate_list):

89 """Choose operator and use it on the candidate. """

90 to_use = self.__get_index__()

91 return self.oplist[to_use].get_new_individual(candidate_list)

93 def get_operator(self):

94 """Choose operator and return it."""

95 to_use = self.__get_index__()

96 return self.oplist[to_use]

99class CombinationMutation(OffspringCreator):

100 """Combine two or more mutations into one operation.

102 Parameters:

104 mutations: Operator instances

105 Supply two or more mutations that will applied one after the other

106 as one mutation operation. The order of the supplied mutations prevail

107 when applying the mutations.

109 """

111 def __init__(self, *mutations, verbose=False):

112 super(CombinationMutation, self).__init__(verbose=verbose)

113 self.descriptor = 'CombinationMutation'

115 # Check that a combination mutation makes sense

116 msg = "Too few operators supplied to a CombinationMutation"

117 assert len(mutations) > 1, msg

119 self.operators = mutations

121 def get_new_individual(self, parents):

122 f = parents[0]

124 indi = self.mutate(f)

125 if indi is None:

126 return indi, 'mutation: {}'.format(self.descriptor)

128 indi = self.initialize_individual(f, indi)

129 indi.info['data']['parents'] = [f.info['confid']]

131 return (self.finalize_individual(indi),

132 'mutation: {}'.format(self.descriptor))

134 def mutate(self, atoms):

135 """Perform the mutations one at a time."""

136 for op in self.operators:

137 if atoms is not None:

138 atoms = op.mutate(atoms)

139 return atoms