예제 #1
0
def test_tree_crossover2():
    np.random.seed(42)
    pset = pg.PrimitiveSet()
    pset.addFunction(op.add, 2)
    pset.addFunction(op.sub, 2)
    pset.addFunction(op.mul, 2)
    pset.addFunction(protected_div, 2)
    num_constants = 10
    for i in range(num_constants):
        pset.addTerminal(np.random.randint(-5, 5))
    pset.addVariable("x")

    t1 = np.array([3, 9, 1, 1, 5, 7, 6, 0, 0, 0])
    t2 = np.array([6, 0, 0, 0, 0, 0, 0, 0, 0, 0])
    i1 = pg.TreeIndividual(tree=t1, nodes=7)
    i2 = pg.TreeIndividual(tree=t2, nodes=1)
    o1, o2 = pg.tree_crossover(i1, i2, pset=pset)
    o1_str = pg.interpreter(pset, o1.genotype)
    o2_str = pg.interpreter(pset, o2.genotype)

    assert o1.depth == 3
    assert o1.nodes == 5
    assert np.array_equal(o1.genotype, np.array([3, 9, 1, 6, 6, 0, 0, 0, 0,
                                                 0]))
    assert o1_str == 'mul(4, add(-2, -2))'

    assert o2.depth == 2
    assert o2.nodes == 3
    assert np.array_equal(o2.genotype, np.array([1, 5, 7, 0, 0, 0, 0, 0, 0,
                                                 0]))
    assert o2_str == 'add(1, 2)'
예제 #2
0
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
예제 #3
0
def test_make_individual_tree():
    ind = pg.TreeIndividual()
    assert type(ind) is pg.TreeIndividual
    assert ind.fitness is None
    assert ind.genotype is None
    assert ind.run_eval is True
    assert ind.depth is None
    assert ind.nodes is None
예제 #4
0
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
예제 #5
0
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
예제 #6
0
def tree_point_mutation(i1, pset=None, gene_rate=None, **kargs):
    '''
    Tree Point Mutation

    Args:
        i1 (TreeIndividual): the individual to be mutated
        pset (PrimitiveSet): the set primitives allowed to be used

    Returns:
        mutated individual
    '''

    if pset.ephemeral_cache:
        terminals_keys = []
        for k in list(pset.terminals.keys()):
            if k in pset.ephemeral_cache:
                pass
            else:
                terminals_keys.append(k)
        terminals_idx = np.concatenate([
            np.array(terminals_keys),
            np.array(list(pset.ephemeral_constants.keys()))
        ])
    else:
        terminals_idx = np.array(list(pset.terminals.keys()))
    variables_idx = np.array(list(pset.variables.keys()))
    all_terminals_idx = np.concatenate([terminals_idx, variables_idx])

    new_genotype = np.copy(i1.genotype)

    i = 0
    while i < new_genotype.size and new_genotype[i] != 0:

        if np.random.uniform() < gene_rate:
            primitive = new_genotype[i]
            # replace terminal/variable with another one
            if primitive in pset.terminals or primitive in pset.variables or primitive in pset.ephemeral_constants:
                if pset.typed:
                    _, term_types = pset.terminals[
                        primitive] if primitive in pset.terminals else (None,
                                                                        None)
                    _, ephm_types = pset.ephemeral_constants[
                        primitive] if primitive in pset.ephemeral_constants else (
                            None, None)
                    _, vars_types = pset.variables[
                        primitive] if primitive in pset.variables else (None,
                                                                        None)

                    if term_types is not None:
                        typed_terminals = pset.terminals_types[term_types[0]]
                        for e in pset.ephemeral_cache:
                            typed_terminals.remove(e)
                    else:
                        typed_terminals = []

                    if ephm_types is not None:
                        typed_ephemerals = pset.terminals_types[ephm_types[0]]
                    else:
                        typed_ephemerals = []

                    if vars_types is not None:
                        typed_variables = pset.variables_types[vars_types[0]]
                    else:
                        typed_variables = []

                    valid_terminals = np.concatenate([
                        np.array(typed_terminals, dtype=int),
                        np.array(typed_variables, dtype=int),
                        np.array(typed_ephemerals, dtype=int)
                    ])

                    new_idx = valid_terminals[np.random.randint(
                        valid_terminals.size)]
                else:
                    new_idx = all_terminals_idx[np.random.randint(
                        all_terminals_idx.size)]

                if new_idx in pset.ephemeral_cache:
                    new_idx = pset.addEphemeralConstant(new_idx)
                new_genotype[i] = new_idx

            # replace function with another one of the same arity
            elif primitive in pset.functions:
                # only functions of the same arity are valid to be used
                _, arity, types = pset.functions[primitive]

                if pset.typed:
                    arity_functions = set(pset.arity_cache[arity])
                    # only cache by return type
                    typed_functions = set(pset.functions_types[types[0]])
                    # compute intersection of arity and typed functions
                    # and if result is not null, check if all arguments are equivalent
                    # otherwise, does not mutate
                    final_candidates = []

                    for fn_key in set.intersection(arity_functions,
                                                   typed_functions):
                        _, _, candidate_types = pset.functions[fn_key]
                        if candidate_types == types:
                            final_candidates.append(fn_key)

                    if final_candidates != []:
                        valid_functions_idx = np.array(final_candidates)
                        new_genotype[i] = valid_functions_idx[
                            np.random.randint(valid_functions_idx.size)]
                else:
                    valid_functions_idx = np.array(pset.arity_cache[arity])
                    new_genotype[i] = valid_functions_idx[np.random.randint(
                        valid_functions_idx.size)]

        i += 1

    new_individual = pg.TreeIndividual(tree=new_genotype,
                                       depth=i1.depth,
                                       nodes=i1.nodes)
    return new_individual