def test_mutate():
    """Unit test for mutate function. Checks output type and checks test individual
     to see if mutation took place successfully."""
    copy_child = ['FB', 0, 0, 984, 0.09, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0,\
     (1, 2), 0, "checkerboard", "checkerboard", 0, 0, 0, 0, 0, 0]
    all_vals = []
    params = Segmentors.parameters()
    for key in params.pkeys:
        all_vals.append(eval(params.ranges[key]))
    assert isinstance(GeneticSearch.mutate(copy_child, all_vals, 0.5, True), list)
    assert GeneticSearch.mutate(copy_child, all_vals, 0.5, True) ==\
     ['FB', 1390, 0.173, 984, 0.09, 9927, 587, 0, 0.55, 0, 0, 0, 0, 1000, 0,\
      (1, 2), 0, 'disk', 'checkerboard', 9, 2907, -47, (0.0, 0.0, 0.0), 0, 0]
Example #2
0
def makeToolbox(pop_size):
    """Make a genetic algorithm toolbox using DEAP. The toolbox uses premade functions
     for crossover, mutation, evaluation and fitness.

    Keyword arguments:
    pop_size -- The size of our population, or how many individuals we have

    """
    # Minimizing fitness function
    creator.create("FitnessMin", base.Fitness, weights=(-0.000001, ))
    creator.create("Individual", list, fitness=creator.FitnessMin)

    # The functions that the GA knows
    toolbox = base.Toolbox()

    # Genetic functions
    toolbox.register("mate", skimageCrossRandom)  # crossover
    #toolbox.register("mutate", mutate)  # Mutation
    toolbox.register("mutate", Segmentors.mutateAlgo)  # Mutation
    toolbox.register("evaluate", Segmentors.runAlgo)  # Fitness
    toolbox.register("select", tools.selTournament, tournsize=5)  # Selection
    toolbox.register("map", futures.map)  # So that we can use scoop

    # DO: May want to later do a different selection process

    # We choose the parameters, for the most part, random
    params = Segmentors.parameters()

    for key in params.pkeys:
        toolbox.register(key, random.choice, eval(params.ranges[key]))

    func_seq = []
    for key in params.pkeys:
        func_seq.append(getattr(toolbox, key))

    # Here we populate our individual with all of the parameters
    toolbox.register("individual",
                     tools.initCycle,
                     creator.Individual,
                     func_seq,
                     n=1)

    # And we make our population
    toolbox.register("population",
                     tools.initRepeat,
                     list,
                     toolbox.individual,
                     n=pop_size)

    return toolbox
Example #3
0
class Evolver(object):
    """Perform the genetic algorithm by initializing a population and evolving it over a
     specified number of generations to find the optimal algorithm and parameters for the problem.

    Functions:
    newpopulation -- Initialize a new population.
    writepop -- Records our population in the file "filename".
    readpop -- Reads in existing population from "filename".
    popfitness -- Calculates the fitness values for our population.
    mutate -- Performs mutation and crossover on population.
    nextgen -- Generates the next generation of our population.
    run -- Runs the genetic algorithm.

    """

    AllVals = []
    my_p = Segmentors.parameters()
    for key in my_p.pkeys:
        AllVals.append(eval(my_p.ranges[key]))

    def __init__(self, img, mask, pop_size=10):
        """Set default values for the variables.

        Keyword arguments:
        img -- The original training image
        mask -- The ground truth segmentation mask for the img
        pop_size -- Integer value denoting size of our population,
            or how many individuals there are (default 10)

        """
        # Build Population based on size
        self.img = img
        self.mask = mask
        self.tool = makeToolbox(pop_size)
        self.hof = deap.tools.HallOfFame(10)
        self.best_avgs = []
        self.gen = 0
        self.cxpb, self.mutpb, self.flip_prob = 0.9, 0.9, 0.9

    def newpopulation(self):
        """Initialize a new population."""
        return self.tool.population()

    def writepop(self, tpop, filename='test.json'):
        """Record the population in the file "filename".

        Keyword arguments:
        tpop -- The population to be recorded.
        filename -- string denoting file in which to record
            the population. (default 'test.json')

        """
        logging.getLogger().info(f"Writting population to {filename}")
        with open(filename, 'w') as outfile:
            json.dump(tpop, outfile)

    def readpop(self, filename='test.json'):
        """Read in existing population from "filename"."""

        logging.getLogger().info(f"Reading population from {filename}")
        self.tool.register("population_read", initPopulation,
                           list, creator.Individual, filename)

        self.tool.register("individual_guess",
                           initIndividual, creator.Individual)
        self.tool.register("population_guess", initPopulation,
                           list, self.tool.individual_guess, "my_guess.json")

        return self.tool.population_read()

    def popfitness(self, tpop):
        """Calculate the fitness values for the population, and log general statistics about these
         values. Uses hall of fame (hof) to keep track of top 10 individuals.

        Keyword arguments:
        tpop -- current population

        Outputs:
        extract_fits -- Fitness values for our population
        tpop -- current population

        """
        new_image = [self.img for i in range(0, len(tpop))]
        new_val = [self.mask for i in range(0, len(tpop))]
        fitnesses = map(self.tool.evaluate, new_image, new_val, tpop)

        # DO: Dirk is not sure exactly why we need these
        for ind, fit in zip(tpop, fitnesses):
            ind.fitness.values = fit
        extract_fits = [ind.fitness.values[0] for ind in tpop]

        self.hof.update(tpop)

        #Algo = AlgorithmSpace(AlgoParams)

        # Evaluating the new population
        leng = len(tpop)
        mean = sum(extract_fits) / leng
        self.best_avgs.append(mean)
        sum1 = sum(i*i for i in extract_fits)
        stdev = abs(sum1 / leng - mean ** 2) ** 0.5
        logging.getLogger().info(f"Generation: {self.gen}")
        logging.getLogger().info(f" Min: {min(extract_fits)}")
        logging.getLogger().info(f" Max: {max(extract_fits)}")
        logging.getLogger().info(f" Avg: {mean}")
        logging.getLogger().info(f" Std: {stdev}")
        logging.getLogger().info(f" Size: {leng}")
        #logging.info(" Time: ", time.time() - initTime)
        logging.getLogger().info(f"Best Fitness: {self.hof[0].fitness.values}")
        logging.getLogger().info(f"{self.hof[0]}")
        # Did we improve the population?
        # past_pop = tpop
        # past_min = min(extract_fits)
        # past_mean = mean

        self.gen += self.gen

        return extract_fits, tpop

    def mutate(self, tpop):
        """Return new population with mutated individuals. Perform both mutation and crossover.

        Keyword arguments:
        tpop -- current population

        Output:
        final -- new population with mutated individuals.

       """
        # Calculate next population

    
        #TODO: There is an error here. We need to make sure the best hof is included?
        
        my_sz = len(tpop) #Length of current population
        top = min(10,max(1,round(0.1 * my_sz)))
        top = min(top, len(self.hof))
        var = max(1,round(0.4 * my_sz))
        var = min(var, len(self.hof))
        ran = my_sz - top - var

        print(f"pop[0:{top}:{var}:{ran}]")
        print(f"pop[0:{top}:{top+var}:{my_sz}]")
        
#         offspring = self.tool.select(tpop, var)
#         offspring = list(map(self.tool.clone, offspring))  # original code

        offspring = copy.deepcopy(list(self.hof))
        
        # crossover
        for child1, child2 in zip(offspring[::2], offspring[1::2]):
            # Do we crossover?
            if random.random() < self.cxpb:
                self.tool.mate(child1, child2)
                # The parents may be okay values so we should keep them
                # in the set
                del child1.fitness.values
                del child2.fitness.values

        # mutation
        for mutant in offspring:
            if random.random() < self.mutpb:
                self.tool.mutate(mutant, self.AllVals, self.flip_prob)
                del mutant.fitness.values

        # new
        #population = self.newpopulation()
        pop = self.tool.population()

        final = pop[0:ran]
        print(f"pop size should be {len(final)}")
        final += self.hof[0:top] 
        print(f"pop size should be {len(final)}")
        final += offspring[0:var] 
        print(f"pop size should be {len(final)}")
        
        print(f"pop[0:{top}:{var}:{ran}]")
        print(f"pop size should be {len(final)}")

        # Replacing the old population
        return final

    def nextgen(self, tpop):
        """Generate the next generation of the population.

        Keyword arguments:
        tpop -- current population

        """
        _, tpop = self.popfitness(tpop)
        return self.mutate(tpop)
        
    def run(self, ngen=10, population=None,startfile=None, checkpoint=None, cp_freq=1):
        """Run the genetic algorithm, updating the population over ngen number of generations.

        Keywork arguments:
        ngen -- number of generations to run the genetic algorithm.
        startfile -- File containing existing population (default None)
        checkpoint -- File containing existing checkpoint (default None)

        Output:
        population -- Resulting population after ngen generations.

        """
        
        if startfile:
            try:
                print(f"Reading in {startfile}")
                population = self.readpop(startfile)
            except FileNotFoundError:
                print("WARNING: Start file not found")
            except:
                raise
        
        if not population:
            print(f"Inicializing new randome population")
            population = self.newpopulation()
            if checkpoint:
                self.writepop(population, filename=f"{checkpoint}")
        

        for cur_g in range(0, ngen+1):
            print(f"generation {cur_g} of population size {len(population)}")
            _, population = self.popfitness(population)
            
            bestsofar = self.hof[0]
            seg = Segmentors.algoFromParams(bestsofar)
            mask = seg.evaluate(self.img)
            fitness = Segmentors.FitnessFunction(mask, self.mask)
            print(f"#BEST - {fitness} - {bestsofar}")

            if checkpoint and cur_g%cp_freq == 0:
                print(f"Writing Checkpoint file - {checkpoint}")
                copyfile(f"{checkpoint}", f"{checkpoint}.prev")
                self.writepop(population, filename=f"{checkpoint}")
                for cur_p in range(len(population)):
                    logging.getLogger().info(population[cur_p])
            if cur_g < ngen+1:
                population = self.mutate(population)
            
        if checkpoint:
            print(f"Writing Checkpoint file - {checkpoint}")
            copyfile(f"{checkpoint}", f"{checkpoint}.prev")
            self.writepop(population, filename=f"{checkpoint}")
            for cur_p in range(len(population)):
                logging.getLogger().info(population[cur_p])
        return population
Example #4
0
def test_parameters():
    """Unit test for parameters function. Checks formatting of parameter."""
    param = Segmentors.parameters()
    assert param.printparam('min_size') ==\
     "min_size=0.0\n\tparameter for felzenszwalb\n\t[i for i in range(0,10000)]\n"