def cross(self, father: IntIndividual, mother: IntIndividual, random_state=None): ''' Cross chromsomes of parent using uniform crossover method. :param population: Population where the selection operation occurs. :type population: :obj:`gaft.components.Population` :return: Selected parents (a father and a mother) :rtype: list of :obj:`gaft.components.IndividualBase` ''' random_state = check_random_state(random_state) do_cross = True if random_state.random() <= self.pc else False if not do_cross: return father.clone(), mother.clone() # Chromsomes for two children. chrom1 = deepcopy(father.chromosome) chrom2 = deepcopy(mother.chromosome) for i in range(father.chromosome.length): g1, g2 = chrom1[i], chrom2[i] do_exchange = True if random_state.random() <= self.pe else False if do_exchange: chrom1[i], chrom2[i] = g2, g1 child1, child2 = father.clone(), father.clone() child1.init(chromosome=chrom1) child2.init(chromosome=chrom2) return child1, child2
def select(self, size: int, population: List[Individual], comparator: Callable[[Individual, Individual], bool] = None, random_state=None) -> List[Individual]: random_state = check_random_state(random_state) if not comparator: comparator = self.single_objective_comparator # Check validity of tournament size. if self.tournament_size > len(population): msg = 'Tournament size({}) is larger than population size({})' raise ValueError(msg.format(self.tournament_size, len(population))) selected_indvs = [] for _ in range(size): chosen = None competitors = random_state.choice(population, self.tournament_size) random_state.shuffle(competitors) competitors = competitors.tolist() competitors.sort(key=cmp_to_key(comparator)) for i in range(len(competitors)): if random_state.uniform(0, 1) <= self.p: chosen = competitors[i] break if not chosen: chosen = competitors[random_state.randint(0, len(competitors)-1)] selected_indvs.append(chosen) return selected_indvs
def sort(population: List[Individual], random_state=None) -> List[Individual]: random_state = check_random_state(random_state) # non-dominated sorting pareto_fronts = NSGAIIEngine.nondominated_sort(population) # assign nondominated rank for rank in range(len(pareto_fronts)): for i in range(len(pareto_fronts[rank])): pareto_fronts[rank][i].nondominated_rank = rank # compute and assign crowding distance for rank in range(len(pareto_fronts)): crowding_distance_dict = NSGAIIEngine.calc_crowding_distance( pareto_fronts[rank]) for i in range(len(pareto_fronts[rank])): pareto_fronts[rank][ i].crowding_distance = crowding_distance_dict[ pareto_fronts[rank][i]] random_state.shuffle(pareto_fronts[rank]) pareto_fronts[rank].sort(key=lambda indv: indv.crowding_distance, reverse=True) flatten_population = list(itertools.chain.from_iterable(pareto_fronts)) return flatten_population
def select(self, size: int, population: List[Individual], is_unique=False, random_state=None) -> List[Individual]: random_state = check_random_state(random_state) fits = [float(indv._objective) for indv in population] max_fit = max(fits) fits = [(max_fit - e_fit + 1) for e_fit in fits] sum_fit = sum(fits) wheel = list(accumulate([i / sum_fit for i in fits])) selected = [True] * len(population) selected_indvs = [] for _ in range(size): idx = bisect_right(wheel, random_state.random()) while is_unique and not selected[idx] and idx > -len(selected): idx -= 1 if idx == -len(selected): idx = random_state.randint(0, len(selected) - 1) selected_indvs.append(population[idx]) selected[idx] = False return selected_indvs
def __init__(self, population: Population, objective: Callable[[Individual], Union[float, int]] = None, objectives: List[Callable[[Individual], Union[float, int]]] = None, selection: Selection = None, selection_size: int = None, crossover: Crossover = None, mutation: Mutation = None, replacement: Replacement = None, callbacks: CallbackList = None, generations: int = None, random_state=None): self.population = population self.generations = generations self.selection = selection self.crossover = crossover self.mutation = mutation self.replacement = replacement self.objective = objective self.objectives = objectives self.selection_size = selection_size or self.population.size self.random_state = check_random_state(random_state) self.callbacks = callbacks self.callbacks.set_engine(self) self.metrics = None self.history = None self.stop_running = False self.coefficients = None self.coefficient = None
def create_random_walk_rst(self, random_state=None): random_state = check_random_state(random_state) if self.root is not None: root = self.root else: random_state.randint(0, self.number_of_vertices) self.initialize() if len(self.edges) != 0: raise Exception( 'Default random init on Tree only accept empty Tree at initialization.\n\ Donot add_edge at initialize() or try other random init function.' ) mark = [False] * self.number_of_vertices mark[root] = True visited_nodes = 1 v0 = root while visited_nodes != self.number_of_vertices: v1 = random_state.choice(self.potential_adj[v0]) if not mark[v1]: self.add_edge(v0, v1) mark[v1] = True visited_nodes += 1 v0 = v1 self.repair()
def mutate(self, indv : Individual, random_state=None): random_state = check_random_state(random_state) idx = bisect_right(self.acc_pm, random_state.random()) if idx >= len(self.mutation_list): return indv.clone() ret_indv = self.mutation_list[idx].mutate(indv, random_state) return ret_indv
def init_population_randomly(self, random_state=None) -> List[Individual]: random_state = check_random_state(random_state) ret = [] for _ in range(self.size): new_indiv = self.individual_temp.clone() new_indiv.random_init(random_state=random_state) ret.append(new_indiv) return ret
def init_genes(self, genes=None, random_state=None): if genes is not None: if (isinstance(genes,(tuple,list)) and len(genes) == self.length): self.genes = np.array(genes) elif isinstance(genes, np.ndarray) and genes.ndim == 1 and genes.shape[0] == self.length: self.genes = genes else: raise Exception("genes has to be an instance of tuple or list and has the same length as chromosome") else: random_state = check_random_state(random_state) self.genes = np.array([ random_state.uniform(int(self.lower_bound[i]), int(self.upper_bound[i])) for i in range(self.length) ])
def mutate(self, individual: BinaryIndividual, random_state=None): random_state = check_random_state(random_state) do_mutation = True if random_state.random() <= self.pm else False ret_individual = individual.clone() if do_mutation: for i, genome in enumerate(ret_individual.chromosome.genes): flip = True if random_state.random() <= self.pe else False if flip: ret_individual.chromosome.genes[i] = genome ^ 1 return ret_individual
def mutate(self, indv: FloatIndividual, random_state=None): random_state = check_random_state(random_state) ret_indv = indv.clone() genes = np.copy(ret_indv.chromosome.genes) length = ret_indv.chromosome.length xl, xu = ret_indv.chromosome.lower_bound, ret_indv.chromosome.upper_bound do_mutation = random_state.random(length) < self.pm genes = genes[do_mutation] xl = xl[do_mutation] xu = xu[do_mutation] delta1 = (genes - xl) / (xu - xl) delta2 = (xu - genes) / (xu - xl) mut_pow = 1.0 / (self.distribution_index + 1.0) u = random_state.random(genes.shape[0]) mask = u <= 0.5 mask_not = np.logical_not(mask) deltaq = np.zeros(genes.shape) xy = 1.0 - delta1 val = 2.0 * u + (1.0 - 2.0 * u) * (np.power( xy, (self.distribution_index + 1.0))) d = np.power(val, mut_pow) - 1.0 deltaq[mask] = d[mask] xy = 1.0 - delta2 val = 2.0 * (1.0 - u) + 2.0 * (u - 0.5) * (np.power( xy, (self.distribution_index + 1.0))) d = 1.0 - (np.power(val, mut_pow)) deltaq[mask_not] = d[mask_not] mutated_genes = genes + deltaq * (xu - xl) # fix out of boudary error mutated_genes[mutated_genes < xl] = xl[mutated_genes < xl] mutated_genes[mutated_genes > xu] = xu[mutated_genes > xu] ret_genes = ret_indv.chromosome.genes ret_genes[do_mutation] = mutated_genes ret_indv.update_genes(ret_genes) return ret_indv
def cross(self, father: PermutationIndividual, mother: PermutationIndividual, random_state=None): ''' Cross chromsomes of parent using single point crossover method. ''' random_state = check_random_state(random_state) do_cross = True if random_state.random() <= self.pc else False if not do_cross: return father.clone(), mother.clone() # Chromsomes for two children. chrom1 = deepcopy(father.chromosome) chrom2 = deepcopy(mother.chromosome) # print(chrom1) # print(chrom2) if father.chromosome.length != mother.chromosome.length: raise ValueError("Father and mother have different length") length = father.chromosome.length slt_points = list(random_state.choice(length, 2, replace=False)) slt_points.sort() p1, p2 = slt_points cs1 = set(father.chromosome.genes[p1:p2]) cs2 = set(mother.chromosome.genes[p1:p2]) j1, j2 = 0, 0 for i in range(length): if mother.chromosome[i] not in cs1: if j1 == p1: j1 = p2 chrom1[j1] = mother.chromosome[i] j1 += 1 if father.chromosome[i] not in cs2: if j2 == p1: j2 = p2 chrom2[j2] = father.chromosome[i] j2 += 1 # print(chrom1) # print(chrom2) child1, child2 = father.clone(), father.clone() child1.init(chromosome=chrom1) child2.init(chromosome=chrom2) return child1, child2
def create_kruskal_rst(self, random_state=None): """random_init. Args: random_state: """ random_state = check_random_state(random_state) # order = random_state.permutation(np.arange(len(self.potential_edges))) weight = random_state.random(len(self.potential_edges)) order = np.argsort(-weight) self.initialize() for i in order: u, v = self.potential_edges[i] if self.try_add_edge(u, v): self.add_edge(u, v) self.repair()
def create_prim_rst(self, random_state=None): """create_prim_rst. This implementation is not good. I cannot find any data structure that support both random choice and insert, remove element This implementation does not take care unconnected graph (no tree) Args: random_state: """ random_state = check_random_state(random_state) self.initialize() # Set of connected nodes C = set() # eligible edges A = rset() # Init tree for u in range(self.number_of_vertices): if self.parent[u] != -1: C.add(u) for v in self.potential_adj[u]: if v not in C: A.add((u, v)) while len(C) < self.number_of_vertices: u, v = A.random_choice(random_state) A.remove((u, v)) if v not in C: self.add_edge(u, v) C.add(v) for w in self.potential_adj[v]: if w not in C: A.add((v, w)) if len(A) == 0 and len(C) != self.number_of_vertices: raise ValueError( 'Cannot create random spanning tree from unconnected tree') self.repair()
def mutate(self, individual: IntIndividual, random_state=None): random_state = check_random_state(random_state) do_mutation = True if random_state.random() <= self.pm else False ret_individual = individual.clone() if not do_mutation: return ret_individual length = individual.chromosome.length points = np.arange(length-1) if self.n_points * 2 > length: raise ValueError('SwapMutation: n_points * 2 > length') slt_points = random_state.choice(points, self.n_points * 2, replace=False) for i in range(0, len(slt_points), 2): x, y = slt_points[i], slt_points[i+1] g1, g2 = ret_individual.chromosome[x], ret_individual.chromosome[y] ret_individual.chromosome[x], ret_individual.chromosome[y] = g2, g1 return ret_individual
def create_random_walk_rst(self, random_state=None): random_state = check_random_state(random_state) self.initialize() mark = [False] * self.number_of_vertices visited_nodes = 0 for u in range(self.number_of_vertices): if self.parent[u] != -1: mark[u] = True visited_nodes += 1 v0 = self.root while visited_nodes != self.number_of_vertices: v1 = random_state.choice(self.potential_adj[v0]) if not mark[v1]: self.add_edge(v0, v1) mark[v1] = True visited_nodes += 1 v0 = v1 self.repair()
def create_prim_rst(self, random_state=None): random_state = check_random_state(random_state) if self.root is not None: root = self.root else: random_state.randint(0, self.number_of_vertices) self.initialize() if len(self.edges) != 0: raise Exception( 'Default random init on Tree only accept empty Tree at initialization.\n\ Donot add_edge at initialize() or try other random init function.' ) # Set of connected nodes C = set() # eligible edges A = rset( ) # my implementation of set that helps get random in set in O(1) # Init tree C.add(root) for v in self.potential_adj[root]: A.add((root, v)) while len(C) < self.number_of_vertices: u, v = A.random_choice(random_state) A.remove((u, v)) if v not in C: self.add_edge(u, v) C.add(v) for w in self.potential_adj[v]: if w not in C: A.add((v, w)) if len(A) == 0: raise ValueError( 'Cannot create random spanning tree from unconnected tree') self.repair()
def cross(self, father: Individual, mother: Individual, random_state=None): ''' Cross chromsomes of parent using single point crossover method. ''' random_state = check_random_state(random_state) do_cross = True if random_state.random() <= self.pc else False if not do_cross: return father.clone(), mother.clone() # Chromsomes for two children. chrom1 = deepcopy(father.chromosome) chrom2 = deepcopy(mother.chromosome) if father.chromosome.length != mother.chromosome.length: raise ValueError("Father and mother have different length") length = father.chromosome.length points = np.arange(length - 1) points = points[self.point_filter(points)] slt_points = list( random_state.choice(points, self.n_points, replace=False)) slt_points.append(length) slt_points.sort() do_exchange = False j = 0 for i in range(father.chromosome.length): if i > slt_points[j]: do_exchange ^= 1 j += 1 if do_exchange: chrom1[i], chrom2[i] = chrom2[i], chrom1[i] child1, child2 = father.clone(), father.clone() child1.init(chromosome=chrom1) child2.init(chromosome=chrom2) return child1, child2
def cross(self, father : FloatIndividual, mother : FloatIndividual, random_state=None): random_state = check_random_state(random_state) do_crossover = True if random_state.random() <= self.pc else False if not do_crossover: return father.clone(), mother.clone() length = father.chromosome.length xl, xu = father.chromosome.lower_bound, father.chromosome.upper_bound # Chromsomes for two children. y1 = deepcopy(father.chromosome.genes) y2 = deepcopy(mother.chromosome.genes) cross_element = np.full(length, True) cross_element[random_state.random(length) > self.pc] = False cross_element[np.abs(y1 - y2) <= self.__EPS] = False # ensure chromo1 < chromo2 for i in range(length): if y1[i] > y2[i]: y1[i], y2[i] = y2[i], y1[i] u = random_state.random(length) def calc_betaq(beta): alpha = 2.0 - np.power(beta, -(self.distribution_index + 1.0)) mask, mask_not = (u <= (1.0 / alpha)), (u > (1.0 / alpha)) betaq = np.zeros(mask.shape) betaq[mask] = np.power((u * alpha), (1.0 / (self.distribution_index + 1.0)))[mask] betaq[mask_not] = np.power((1.0 / (2.0 - u * alpha)), (1.0 / (self.distribution_index + 1.0)))[mask_not] return betaq delta = (y2 - y1) delta[delta < 1.0e-10] = 1.0e-10 beta = 1.0 + (2.0 * (y1 - xl) / delta) betaq = calc_betaq(beta) c1 = 0.5 * ((y1 + y2) - betaq * delta) beta = 1.0 + (2.0 * (xu - y2) / delta) betaq = calc_betaq(beta) c2 = 0.5 * ((y1 + y2) + betaq * delta) # do randomly a swap of variables b = random_state.random(length) <= 0.5 val = np.copy(c1[b]) c1[b] = c2[b] c2[b] = val genes1 = np.copy(father.chromosome.genes) genes2 = np.copy(mother.chromosome.genes) # copy the positions where the crossover was done genes1[cross_element] = c1[cross_element] genes2[cross_element] = c2[cross_element] # repair boundary genes1[genes1 < xl] = xl[genes1 < xl] genes1[genes1 > xu] = xu[genes1 > xu] genes2[genes2 < xl] = xl[genes2 < xl] genes2[genes2 > xu] = xu[genes2 > xu] offspring1, offspring2 = father.clone(), mother.clone() offspring1.update_genes(genes1) offspring2.update_genes(genes2) return offspring1, offspring2
def cross(self, father: Individual, mother: Individual, random_state=None): """ Cross chromsomes of parent using kruskal crossover method. + join 2 tree + keep the edges that appear in both + random weight for the remaining edges + use kruskal algorithm create offspring 1 + random another weight for the remaining edges + use kruskal algorithm to create offspring 2 """ random_state = check_random_state(random_state) do_cross = True if random_state.random() <= self.pc else False if not do_cross: return father.clone(), mother.clone() tree1, tree2 = father.decode(), mother.decode() if not (isinstance(tree1, KruskalTree) and isinstance(tree2, KruskalTree)): raise ValueError( f"The KruskalCrossover is only used on the individual that \ decodes to an instance of KruskallTree. \ got father type: {type(tree1)} and mother type {type(tree2)}" ) intersection = set() remaining_edges = set() tree1_edges = set(tree1.edges) tree2_edges = set(tree2.edges) for u, v in tree1_edges: if (u, v) in tree2_edges or (v, u) in tree2_edges: intersection.add((u, v)) elif (v, u) not in remaining_edges: remaining_edges.add((u, v)) for u, v in tree2_edges: if (u, v) not in tree1_edges and (v, u) not in tree1_edges \ and (v, u) not in remaining_edges: remaining_edges.add((u, v)) remaining_edges = list(remaining_edges) children = [tree1.clone(), tree2.clone()] for i in range(len(children)): # renew offspring tree children[i].initialize() # add edges that appear in both tree to offsprings for u, v in intersection: children[i].add_edge(u, v) # random weight for remaining_edges order = random_state.permutation(np.arange(len(remaining_edges))) for j in order: u, v = remaining_edges[j] children[i].add_edge(u, v) children[i].repair() offspring1, offspring2 = father.clone(), mother.clone() try: offspring1.encode(children[0]) offspring2.encode(children[1]) except NotImplementedError: raise ValueError( "Cannot call encode method. PrimCrossover requires encode method in Individual" ) except Exception as e: raise e return offspring1, offspring2
def pop(self, random_state=None): random_state = check_random_state(random_state) index = random_state.randint(0, len(self.__arr)) self.remove(self.__arr[index])
def random_choice(self, random_state=None): random_state = check_random_state(random_state) index = random_state.randint(0, len(self.__arr)) return self.__arr[index]
def random_init(self, random_state=None): random_state = check_random_state(random_state) genes = random_state.permutation(self.length) genes = genes + self.start self.update_genes(genes)
def cross(self, father: Individual, mother: Individual, random_state=None): random_state = check_random_state(random_state) do_cross = True if random_state.random() <= self.pc else False children = father.clone(), mother.clone() if not do_cross: return children trees = children[0].decode(), children[1].decode() if not (isinstance(trees[0], Tree) and isinstance(trees[1], Tree)): raise ValueError(f"The PrimCrossover is only used on the individual that \ decodes to an instance of Tree. \ got father type: {type(trees[0])} and mother type {type(trees[1])}") edge_union = set() potential_adj = [list() for _ in range(trees[0].number_of_vertices)] for i in range(2): for u, v in trees[i].edges: if (v, u) not in edge_union: edge_union.add((u, v)) potential_adj[u].append(v) potential_adj[v].append(u) for i in range(2): trees[i].initialize() # This is tricky, # I'm trying to make this method can work both cases # one when you add_edge in initialize() function, and one is not # I'm using parent attribute to do this. # But this only work when you update parent attribute after add_edge in initialize() # or you this crossover method with RootedTree, it supports update parent attribute in add_edge if not isinstance(trees[i], RootedTree) and len(trees[i].edges) != 0: raise Exception("Unexpected error occurred when running PrimCrossover") if trees[i].root is not None: root = trees[i].root else: random_state.randint(0, trees[i].number_of_vertices) trees[i].parent[root] = root # Set of connected nodes C = set() # eligible edges A = rset() # Init tree for u in range(trees[i].number_of_vertices): if trees[i].parent[u] != -1: C.add(u) for v in potential_adj[u]: if v not in C: A.add((u, v)) while len(C) < trees[i].number_of_vertices: u, v = A.random_choice(random_state) A.remove((u, v)) if v not in C: trees[i].add_edge(u, v) C.add(v) for w in potential_adj[v]: if w not in C: A.add((v, w)) if len(A) == 0 and len(C) != trees[i].number_of_vertices: raise ValueError('Cannot create random spanning tree from unconnected tree') trees[i].repair() try: children[0].encode(trees[0]) children[1].encode(trees[1]) except NotImplementedError: raise ValueError("Cannot call encode method. PrimCrossover requires encode method in Individual") except Exception as e: raise e return children[0], children[1]
def random_init(self, random_state=None): random_state = check_random_state(random_state or self.random_state) self.chromosome.init_genes(random_state=random_state)