From de2bc81bbeaae759259e6523a05a1a90c977bf63 Mon Sep 17 00:00:00 2001 From: Werner Van Geit Date: Wed, 20 Apr 2016 15:51:25 +0200 Subject: [PATCH] Include IBEA code inside bluepyopt Compiler now became dependency New subpackage 'deapext' Backward compatibility is maintained for the moment --- README.md | 7 +- bluepyopt/__init__.py | 12 +- bluepyopt/deapext/__init__.py | 0 bluepyopt/deapext/algorithms.py | 136 ++++++++++++++ bluepyopt/deapext/optimisations.py | 283 +++++++++++++++++++++++++++++ bluepyopt/deapext/tools/__init__.py | 3 + bluepyopt/deapext/tools/eps.c | 86 +++++++++ bluepyopt/deapext/tools/selIBEA.py | 155 ++++++++++++++++ bluepyopt/optimisations.py | 353 ------------------------------------ examples/l5pc/l5_config.zip | Bin 3818835 -> 3818835 bytes examples/l5pc/opt_l5pc.py | 10 +- requirements.txt | 1 - setup.py | 9 +- 13 files changed, 687 insertions(+), 368 deletions(-) create mode 100644 bluepyopt/deapext/__init__.py create mode 100644 bluepyopt/deapext/algorithms.py create mode 100644 bluepyopt/deapext/optimisations.py create mode 100644 bluepyopt/deapext/tools/__init__.py create mode 100644 bluepyopt/deapext/tools/eps.c create mode 100644 bluepyopt/deapext/tools/selIBEA.py diff --git a/README.md b/README.md index fd9b27a..907e035 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ archivePrefix = "arXiv", News ==== +- 2016/04/20: BluePyOpt now contains the code of the IBEA selector, no need to install a BBP-specific version of DEAP anymore - 2016/03/24: Released version 1.0 Requirements @@ -54,12 +55,6 @@ Installation If you want to use the ephys module of BluePyOpt, you first need to install Neuron with Python support on your machine. -All users need to install the BlueBrain version of DEAP: - -```bash -pip install git+http://github.com/BlueBrain/deap/ -``` - And then bluepyopt itself: ```bash diff --git a/bluepyopt/__init__.py b/bluepyopt/__init__.py index 5a3d152..845ff93 100644 --- a/bluepyopt/__init__.py +++ b/bluepyopt/__init__.py @@ -19,15 +19,25 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. """ +# pylint: disable=W0611 + from ._version import get_versions __version__ = get_versions()['version'] del get_versions from .api import * # NOQA import bluepyopt.optimisations +import bluepyopt.deapext.optimisations + +# Add some backward compatibility for the time when DEAPoptimisation not in +# deapext yet +# TODO deprecate this +bluepyopt.optimisations.DEAPOptimisation = \ + bluepyopt.deapext.optimisations.DEAPOptimisation + import bluepyopt.evaluators import bluepyopt.objectives -import bluepyopt.parameters +import bluepyopt.parameters # NOQA # TODO let objects read / write themselves using json # TODO create 'Variables' class diff --git a/bluepyopt/deapext/__init__.py b/bluepyopt/deapext/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/bluepyopt/deapext/algorithms.py b/bluepyopt/deapext/algorithms.py new file mode 100644 index 0000000..4c9eb99 --- /dev/null +++ b/bluepyopt/deapext/algorithms.py @@ -0,0 +1,136 @@ +"""Optimisation class""" + +""" +Copyright (c) 2016, EPFL/Blue Brain Project + + This file is part of BluePyOpt + + This library is free software; you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License version 3.0 as published + by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + details. + + You should have received a copy of the GNU Lesser General Public License + along with this library; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +""" + +# pylint: disable=R0914, R0912 + + +import random +import logging + +import deap +import deap.algorithms +import deap.tools +import pickle + +logger = logging.getLogger('__main__') + + +def eaAlphaMuPlusLambdaCheckpoint( + population, + toolbox, + mu, + _, + cxpb, + mutpb, + ngen, + stats=None, + halloffame=None, + cp_frequency=1, + cp_filename=None, + continue_cp=False): + r"""This is the :math:`(~\alpha,\mu~,~\lambda)` evolutionary algorithm.""" + + if continue_cp: + # A file name has been given, then load the data from the file + cp = pickle.load(open(cp_filename, "r")) + population = cp["population"] + parents = cp["parents"] + start_gen = cp["generation"] + halloffame = cp["halloffame"] + logbook = cp["logbook"] + history = cp["history"] + random.setstate(cp["rndstate"]) + else: + # Start a new evolution + start_gen = 1 + parents = population[:] + logbook = deap.tools.Logbook() + logbook.header = ['gen', 'nevals'] + (stats.fields if stats else []) + history = deap.tools.History() + + # TODO this first loop should be not be repeated ! + + # Evaluate the individuals with an invalid fitness + invalid_ind = [ind for ind in population if not ind.fitness.valid] + fitnesses = toolbox.map(toolbox.evaluate, invalid_ind) + for ind, fit in zip(invalid_ind, fitnesses): + ind.fitness.values = fit + + record = stats.compile(population) if stats is not None else {} + logbook.record(gen=start_gen, nevals=len(invalid_ind), **record) + + # Update the hall of fame with the generated individuals + if halloffame is not None: + halloffame.update(population) + + if history is not None: + history.update(population) + + # if history is not None: + # toolbox.decorate("mate", history.decorator) + # toolbox.decorate("mutate", history.decorator) + + # Begin the generational process + for gen in range(start_gen + 1, ngen + 1): + # Vary the parents + if hasattr(toolbox, 'variate'): + offspring = toolbox.variate(parents, toolbox, cxpb, mutpb) + else: + offspring = deap.algorithms.varAnd(parents, toolbox, cxpb, mutpb) + + population[:] = parents + offspring + + # Evaluate the individuals with an invalid fitness + invalid_ind = [ind for ind in offspring if not ind.fitness.valid] + fitnesses = toolbox.map(toolbox.evaluate, invalid_ind) + + for ind, fit in zip(invalid_ind, fitnesses): + ind.fitness.values = fit + + # Update the hall of fame with the generated individuals + if halloffame is not None: + halloffame.update(offspring) + + if history is not None: + history.update(offspring) + + # Select the next generation parents + parents[:] = toolbox.select(population, mu) + + # Update the statistics with the new population + record = stats.compile(population) if stats is not None else {} + logbook.record(gen=gen, nevals=len(invalid_ind), **record) + + logger.info(logbook.stream) + + if cp_filename and cp_frequency: + if gen % cp_frequency == 0: + cp = dict(population=population, generation=gen, + parents=parents, + halloffame=halloffame, + history=history, + logbook=logbook, rndstate=random.getstate()) + pickle.dump( + cp, + open(cp_filename, "wb")) + logger.debug('Wrote checkpoint to %s', cp_filename) + + return population, logbook, history diff --git a/bluepyopt/deapext/optimisations.py b/bluepyopt/deapext/optimisations.py new file mode 100644 index 0000000..53bf43a --- /dev/null +++ b/bluepyopt/deapext/optimisations.py @@ -0,0 +1,283 @@ +"""Optimisation class""" + +""" +Copyright (c) 2016, EPFL/Blue Brain Project + + This file is part of BluePyOpt + + This library is free software; you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License version 3.0 as published + by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + details. + + You should have received a copy of the GNU Lesser General Public License + along with this library; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +""" + +# pylint: disable=R0912 + + +import random +import logging +import functools + +import deap +import deap.base +import deap.algorithms +import deap.tools + +from . import algorithms +from . import tools + +import bluepyopt.optimisations + +logger = logging.getLogger('__main__') + +# TODO decide which variables go in constructor, which ones go in 'run' function +# TODO abstract the algorithm by creating a class for every algorithm, that way +# settings of the algorithm can be stored in objects of these classes + + +class WeightedSumFitness(deap.base.Fitness): + + """Fitness that compares by weighted sum""" + + def __init__(self, values=(), obj_size=None): + self.weights = [-1.0] * obj_size if obj_size is not None else [-1] + + super(WeightedSumFitness, self).__init__(values) + + @property + def weighted_sum(self): + """Weighted sum of wvalues""" + return sum(self.wvalues) + + @property + def sum(self): + """Weighted sum of values""" + return sum(self.values) + + def __le__(self, other): + return self.weighted_sum <= other.weighted_sum + + def __lt__(self, other): + return self.weighted_sum < other.weighted_sum + + def __deepcopy__(self, _): + """Override deepcopy""" + + cls = self.__class__ + result = cls.__new__(cls) + result.__dict__.update(self.__dict__) + return result + + +class WSListIndividual(list): + + """Individual consisting of list with weighted sum field""" + + def __init__(self, *args, **kwargs): + """Constructor""" + self.fitness = WeightedSumFitness(obj_size=kwargs['obj_size']) + del kwargs['obj_size'] + super(WSListIndividual, self).__init__(*args, **kwargs) + + +class DEAPOptimisation(bluepyopt.optimisations.Optimisation): + + """DEAP Optimisation class""" + + def __init__(self, evaluator=None, + use_scoop=False, + seed=1, + offspring_size=10, + eta=10, + mutpb=1.0, + cxpb=1.0, + map_function=None): + """Constructor""" + + super(DEAPOptimisation, self).__init__(evaluator=evaluator) + + self.use_scoop = use_scoop + self.seed = seed + self.offspring_size = offspring_size + self.eta = eta + self.cxpb = cxpb + self.mutpb = mutpb + self.map_function = map_function + + # Create a DEAP toolbox + self.toolbox = deap.base.Toolbox() + + self.setup_deap() + + def setup_deap(self): + """Set up optimisation""" + + # Number of objectives + OBJ_SIZE = len(self.evaluator.objectives) + + # Set random seed + random.seed(self.seed) + + # Eta parameter of crossover / mutation parameters + # Basically defines how much they 'spread' solution around + # The lower this value, the more spread + ETA = self.eta + + # Number of parameters + IND_SIZE = len(self.evaluator.params) + + # Bounds for the parameters + + LOWER = [] + UPPER = [] + + for parameter in self.evaluator.params: + LOWER.append(parameter.lower_bound) + UPPER.append(parameter.upper_bound) + + # Define a function that will uniformly pick an individual + def uniform(lower_list, upper_list, dimensions): + """Fill array """ + + if hasattr(lower_list, '__iter__'): + return [random.uniform(lower, upper) for lower, upper in + zip(lower_list, upper_list)] + else: + return [random.uniform(lower_list, upper_list) + for _ in range(dimensions)] + + # Register the 'uniform' function + self.toolbox.register("uniformparams", uniform, LOWER, UPPER, IND_SIZE) + + # Register the individual format + # An indiviual is create by WSListIndividual and parameters + # are initially + # picked by 'uniform' + self.toolbox.register( + "Individual", + deap.tools.initIterate, + functools.partial(WSListIndividual, obj_size=OBJ_SIZE), + self.toolbox.uniformparams) + + # Register the population format. It is a list of individuals + self.toolbox.register( + "population", + deap.tools.initRepeat, + list, + self.toolbox.Individual) + + # Register the evaluation function for the individuals + # import deap_efel_eval1 + self.toolbox.register("evaluate", self.evaluator.evaluate_with_lists) + + # Register the mate operator + self.toolbox.register( + "mate", + deap.tools.cxSimulatedBinaryBounded, + eta=ETA, + low=LOWER, + up=UPPER) + + # Register the mutation operator + self.toolbox.register( + "mutate", + deap.tools.mutPolynomialBounded, + eta=ETA, + low=LOWER, + up=UPPER, + indpb=0.5) + + # Register the variate operator + self.toolbox.register("variate", deap.algorithms.varAnd) + + # Register the selector (picks parents from population) + self.toolbox.register("select", tools.selIBEA) + + def _reduce_method(meth): + """Overwrite reduce""" + return (getattr, (meth.__self__, meth.__func__.__name__)) + import copy_reg + import types + copy_reg.pickle(types.MethodType, _reduce_method) + + if self.use_scoop: + if self.map_function: + raise Exception( + 'Impossible to use scoop is providing self ' + 'defined map function: %s' % + self.map_function) + + from scoop import futures + self.toolbox.register("map", futures.map) + + elif self.map_function: + self.toolbox.register("map", self.map_function) + + def run( + self, + max_ngen=10, + continue_cp=False, + cp_filename=None, + cp_frequency=1): + """Run optimisation""" + + # Total number of generation to run + NGEN = max_ngen + + # Crossover, mutation probabilities + CXPB = self.cxpb + MUTPB = self.mutpb + + # Total population size of EA + # ALPHA = POP_SIZE + # Total parent population size of EA + MU = self.offspring_size + # Total offspring size of EA + # LAMBDA = OFFSPRING_SIZE + + # Generate the population object + pop = self.toolbox.population(n=MU) + + hof = deap.tools.HallOfFame(10) + + stats = deap.tools.Statistics(key=lambda ind: ind.fitness.sum) + + import numpy + stats.register("avg", numpy.mean) + stats.register("std", numpy.std) + stats.register("min", numpy.min) + stats.register("max", numpy.max) + + pop, log, history = algorithms.eaAlphaMuPlusLambdaCheckpoint( + pop, + self.toolbox, + MU, + None, + CXPB, + MUTPB, + NGEN, + stats=stats, + halloffame=hof, + cp_frequency=cp_frequency, + continue_cp=continue_cp, + cp_filename=cp_filename) + + return pop, hof, log, history + + +class IBEADEAPOptimisation(DEAPOptimisation): + + """IBEA DEAP class""" + + def __init__(self, *args, **kwargs): + """Constructor""" + + super(IBEADEAPOptimisation, self).__init__(*args, **kwargs) diff --git a/bluepyopt/deapext/tools/__init__.py b/bluepyopt/deapext/tools/__init__.py new file mode 100644 index 0000000..2a8c2d2 --- /dev/null +++ b/bluepyopt/deapext/tools/__init__.py @@ -0,0 +1,3 @@ +"""Init""" + +from .selIBEA import * # NOQA diff --git a/bluepyopt/deapext/tools/eps.c b/bluepyopt/deapext/tools/eps.c new file mode 100644 index 0000000..89dc26c --- /dev/null +++ b/bluepyopt/deapext/tools/eps.c @@ -0,0 +1,86 @@ +/* +Copyright (c) 2016, EPFL/Blue Brain Project + + This file is part of BluePyOpt + + This library is free software; you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License version 3.0 as published + by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + details. + + You should have received a copy of the GNU Lesser General Public License + along with this library; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + The code in this file was original written in 2015 at the + BlueBrain Project, EPFL, Lausanne + The authors were Werner Van Geit, Michael Gevaert and Jean-Denis Courcol + It is based on a C implementation of the IBEA algorithm in the PISA + optimization framework developed at the ETH, Zurich + http://www.tik.ee.ethz.ch/pisa/selectors/ibea/?page=ibea.php + +*/ + +#include + +static PyObject * eps_indicator(PyObject *self, PyObject *args) +{ + unsigned n_of_dimensions, dimension; + double obj1, obj2, range; + double eps, max_eps = 0.0; + double min_box_bound, max_box_bound; + // Individual 1 objectives + PyObject * objectives1; + // Individual 2 objectives + PyObject * objectives2; + PyObject * min_box_bounds; + PyObject * max_box_bounds; + + + if (!PyArg_ParseTuple( args, "O!O!O!O!", + &PyList_Type, &objectives1, + &PyList_Type, &objectives2, + &PyList_Type, &min_box_bounds, + &PyList_Type, &max_box_bounds + )) return NULL; + + n_of_dimensions = PyList_Size(objectives1); + + + for (dimension = 0; dimension < n_of_dimensions; dimension++) { + min_box_bound = + PyFloat_AsDouble(PyList_GetItem(min_box_bounds, dimension)); + max_box_bound = + PyFloat_AsDouble(PyList_GetItem(max_box_bounds, dimension)); + + range = max_box_bound - min_box_bound; + + obj1 = PyFloat_AsDouble(PyList_GetItem(objectives1, dimension)); + obj2 = PyFloat_AsDouble(PyList_GetItem(objectives2, dimension)); + + eps = (obj1 - obj2) / range; + + // Find the maximum eps + if (dimension == 0) { + max_eps = eps; + } else { + if (eps > max_eps) max_eps = eps; + } + } + + return Py_BuildValue("f", max_eps); +} + +static PyMethodDef EPSMethods[] = { + {"indicator", eps_indicator, METH_VARARGS, + "Calculate the epsilon indicator."}, + {NULL, NULL, 0, NULL} /* Sentinel */ +}; + +PyMODINIT_FUNC initeps(void) { + (void) Py_InitModule("eps", EPSMethods); +} diff --git a/bluepyopt/deapext/tools/selIBEA.py b/bluepyopt/deapext/tools/selIBEA.py new file mode 100644 index 0000000..d463c23 --- /dev/null +++ b/bluepyopt/deapext/tools/selIBEA.py @@ -0,0 +1,155 @@ +"""IBEA selector""" + +from __future__ import division + +""" +Copyright (c) 2016, EPFL/Blue Brain Project + + This file is part of BluePyOpt + + This library is free software; you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License version 3.0 as published + by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + details. + + You should have received a copy of the GNU Lesser General Public License + along with this library; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +The code in this file was original written in 2015 at the +BlueBrain Project, EPFL, Lausanne +The authors were Werner Van Geit, Michael Gevaert and Jean-Denis Courcol +It is based on a C implementation of the IBEA algorithm in the PISA +optimization framework developed at the ETH, Zurich +http://www.tik.ee.ethz.ch/pisa/selectors/ibea/?page=ibea.php +""" + + +import numpy as numpy +import itertools +import random + + +def selIBEA(population, mu, alpha=None, kappa=.05, tournament_n=4): + """IBEA Selector""" + + if alpha is None: + alpha = len(population) + + # Put all the objectives of all individuals in a matrix + # DEAP selector are supposed to maximise the objective values + # We take the negative objectives because this algorithm will minimise + population_matrix = [ + [-x for x in individual.fitness.wvalues] for individual in population] + + # Calculate minimal square bounding box of the objectives + min_box_bounds, max_box_bounds = _calc_box_bounds(population_matrix) + + # Calculate a matrix with the fitness components of every individual + components = _calc_fitness_components( + population_matrix, + min_box_bounds, + max_box_bounds, + kappa=kappa) + + # Calculate the fitness values + _calc_fitnesses(population, components) + + # Do the environmental selection + population[:] = _environmental_selection(population, alpha) + + # Select the parents in a tournament + parents = _mating_selection(population, mu, tournament_n) + + return parents + + +def _calc_box_bounds(population_matrix): + """Calculate the minimal square bounding box of the objectives""" + + # Calculate the min/max over the columns + min_bounds = list(numpy.min(population_matrix, axis=0)) + max_bounds = list(numpy.max(population_matrix, axis=0)) + + # Return, parse to a list (indicators need lists, not numpy arrays) + return list(min_bounds), list(max_bounds) + + +def _calc_fitness_components( + population_matrix, + min_box_bounds, + max_box_bounds, + kappa=None): + """returns an N * N numpy array of doubles, which is their IBEA fitness """ + + # Population size is the number of rows in the population_matrix + pop_size = len(population_matrix) + + components_matrix = numpy.zeros((pop_size, pop_size)) + + # pylint: disable=F0401, E0611 + import eps + # pylint: enable=F0401, E0611 + + # Calculator the indicator value for every element in the matrix + # The code inside this for loop is (has to be) heavily optimised for speed + for i in xrange(0, pop_size): + ind1 = population_matrix[i] + for j in itertools.chain(xrange(0, i), xrange(i + 1, pop_size)): + ind2 = population_matrix[j] + components_matrix[i, j] = eps.indicator(ind1, + ind2, + min_box_bounds, + max_box_bounds) + + # Calculate max of absolute value of all elements in matrix + max_absolute_indicator = numpy.max(abs(components_matrix)) + + # Normalisation + components_matrix = \ + numpy.exp(numpy.multiply(components_matrix, + -1.0 / (kappa * max_absolute_indicator))) + + return components_matrix + + +def _calc_fitnesses(population, components): + """Calculate the IBEA fitness of every individual""" + + # Calculate sum of every column in the matrix, ignore diagonal elements + column_sums = numpy.sum(components, axis=0) - numpy.diagonal(components) + + # Fill the 'ibea_fitness' field on the individuals with the fitness value + for individual, ibea_fitness in zip(population, column_sums): + individual.ibea_fitness = ibea_fitness + + +def _mating_selection(population, mu, tournament_n): + """Returns the n_of_parents individuals with the best fitness""" + + parents = [] + for _ in xrange(mu): + # Pick individuals for tournament + tournament = [random.choice(population) for _ in range(tournament_n)] + # Sort according to fitness + tournament.sort(key=lambda ind: ind.ibea_fitness) + # Winner is element with smallest fitness + parents.append(tournament[0]) + + return parents + + +def _environmental_selection(population, selection_size): + """Returns the selection_size individuals with the best fitness""" + + # Sort the individuals based on their fitness + population.sort(key=lambda ind: ind.ibea_fitness) + + # Return the first 'selection_size' elements + return population[:selection_size] + +__all__ = ['selIBEA'] diff --git a/bluepyopt/optimisations.py b/bluepyopt/optimisations.py index 69c5de0..15b1590 100644 --- a/bluepyopt/optimisations.py +++ b/bluepyopt/optimisations.py @@ -21,23 +21,10 @@ # pylint: disable=R0912 - -import random import logging -import functools - -import deap -import deap.base -import deap.algorithms -import deap.tools -import pickle logger = logging.getLogger('__main__') -# TODO decide which variables go in constructor, which ones go in 'run' function -# TODO abstract the algorithm by creating a class for every algorithm, that way -# settings of the algorithm can be stored in objects of these classes - class Optimisation(object): @@ -49,343 +36,3 @@ def __init__(self, evaluator=None): """Constructor""" self.evaluator = evaluator - - -class WeightedSumFitness(deap.base.Fitness): - - """Fitness that compares by weighted sum""" - - def __init__(self, values=(), obj_size=None): - self.weights = [-1.0] * obj_size if obj_size is not None else [-1] - - super(WeightedSumFitness, self).__init__(values) - - @property - def weighted_sum(self): - """Weighted sum of wvalues""" - return sum(self.wvalues) - - @property - def sum(self): - """Weighted sum of values""" - return sum(self.values) - - def __le__(self, other): - return self.weighted_sum <= other.weighted_sum - - def __lt__(self, other): - return self.weighted_sum < other.weighted_sum - - def __deepcopy__(self, _): - """Override deepcopy""" - - cls = self.__class__ - result = cls.__new__(cls) - result.__dict__.update(self.__dict__) - return result - - -class WSListIndividual(list): - - """Individual consisting of list with weighted sum field""" - - def __init__(self, *args, **kwargs): - """Constructor""" - self.fitness = WeightedSumFitness(obj_size=kwargs['obj_size']) - del kwargs['obj_size'] - super(WSListIndividual, self).__init__(*args, **kwargs) - - -class DEAPOptimisation(Optimisation): - - """DEAP Optimisation class""" - - _instance_counter = 0 - - def __init__(self, evaluator=None, - use_scoop=False, - seed=1, - offspring_size=10, - eta=10, - mutpb=1.0, - cxpb=1.0, - map_function=None): - """Constructor""" - - super(DEAPOptimisation, self).__init__(evaluator=evaluator) - - Optimisation._instance_counter += 1 - - self.use_scoop = use_scoop - self.seed = seed - self.offspring_size = offspring_size - self.eta = eta - self.cxpb = cxpb - self.mutpb = mutpb - self.map_function = map_function - - # Create a DEAP toolbox - self.toolbox = deap.base.Toolbox() - - self.setup_deap() - - def setup_deap(self): - """Set up optimisation""" - - # Number of objectives - OBJ_SIZE = len(self.evaluator.objectives) - - # Set random seed - random.seed(self.seed) - - # Eta parameter of crossover / mutation parameters - # Basically defines how much they 'spread' solution around - # The lower this value, the more spread - ETA = self.eta - - # Number of parameters - IND_SIZE = len(self.evaluator.params) - - # Bounds for the parameters - - LOWER = [] - UPPER = [] - - for parameter in self.evaluator.params: - LOWER.append(parameter.lower_bound) - UPPER.append(parameter.upper_bound) - - # Define a function that will uniformly pick an individual - def uniform(lower_list, upper_list, dimensions): - """Fill array """ - - if hasattr(lower_list, '__iter__'): - return [random.uniform(lower, upper) for lower, upper in - zip(lower_list, upper_list)] - else: - return [random.uniform(lower_list, upper_list) - for _ in range(dimensions)] - - # Register the 'uniform' function - self.toolbox.register("uniformparams", uniform, LOWER, UPPER, IND_SIZE) - - # Register the individual format - # An indiviual is create by WSListIndividual and parameters - # are initially - # picked by 'uniform' - self.toolbox.register( - "Individual", - deap.tools.initIterate, - functools.partial(WSListIndividual, obj_size=OBJ_SIZE), - self.toolbox.uniformparams) - - # Register the population format. It is a list of individuals - self.toolbox.register( - "population", - deap.tools.initRepeat, - list, - self.toolbox.Individual) - - # Register the evaluation function for the individuals - # import deap_efel_eval1 - self.toolbox.register("evaluate", self.evaluator.evaluate_with_lists) - - # Register the mate operator - self.toolbox.register( - "mate", - deap.tools.cxSimulatedBinaryBounded, - eta=ETA, - low=LOWER, - up=UPPER) - - # Register the mutation operator - self.toolbox.register( - "mutate", - deap.tools.mutPolynomialBounded, - eta=ETA, - low=LOWER, - up=UPPER, - indpb=0.5) - - # Register the variate operator - self.toolbox.register("variate", deap.algorithms.varAnd) - - # Register the selector (picks parents from population) - self.toolbox.register("select", deap.tools.selIBEA) - def _reduce_method(meth): - """Overwrite reduce""" - return (getattr, (meth.__self__, meth.__func__.__name__)) - import copy_reg - import types - copy_reg.pickle(types.MethodType, _reduce_method) - - if self.use_scoop: - if self.map_function: - raise Exception( - 'Impossible to use scoop is providing self ' - 'defined map function: %s' % - self.map_function) - - from scoop import futures - self.toolbox.register("map", futures.map) - - elif self.map_function: - self.toolbox.register("map", self.map_function) - - def run( - self, - max_ngen=10, - continue_cp=False, - cp_filename=None, - cp_frequency=1): - """Run optimisation""" - - # Total number of generation to run - NGEN = max_ngen - - # Crossover, mutation probabilities - CXPB = self.cxpb - MUTPB = self.mutpb - - # Total population size of EA - # ALPHA = POP_SIZE - # Total parent population size of EA - MU = self.offspring_size - # Total offspring size of EA - # LAMBDA = OFFSPRING_SIZE - - # Generate the population object - pop = self.toolbox.population(n=MU) - - hof = deap.tools.HallOfFame(10) - - stats = deap.tools.Statistics(key=lambda ind: ind.fitness.sum) - - import numpy - stats.register("avg", numpy.mean) - stats.register("std", numpy.std) - stats.register("min", numpy.min) - stats.register("max", numpy.max) - - pop, log, history = eaAlphaMuPlusLambdaCheckpoint( - pop, - self.toolbox, - MU, - None, - CXPB, - MUTPB, - NGEN, - stats=stats, - halloffame=hof, - cp_frequency=cp_frequency, - continue_cp=continue_cp, - cp_filename=cp_filename) - - return pop, hof, log, history - -# pylint: disable=R0912, R0914 - -# TODO this function shouldn't be here - - -def eaAlphaMuPlusLambdaCheckpoint( - population, - toolbox, - mu, - _, - cxpb, - mutpb, - ngen, - stats=None, - halloffame=None, - cp_frequency=1, - cp_filename=None, - continue_cp=False): - r"""This is the :math:`(~\alpha,\mu~,~\lambda)` evolutionary algorithm.""" - - if continue_cp: - # A file name has been given, then load the data from the file - cp = pickle.load(open(cp_filename, "r")) - population = cp["population"] - parents = cp["parents"] - start_gen = cp["generation"] - halloffame = cp["halloffame"] - logbook = cp["logbook"] - history = cp["history"] - random.setstate(cp["rndstate"]) - else: - # Start a new evolution - start_gen = 1 - parents = population[:] - logbook = deap.tools.Logbook() - logbook.header = ['gen', 'nevals'] + (stats.fields if stats else []) - history = deap.tools.History() - - # TODO this first loop should be not be repeated ! - - # Evaluate the individuals with an invalid fitness - invalid_ind = [ind for ind in population if not ind.fitness.valid] - fitnesses = toolbox.map(toolbox.evaluate, invalid_ind) - for ind, fit in zip(invalid_ind, fitnesses): - ind.fitness.values = fit - - record = stats.compile(population) if stats is not None else {} - logbook.record(gen=start_gen, nevals=len(invalid_ind), **record) - - # Update the hall of fame with the generated individuals - if halloffame is not None: - halloffame.update(population) - - if history is not None: - history.update(population) - - # if history is not None: - # toolbox.decorate("mate", history.decorator) - # toolbox.decorate("mutate", history.decorator) - - # Begin the generational process - for gen in range(start_gen + 1, ngen + 1): - # Vary the parents - if hasattr(toolbox, 'variate'): - offspring = toolbox.variate(parents, toolbox, cxpb, mutpb) - else: - offspring = deap.algorithms.varAnd(parents, toolbox, cxpb, mutpb) - - population[:] = parents + offspring - - # Evaluate the individuals with an invalid fitness - invalid_ind = [ind for ind in offspring if not ind.fitness.valid] - fitnesses = toolbox.map(toolbox.evaluate, invalid_ind) - - for ind, fit in zip(invalid_ind, fitnesses): - ind.fitness.values = fit - - # Update the hall of fame with the generated individuals - if halloffame is not None: - halloffame.update(offspring) - - if history is not None: - history.update(offspring) - - # Select the next generation parents - parents[:] = toolbox.select(population, mu) - - # Update the statistics with the new population - record = stats.compile(population) if stats is not None else {} - logbook.record(gen=gen, nevals=len(invalid_ind), **record) - - logger.info(logbook.stream) - - if cp_filename and cp_frequency: - if gen % cp_frequency == 0: - cp = dict(population=population, generation=gen, - parents=parents, - halloffame=halloffame, - history=history, - logbook=logbook, rndstate=random.getstate()) - pickle.dump( - cp, - open(cp_filename, "wb")) - logger.debug('Wrote checkpoint to %s', cp_filename) - - return population, logbook, history diff --git a/examples/l5pc/l5_config.zip b/examples/l5pc/l5_config.zip index b98ef0d12615ad1fc91102f37295925e14c3f16c..4415bc01e598569c55141da1003ffc5fea09813f 100644 GIT binary patch delta 851 zcmZY7Ur1A77{~F>^PV}kwbW|!=u$H?|7@AlX6`)`#0Cwee-Q1ayYQkQ0=ozVK@e2B zi;?H5n@})BMO2Q6i53Y#U9EfRqGH|XMc|d-@4WBJS**ba9=_)}+j+O;Ulp`G1n&(o z-G9n?-JQ<84%U@VX2RJQ)74pPY>sM}6YsQ(_ylof{1G`Vw@)2z%!Sw&5r44QKT%&7 z@ta|8d&?Is)=^(Z)7>Y@?fVnD^9U10V9E$+wh^G1C5oMS zM{(w^4$1g+jz&q2gY&ej#)<{lJe58 zO}ekP%x!wEww88iR*eroQCpSVo%l|Bm2$lOlN!~+$Zt9phqguv)a0Jpep{gCGUv;% zJ|9p^vHv}wXI}9xQ^Q9D40P~78F--_eBg%woQ5ErfeNUEDmV-0;5>vN4Ao#j4b(ye zEF|90%~`_hwypw@uA<8}nag=AYfHt=8N(D`YJsNl_4E2M>ZMC`I5!5Tw9^ z&@GmlQ}oaIW4PW4D9#<$n{)R_5C;%buR28s>x-Iz;HdQnco84w93lm#9;OqXR`N9b%tE zxWb}tXF8<65aAbnE6i%1S91NLx6NIj{pr1|iQcomi2H!-l&??I-IJu3{BixyP|=z( z0<%Uy%NYTR+PbrUuJ1m@nA_Sf!_RXxMY25ih~BEQ;t6?FX*{JyRhrLeK$Umrse#F5 zFQ3zzO0ffD_TvRg$XnkR=#uI(-_Ui{m06?*ssJ1IovwNHhxjnFBO3$^bnrkKc%dA8;D-S0gdpsK z3aErC*bUXN2SN~ry=1.6', 'pandas', 'efel>=2.6', 'scoop>=0.7'], + install_requires=['numpy>=1.6', 'pandas', 'deap', 'efel>=2.6', + 'scoop>=0.7'], packages=setuptools.find_packages(exclude=('examples',)), author="BlueBrain Project, EPFL", author_email="werner.vangeit@epfl.ch", @@ -48,4 +52,5 @@ 'Operating System :: POSIX', 'Topic :: Scientific/Engineering', 'Topic :: Utilities'], - package_data={}) + package_data={}, + ext_modules=[eps])