Genotypes Module

This module implements genotypes for grammatical evolution.

Genotype Class

The Genotype class holds the genetic material. It has the ability to run fitness functions and mutate. It is an internal object, and so few aspects of it would be regarded as public.

The class takes many properties from the grammatical evolution class, and so in some ways it might seem to be unnecessarily duplicative. The reason for doing it this way is to make each genotype relatively complete on its own. That way, if the genotype is packed up and marshalled off to a remote device for processing, everything is there to handle the tasks.

def __init__(self, start_gene_length, max_gene_length,member_no):

This function initiates the genotype. It must open with the starting gene length and the maximum gene length. These lengths are the decimal lengths not the binary lengths. In addition, the member number is needed, since the genotype creation process is controlled by the grammatic evolution class.

def _generate_binary_gene(self, length):

This function creates a random set of bits.

def set_binary_gene(self, binary_gene):

This function sets the value of the binary gene directly. This is used in the crossover and mutation functions. There is an automatic adjustment to trim the length to a multiple of 8.

def generate_decimal_gene(self):

This function converts the binary gene to a usable decimal gene.

def _dec2bin_gene(dec_gene):

This is a utility function that converts a decimal list to binary string.

def _place_material(program, item, start_pos, end_pos):

This is a utility function that replaces a part of a string in a specific location.

def runtime_resolve(self, item, return_type):

This function is callable by the generated program to enable additional values be pulled from genotype and BNF as the need arises during execution of the program.

Usage is self.runtime_resolve('', return_type);

The return type casts the result back to the format needed. Supported return types are: 'int', 'float', 'str', and 'bool'.

def _fmt_resolved_vars(value, return_type):

This method formats the result for a resolved variable for use during runtime so that the information can fit into the context of what is running.

Note that if the execute code was to be subclassed to a parser to avoid the use of exec, then this funtion should also be done as well, since it uses eval.

def set_bnf_variable(self, variable_name, value):

This function adds a variable to the bnf. The format is the name, typically bounded by <>, such as "", and the parameters are in the form of a list. The items in the list will be converted to strings, if not already.

def resolve_variable(self, variable):

This function receives a variable and using the variable as a key looks it up in the local_bnf. The list of possible values available are then used by the genotype via a codon to select a final value that would be used.

def _map_variables(self, program, check_stoplist):

This function looks for a variable in the form of . If check_stoplist is True, then there will be a check to determine if it is a run-time variable, and therefor will be resolved later.

This process runs until all of the variables have been satisfied, or a time limit on the process has been reached.

def on_stoplist(item):

Checks the stop list for runtime variables

def _get_codon(self):

This function gets the next decimal codon from the genotype.

There are two counters for this function. One pointer is used to indicate the next location of the decimal code that is to be returned. The other pointer is the index of the codon that has been drawn regardless if process has wrapped around.

If the end of the genotype is reached, and the wrap flag is True, then the position for the next codon is taken from the front again. Additionally, if wrapping has taken place and the extend_genotype flag is set, then the genotype will continue to grow in length until the max_gene_length is reached.

If the wrap flag is not set, when the end of the genotype is reached, an error is raised.

At the start of this function, the position has been already incremented to get the codon. Therefore, the position has to be checked to determine whether it is pointing past the end of of the maximum length of the gene. If it is, then the position is just reset back to the starting position.

def _reset_gene_position(self):

This function resets the next decimal gene that will be selected back to 0. The point of this is when reusing a gene that is already instantiated, you can regenerate your program using exactly the same characteristics as before.

def _update_genotype(self):

This function updates the binary genotype from the decimal gene if the genotype is extended.

def compute_fitness(self):

This function computes the fitness function. The process consists mapping the codon to the program variables and running the resulting program and computing the fitness value. In addition, the binary gene is updated if the decimal gene has been extended.

def _map_gene(self):

This function applies the genotype information to build a program. Mapping the variables into the search space is an initial load, and can also iteratively accept values as the program that has been created executes via the runtime_resolve function.

If for any reason the mapping process fails to create a viable program, or it takes too long, then the process is aborted and the fitness_fail value is applied instead.

This function uses the print command to show the program that has been generated as well as print the fitness value. It is expected that this will be converted to a more useful logging system at some point.

def _execute_code(self, program):

This function executes code that has been generated. This function would be subclassed if executing the code on a remote server, or swapping in a custom parser.

def mutate(self, mutation_rate, mutation_type):

This is function randomly mutates a binary genotype by changing 1 to 0 and vice versa. It is not context-perserving at this level.

def _multiple_mutate(self, mutation_rate):

This function walks the gene and based upon the mutation rate will alter a bit.

def get_binary_gene_length(self):

This function returns the length of the binary gene. Which is 8 times the length of the decimal gene.

def _single_mutate(self):

This function with a randomly selects a mutation point within the gene and changes a 1 to 0, or vice versa.

def _mutate(gene, position):

This function does the actual mutation of the gene at a specific position.

def _select_choice(codon, selection):

This function, based upon the codon, makes a choice from the list. The determination is based upon the module of the codon to the length of the list of choices. For example, if the codon is 10 and the list is 7 choices long, then the selection would be from selection[3]. This ensures that for every codon for every selection there is some choice.

def get_program(self):

This function returns the program that has been generated. It is only valid after the gene has been mapped.

def get_preprogram(self):

This function returns the prototype program to which the variables will be applied.

def get_fitness(self):

This function returns the fitness value that has been created as a result of running the fitness function.

def get_fitness_fail(self):

This function returns the fitness value that constitutes failure as assigned by the parent grammatical evolution.

def conv_int(str_value):

This method attempts to convert string value to an int. This function used to live inside self._fmt_resolved_vars, but was taken out because it is easier to do unit testing this way.