Beispiel #1
0
def pi_grow(tree, max_depth):
    """
    Grows a tree until a single branch reaches a specified depth. Does this
    by only using recursive production choices until a single branch of the
    tree has reached the specified maximum depth. After that any choices are
    allowed.
    
    :param tree: An instance of the representation.tree.Tree class.
    :param max_depth: The maximum depth to which to derive a tree.
    :return: The fully derived tree.
    """

    # Initialise derivation queue.
    queue = [[tree, ret_true(params['BNF_GRAMMAR'].non_terminals[
                                 tree.root]['recursive'])]]

    # Initialise empty genome. With PI operators we can't use a depth-first
    # traversal of the tree to build the genome, we need to build it as we
    # encounter each node.
    genome = []

    while queue:
        # Loop until no items remain in the queue.

        # Pick a random item from the queue.
        chosen = randint(0, len(queue) - 1)

        # Pop the next item from the queue.
        all_node = queue.pop(chosen)
        node, recursive = all_node[0], all_node[0]

        # Get depth of current node.
        if node.parent is not None:
            node.depth = node.parent.depth + 1

        # Get maximum depth of overall tree.
        _, overall_depth = get_nodes_and_depth(tree)
        
        # Find the productions possible from the current root.
        productions = params['BNF_GRAMMAR'].rules[node.root]

        # Set remaining depth.
        remaining_depth = max_depth - node.depth

        if (overall_depth < max_depth) or \
                (recursive and (not any([item[1] for item in queue]))):
            # We want to prevent the tree from creating terminals until a
            # single branch has reached the full depth. Only select recursive
            # choices.

            # Find which productions can be used based on the derivation method.
            available = legal_productions("full", remaining_depth, node.root,
                                          productions['choices'])
        else:
            # Any production choices can be made.
            
            # Find which productions can be used based on the derivation method.
            available = legal_productions("random", remaining_depth, node.root,
                                          productions['choices'])
        
        # Randomly pick a production choice.
        chosen_prod = choice(available)

        # Find the index of the chosen production and set a matching codon
        # based on that index.
        prod_index = productions['choices'].index(chosen_prod)
        codon = randrange(productions['no_choices'],
                          params['BNF_GRAMMAR'].codon_size,
                          productions['no_choices']) + prod_index

        # Set the codon for the current node and append codon to the genome.
        node.codon = codon

        # Insert codon into the genome.
        genome.append(codon)
            
        # Initialise empty list of children for current node.
        node.children = []

        for i, symbol in enumerate(chosen_prod['choice']):
            # Iterate over all symbols in the chosen production.

            # Create new child.
            child = Tree(symbol["symbol"], node)

            # Append new node to children.
            node.children.append(child)

            if symbol["type"] == "NT":
                # The symbol is a non-terminal.
    
                # Check whether child is recursive
                recur_child = ret_true(params['BNF_GRAMMAR'].non_terminals
                                       [child.root]['recursive'])
    
                # Insert new child into the correct position in the queue.
                queue.insert(chosen + i, [child, recur_child])

    # genome, output, invalid, depth, and nodes can all be generated by
    # recursing through the tree once.
    _, output, invalid, depth, \
    nodes = tree.get_tree_info(params['BNF_GRAMMAR'].non_terminals.keys(),
                               [], [])
    
    return genome, output, nodes, depth
def pi_random_derivation(tree, max_depth):
    """
    Randomly builds a tree from a given root node up to a maximum given
    depth. Uses position independent methods to derive non-terminal nodes.
    Final tree is not guaranteed to reach the specified max_depth limit.
    
    :param tree: An instance of the representation.tree.Tree class.
    :param max_depth: The maximum depth to which to derive a tree.
    :return: The fully derived tree.
    """

    # Initialise derivation queue.
    queue = [[
        tree,
        ret_true(params['BNF_GRAMMAR'].non_terminals[tree.root]['recursive'])
    ]]

    # Initialise empty genome. With PI operators we can't use a depth-first
    # traversal of the tree to build the genome, we need to build it as we
    # encounter each node.
    genome = []

    while queue:
        # Loop until no items remain in the queue.

        # Pick a random item from the queue.
        chosen = randint(0, len(queue) - 1)

        # Pop the next item from the queue.
        all_node = queue.pop(chosen)
        node = all_node[0]

        # Get depth current node.
        if node.parent is not None:
            node.depth = node.parent.depth + 1

        # Find the productions possible from the current root.
        productions = params['BNF_GRAMMAR'].rules[node.root]

        # Set remaining depth.
        remaining_depth = max_depth - node.depth

        # Find which productions can be used based on the derivation method.
        available = legal_productions("random", remaining_depth, node.root,
                                      productions['choices'])

        # Randomly pick a production choice.
        chosen_prod = choice(available)

        # Find the index of the chosen production and set a matching codon
        # based on that index.
        prod_index = productions['choices'].index(chosen_prod)
        codon = randrange(productions['no_choices'],
                          params['BNF_GRAMMAR'].codon_size,
                          productions['no_choices']) + prod_index

        # Set the codon for the current node and append codon to the genome.
        node.codon = codon

        # Insert codon into the genome.
        genome.append(codon)

        # Initialise empty list of children for current node.
        node.children = []

        for i, symbol in enumerate(chosen_prod['choice']):
            # Iterate over all symbols in the chosen production.

            # Create new child.
            child = Tree(symbol["symbol"], node)

            # Append new node to children.
            node.children.append(child)

            if symbol["type"] == "NT":
                # The symbol is a non-terminal.

                # Check whether child is recursive
                recur_child = ret_true(params['BNF_GRAMMAR'].non_terminals[
                    child.root]['recursive'])

                # Insert new child into the correct position in the queue.
                queue.insert(chosen + i, [child, recur_child])

    # genome, output, invalid, depth, and nodes can all be Generated by
    # recursing through the tree once.
    _, output, invalid, depth, \
    nodes = tree.get_tree_info(params['BNF_GRAMMAR'].non_terminals.keys(),
                               [], [])

    return genome, output, nodes, depth