Exemplo n.º 1
0
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
Exemplo n.º 2
0
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
Exemplo n.º 3
0
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
Exemplo n.º 4
0
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
Exemplo n.º 5
0
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
Exemplo n.º 6
0
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
Exemplo n.º 7
0
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