def make_tree_population(size, pset, init_max_depth, max_depth, initial_type=None, init_method=grow_tree): ''' Make Tree Population Args: size (int): number of individuals in the Population pset (PrimitiveSet): set of primitives to build a random tree init_max_depth (int): initial max tree depth max_depth (int): max tree depth that translates into max array size initial_type (type): when using types, this constraints the initial primitive ot be of this type init_method (function): function that generates random trees (grow_tree, full_tree) Returns: array of tree based individuals initialized according to given method, with or without types ''' pop = make_empty_population(size) for i in range(size): pop.individuals[i] = pg.TreeIndividual() pop.individuals[i].genotype = init_method(pset, init_max_depth, max_depth, initial_type=initial_type) depth, nodes = pg.count_tree_internals(pset, pop.individuals[i].genotype) pop.individuals[i].depth = depth pop.individuals[i].nodes = nodes return pop
def test_count_tree_internals(): pset = pg.PrimitiveSet() pset.addFunction(op.add, 2) pset.addFunction(op.sub, 2) pset.addTerminal(1) pset.addTerminal(2) pset.addTerminal(3) pset.addVariable("x") tree1 = np.array([1, 2, 1, 5, 1, 2, 2, 6, 5, 2, 3, 4, 2, 2, 4, 4, 2, 6, 6, 2, 3, 3, 1, 2, 5, 5, 1, 5, 6, 0, 0, 0]) depth, nodes = pg.count_tree_internals(pset, tree1) assert depth == 7 assert nodes == 29 tree2 = np.array([5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) depth, nodes = pg.count_tree_internals(pset, tree2) assert depth == 1 assert nodes == 1
def test_count_tree_internals_typed(): np.random.seed(42) pset = pg.PrimitiveSet(typed=True) pset.addFunction(op.add, 2, types=[int, int, int]) pset.addFunction(op.sub, 2, types=[int, int, int]) pset.addFunction(op.mul, 2, types=[int, int, int]) pset.addFunction(protected_div, 2, types=[float, float, float]) num_constants = 5 for i in range(num_constants): pset.addTerminal(np.random.randint(-5, 5), types=[int]) for i in range(num_constants): pset.addTerminal(np.random.uniform(), types=[float]) pset.addVariable("x", types=[int]) tree1 = np.array([ 3, 2, 1, 6, 8, 2, 1, 3, 7, 6, 3, 7, 8, 8, 1, 1, 8, 6, 2, 14, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) depth, nodes = pg.count_tree_internals(pset, tree1) assert depth == 6 assert nodes == 21 np.random.seed(42) pset = pg.PrimitiveSet(typed=True) pset.addFunction(op.add, 2, types=[int, int, int]) pset.addFunction(op.sub, 2, types=[int, float, float]) pset.addFunction(op.mul, 2, types=[float, int, int]) pset.addFunction(protected_div, 2, types=[float, float, float]) num_constants = 5 for i in range(num_constants): pset.addTerminal(np.random.randint(-5, 5), types=[int]) for i in range(num_constants): pset.addTerminal(np.random.uniform(), types=[float]) pset.addVariable("x", types=[int]) tree2 = np.array([ 1, 1, 1, 7, 6, 2, 3, 6, 8, 12, 2, 3, 7, 8, 3, 8, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) depth, nodes = pg.count_tree_internals(pset, tree2) assert depth == 5 assert nodes == 17
def evaluate_tree_population(pop, fitness_fn, pset, **kargs): ''' Evaluate Tree Population Args: pop (Population): population to be evaluated in-place fitness_fn (function): fitness function that receives an individual pset (PrimitiveSet): set of primitives **kargs: keyword arguments that fitness_fn might have Returns: evaluated population by calling the fitness function ''' for i in range(pop.size): ind = pop.individuals[i] if ind.run_eval: ind.fitness = Fitness(fitness_fn(ind.genotype, **kargs)) depth, nodes = pg.count_tree_internals(pset, ind.genotype) ind.depth = depth ind.nodes = nodes ind.run_eval = False return pop
def tree_crossover(parent1, parent2, pset=None): ''' Tree Crossover Args: parent1 (TreeIndividual): first parent parent2 (TreeIndividual): second parent pset (PrimitiveSet): the set primitives allowed to be used Returns: offsprings from the two parents ''' def arraycopy(src, src_pos, dest, dest_pos, length): dest[dest_pos:dest_pos + length] = src[src_pos:src_pos + length] def get_primitive_type(primitive): if primitive in pset.ephemeral_constants: _, p_type = pset.ephemeral_constants[primitive] elif primitive in pset.terminals: _, p_type = pset.terminals[primitive] elif primitive in pset.variables: _, p_type = pset.variables[primitive] elif primitive in pset.functions: _, _, p_type = pset.functions[primitive] else: p_type = None raise AttributeError( 'This is a typed primitive set so types are required!') return p_type[0] # create offsprings offspring1 = pg.TreeIndividual( tree=np.zeros(parent1.genotype.size, dtype=parent1.genotype.dtype)) offspring2 = pg.TreeIndividual( tree=np.zeros(parent2.genotype.size, dtype=parent2.genotype.dtype)) # define tree cut points for subtree swap start1 = np.random.randint(parent1.nodes) end1 = pg.transverse_tree(pset, parent1.genotype, start1) # if typed set, start2 must be of the same type as start1 if pset.typed: p1_primitive = parent1.genotype[start1] p1_type = get_primitive_type(p1_primitive) valid_gene_pos = [] pos = 0 while pos < parent2.nodes: if p1_type == get_primitive_type(parent2.genotype[pos]): valid_gene_pos.append(pos) pos += 1 if valid_gene_pos == []: return parent1, parent2 # there is not valid point in the other, return original parents else: valid_pos = np.array(valid_gene_pos) start2 = valid_pos[np.random.randint(valid_pos.size)] else: start2 = np.random.randint(parent2.nodes) end2 = pg.transverse_tree(pset, parent2.genotype, start2) # define length of offspring trees len1 = start1 + (end2 - start2) + (parent1.nodes - end1) len2 = start2 + (end1 - start1) + (parent2.nodes - end2) if len1 > parent1.genotype.size: # passes max depth, return parent offspring1 = parent1 else: # produce offpsring 1 arraycopy(parent1.genotype, 0, offspring1.genotype, 0, start1) num_elements = (end2 - start2) arraycopy(parent2.genotype, start2, offspring1.genotype, start1, num_elements) num_elements = (parent1.nodes - end1) arraycopy(parent1.genotype, end1, offspring1.genotype, start1 + (end2 - start2), num_elements) offspring1.depth, offspring1.nodes = pg.count_tree_internals( pset, offspring1.genotype) if len2 > parent2.genotype.size: # passes max depth, return parent offspring2 = parent2 else: # produce offspring 2 arraycopy(parent2.genotype, 0, offspring2.genotype, 0, start2) num_elements = (end1 - start1) arraycopy(parent1.genotype, start1, offspring2.genotype, start2, num_elements) num_elements = (parent2.nodes - end2) arraycopy(parent2.genotype, end2, offspring2.genotype, start2 + (end1 - start1), num_elements) offspring2.depth, offspring2.nodes = pg.count_tree_internals( pset, offspring2.genotype) return offspring1, offspring2
def subtree_mutation(parent, pset=None, **kargs): ''' SubTree Mutation Args: parent (TreeIndividual): the individual to be mutated pset (PrimitiveSet): the set primitives allowed to be used Returns: mutated individual ''' def arraycopy(src, src_pos, dest, dest_pos, length): dest[dest_pos:dest_pos + length] = src[src_pos:src_pos + length] def get_primitive_type(primitive): if primitive in pset.ephemeral_constants: _, p_type = pset.ephemeral_constants[primitive] elif primitive in pset.terminals: _, p_type = pset.terminals[primitive] elif primitive in pset.variables: _, p_type = pset.variables[primitive] elif primitive in pset.functions: _, _, p_type = pset.functions[primitive] else: p_type = None raise AttributeError( 'This is a typed primitive set so types are required!') return p_type[0] offspring = pg.TreeIndividual( tree=np.zeros(parent.genotype.size, dtype=parent.genotype.dtype)) # define tree cut points for subtree replacement start1 = np.random.randint(parent.nodes) end1 = pg.transverse_tree(pset, parent.genotype, start1) # if typed subtree must return the appropriate type if pset.typed: primitive = parent.genotype[start1] parent_type = get_primitive_type(primitive) else: parent_type = None # TODO: the default values to set the size of the generated tree must be revised # and a proper mechanism to set these values on a per-problem case must be available # if typed set, start2 must be of the same type as start1 try: subtree = pg.grow_tree(pset, parent.depth - 1, parent.depth, initial_type=parent_type) except: e = sys.exc_info()[0] print('%s' % e) print('parent.depth: %s' % parent.depth) start2 = 0 end2 = pg.transverse_tree(pset, subtree, start2) len1 = start1 + (end2 - start2) + (parent.nodes - end1) # produce offpsring 1 arraycopy(parent.genotype, 0, offspring.genotype, 0, start1) num_elements = (end2 - start2) arraycopy(subtree, start2, offspring.genotype, start1, num_elements) num_elements = (parent.nodes - end1) arraycopy(parent.genotype, end1, offspring.genotype, start1 + (end2 - start2), num_elements) # update tree metrics offspring.depth, offspring.nodes = pg.count_tree_internals( pset, offspring.genotype) if offspring.nodes <= parent.genotype.size: return offspring else: return parent