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