def generate_PI_ind_tree(max_depth): """ Generate an individual using a given Position Independent subtree initialisation method. :param max_depth: The maximum depth for the initialised subtree. :return: A fully built individual. """ # Initialise an instance of the tree class ind_tree = Tree(str(params['BNF_GRAMMAR'].start_rule["symbol"]), None) # Generate a tree genome, output, nodes, depth = pi_grow(ind_tree, max_depth) # Get remaining individual information phenotype, invalid, used_cod = "".join(output), False, len(genome) if params['BNF_GRAMMAR'].python_mode: # Grammar contains python code phenotype = python_filter(phenotype) # Initialise individual ind = individual.Individual(genome, ind_tree, map_ind=False) # Set individual parameters ind.phenotype, ind.nodes = phenotype, nodes ind.depth, ind.used_codons, ind.invalid = depth, used_cod, invalid # Generate random tail for genome. ind.genome = genome + [randint(0, params['CODON_SIZE']) for _ in range(int(ind.used_codons / 2))] return ind
def map_tree_from_genome(genome): """ Maps a full tree from a given genome. :param genome: A genome to be mapped. :return: All components necessary for a fully mapped individual. """ # Initialise an instance of the tree class tree = Tree(str(params['BNF_GRAMMAR'].start_rule[0]), None, depth_limit=params['MAX_TREE_DEPTH']) # Map tree from the given genome output, used_codons, nodes, depth, max_depth, invalid = \ genome_tree_map(tree, genome, [], 0, 0, 0, 0) # Build phenotype. phenotype = "".join(output) if params['BNF_GRAMMAR'].python_mode: # Grammar contains python code phenotype = python_filter(phenotype) if invalid: # Return "None" phenotype if invalid return None, genome, tree, nodes, invalid, max_depth, \ used_codons else: return phenotype, genome, tree, nodes, invalid, max_depth, \ used_codons
def mapper(genome, tree): """ Wheel for mapping. Calls the correct mapper for a given _input. Checks the params dict to ensure the correct type of individual is being created. If a genome is passed in with no tree, all tree-related information is generated. If a tree is passed in with no genome, the genome is sequenced from the tree. :param genome: Genome of an individual. :param tree: Tree of an individual. :return: All components necessary for a fully mapped individual. """ # one or other must be passed in, but not both assert (genome or tree) assert not (genome and tree) derivation_dic = {} if genome: # We have a genome and need to map an individual from that genome. genome = list(genome) # This is a fast way of creating a new unique copy of the genome # (prevents cross-contamination of information between individuals). if params['GENOME_OPERATIONS']: # Can generate tree information faster using # algorithm.mapper.map_ind_from_genome() if we don't need to # store the whole tree. phenotype, genome, tree, nodes, invalid, depth, \ used_codons, derivation_dic = map_ind_from_genome(genome) else: # Build the tree using algorithm.mapper.map_tree_from_genome(). phenotype, genome, tree, nodes, invalid, depth, \ used_codons = map_tree_from_genome(genome) derivation_dic = derivation_dic_from_tree(tree) else: # We have a tree. # genome, output, invalid, depth, and nodes can all be # generated by recursing through the tree once. genome, output, invalid, depth, \ nodes = tree.get_tree_info(params['BNF_GRAMMAR'].non_terminals.keys(), [], []) used_codons, phenotype = len(genome), "".join(output) derivation_dic = derivation_dic_from_tree(tree) if params['BNF_GRAMMAR'].python_mode and not invalid: # Grammar contains python code phenotype = python_filter(phenotype) if invalid: # Set values for invalid individuals. phenotype, nodes, depth, used_codons = None, np.NaN, np.NaN, np.NaN return phenotype, genome, tree, nodes, invalid, depth, used_codons, derivation_dic
def mapper(genome, tree): """ Wheel for mapping. Calls the correct mapper for a given _input. Checks the params dict to ensure the correct type of individual is being created. If a genome is passed in with no tree, all tree-related information is generated. If a tree is passed in with no genome, the genome is sequenced from the tree. :param genome: Genome of an individual. :param tree: Tree of an individual. :return: All components necessary for a fully mapped individual. """ # one or other must be passed in, but not both assert (genome or tree) assert not (genome and tree) if genome: # We have a genome and need to map an individual from that genome. genome = list(genome) # This is a fast way of creating a new unique copy of the genome # (prevents cross-contamination of information between individuals). if params['GENOME_OPERATIONS']: # Can generate tree information faster using # algorithm.mapper.map_ind_from_genome() if we don't need to # store the whole tree. phenotype, genome, tree, nodes, invalid, depth, \ used_codons = map_ind_from_genome(genome) else: # Build the tree using algorithm.mapper.map_tree_from_genome(). phenotype, genome, tree, nodes, invalid, depth, \ used_codons = map_tree_from_genome(genome) else: # We have a tree. # genome, output, invalid, depth, and nodes can all be # generated by recursing through the tree once. genome, output, invalid, depth, \ nodes = tree.get_tree_info(params['BNF_GRAMMAR'].non_terminals.keys(), [], []) used_codons, phenotype = len(genome), "".join(output) if params['BNF_GRAMMAR'].python_mode and not invalid: # Grammar contains python code phenotype = python_filter(phenotype) if invalid: # Set values for invalid individuals. phenotype, nodes, depth, used_codons = None, np.NaN, np.NaN, np.NaN return phenotype, genome, tree, nodes, invalid, depth, used_codons
def generate_ind_tree(max_depth, method): """ Generate an individual using a given subtree initialisation method. :param max_depth: The maximum depth for the initialised subtree. :param method: The method of subtree initialisation required. :return: """ # Initialise an instance of the tree class ind_tree = Tree(str(params['BNF_GRAMMAR'].start_rule[0]), None, depth_limit=max_depth - 1) # Generate a tree genome, output, nodes, _, depth = generate_tree(ind_tree, [], [], method, 0, 0, 0, max_depth - 1) # Get remaining individual information phenotype, invalid, used_cod = "".join(output), False, len(genome) if params['BNF_GRAMMAR'].python_mode: # Grammar contains python code phenotype = python_filter(phenotype) # Initialise individual ind = individual.Individual(genome, ind_tree, map_ind=False) # Set individual parameters ind.phenotype, ind.nodes = phenotype, nodes ind.depth, ind.used_codons, ind.invalid = depth, used_cod, invalid # Generate random tail for genome. ind.genome = genome + [ randint(0, params['CODON_SIZE']) for _ in range(int(ind.used_codons / 2)) ] return ind
def map_ind_from_genome(genome): """ A fast genotype to phenotype mapping process. Map input via rules to output. Does not require the recursive tree class, but still calculates tree information, e.g. number of nodes and maximum depth. :param genome: A genome to be mapped. :return: Output in the form of a phenotype string ('None' if invalid), Genome, None (this is reserved for the derivation tree), The number of nodes in the derivation, A boolean flag for whether or not the individual is invalid, The maximum depth of any node in the tree, and The number of used codons. """ # Create local variables to avoide multiple dictionary lookups max_tree_depth, max_wraps = params['MAX_TREE_DEPTH'], params['MAX_WRAPS'] nt_symbol, bnf_grammar = params['BNF_GRAMMAR'].NT, params['BNF_GRAMMAR'] n_input = len(genome) # Depth, max_depth, and nodes start from 1 to account for starting root # Initialise number of wraps at -1 (since used_input, current_depth, max_depth, nodes, wraps = 0, 1, 1, 1, -1 # Initialise output as empty deque list (deque is a list-like container # with fast appends and pops on either end). output = deque() # Initialise the list of unexpanded non-terminals with the start rule. unexpanded_symbols = deque([(bnf_grammar.start_rule, 1)]) while (wraps < max_wraps) and \ unexpanded_symbols and \ (max_depth <= max_tree_depth): # While there are unexpanded non-terminals, and we are below our # wrapping limit, and we haven't breached our maximum tree depth, we # can continue to map the genome. if used_input % n_input == 0 and \ used_input > 0 and \ any([i[0][1] == nt_symbol for i in unexpanded_symbols]): # If we have reached the end of the genome and unexpanded # non-terminals remain, then we need to wrap back to the start # of the genome again. Can break the while loop. wraps += 1 # Expand a production from the list of unexpanded non-terminals. current_item = unexpanded_symbols.popleft() current_symbol, current_depth = current_item[0], current_item[1] if max_depth < current_depth: # Set the new maximum depth. max_depth = current_depth # Set output if it is a terminal. if current_symbol[1] != nt_symbol: output.append(current_symbol[0]) else: # Current item is a new non-terminal. Find associated production # choices. production_choices = bnf_grammar.rules[current_symbol[0]] # Select a production based on the next available codon in the # genome. # TODO store the length of production choices to avoid len call? current_production = genome[used_input % n_input] % \ len(production_choices) # Use an input used_input += 1 # TODO: Derviation order is left to right(depth-first). Is a list comprehension faster? (Only if the loop for counting NT for each production can be avoided, by using a lookup instead. # Initialise children as empty deque list. children = deque() nt_count = 0 for prod in production_choices[current_production]: # iterate over all elements of chosen production rule. child = [prod, current_depth + 1] # Extendleft reverses the order, thus reverse adding. children.appendleft(child) # TODO store number of NT to avoid counting and simply do lookup instead? if child[0][1] == nt_symbol: nt_count += 1 # Add the new children to the list of unexpanded symbols. unexpanded_symbols.extendleft(children) if nt_count > 0: nodes += nt_count else: nodes += 1 # Generate phenotype string. output = "".join(output) if len(unexpanded_symbols) > 0: # All non-terminals have not been completely expanded, invalid # solution. return None, genome, None, nodes, True, max_depth, used_input if bnf_grammar.python_mode: # Grammar contains python code output = python_filter(output) return output, genome, None, nodes, False, max_depth, used_input