Skip to content
This repository was archived by the owner on Feb 26, 2025. It is now read-only.

Commit 0e34c50

Browse files
author
damart
committed
Added CMA optimisation
1 parent 7dfae69 commit 0e34c50

File tree

13 files changed

+1074
-32
lines changed

13 files changed

+1074
-32
lines changed

.idea/.gitignore

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/BluePyOpt.iml

Lines changed: 19 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/inspectionProfiles/profiles_settings.xml

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/misc.xml

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/modules.xml

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/vcs.xml

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bluepyopt/deapext/CMA_MO.py

Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
"""Multi Objective CMA-es class"""
2+
3+
"""
4+
Copyright (c) 2016, EPFL/Blue Brain Project
5+
6+
This file is part of BluePyOpt <https://github.com/BlueBrain/BluePyOpt>
7+
8+
This library is free software; you can redistribute it and/or modify it under
9+
the terms of the GNU Lesser General Public License version 3.0 as published
10+
by the Free Software Foundation.
11+
12+
This library is distributed in the hope that it will be useful, but WITHOUT
13+
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14+
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
15+
details.
16+
17+
You should have received a copy of the GNU Lesser General Public License
18+
along with this library; if not, write to the Free Software Foundation, Inc.,
19+
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20+
"""
21+
22+
# pylint: disable=R0912, R0914
23+
24+
import logging
25+
import numpy
26+
import copy
27+
from math import log
28+
29+
import deap
30+
from deap import base
31+
from deap import cma
32+
33+
from .stoppingCriteria import MaxNGen
34+
35+
from .utils import _bound
36+
37+
logger = logging.getLogger('__main__')
38+
39+
from deap.tools._hypervolume import hv as hv_c
40+
41+
42+
def get_hv(to_evaluate):
43+
i = to_evaluate[0]
44+
wobj = to_evaluate[1]
45+
ref = to_evaluate[2]
46+
return hv_c.hypervolume(numpy.concatenate((wobj[:i], wobj[i + 1:])), ref)
47+
48+
49+
def contribution(to_evaluate):
50+
def _reduce_method(meth):
51+
"""Overwrite reduce"""
52+
return (getattr, (meth.__self__, meth.__func__.__name__))
53+
54+
import copyreg
55+
import types
56+
copyreg.pickle(types.MethodType, _reduce_method)
57+
import pebble
58+
59+
with pebble.ProcessPool(max_tasks=1) as pool:
60+
tasks = pool.schedule(get_hv, kwargs={'to_evaluate': to_evaluate})
61+
response = tasks.result()
62+
63+
return response
64+
65+
66+
class CMA_MO(cma.StrategyMultiObjective):
67+
"""Multiple objective covariance matrix adaption"""
68+
69+
def __init__(self,
70+
centroids,
71+
offspring_size,
72+
sigma,
73+
max_ngen,
74+
IndCreator,
75+
RandIndCreator,
76+
map_function=None,
77+
use_scoop=False):
78+
"""Constructor
79+
80+
Args:
81+
centroid (list): initial guess used as the starting point of
82+
the CMA-ES
83+
sigma (float): initial standard deviation of the distribution
84+
max_ngen (int): total number of generation to run
85+
IndCreator (fcn): function returning an individual of the pop
86+
"""
87+
88+
if offspring_size is None:
89+
lambda_ = int(4 + 3 * log(len(RandIndCreator())))
90+
else:
91+
lambda_ = offspring_size
92+
93+
if centroids is None:
94+
starters = [RandIndCreator() for i in range(lambda_)]
95+
else:
96+
if len(centroids) != lambda_:
97+
from itertools import cycle
98+
generator = cycle(centroids)
99+
starters = [next(generator) for i in range(lambda_)]
100+
else:
101+
starters = centroids
102+
103+
cma.StrategyMultiObjective.__init__(self, starters, sigma,
104+
mu=int(lambda_ * 0.5),
105+
lambda_=lambda_)
106+
107+
self.population = []
108+
self.problem_size = len(starters[0])
109+
110+
self.map_function = map_function
111+
self.use_scoop = use_scoop
112+
113+
# Toolbox specific to this CMA-ES
114+
self.toolbox = base.Toolbox()
115+
self.toolbox.register("generate", self.generate, IndCreator)
116+
self.toolbox.register("update", self.update)
117+
118+
if self.use_scoop:
119+
if self.map_function:
120+
raise Exception(
121+
'Impossible to use scoop is providing self defined map '
122+
'function: %s' % self.map_function)
123+
from scoop import futures
124+
self.toolbox.register("map", futures.map)
125+
elif self.map_function:
126+
self.toolbox.register("map", self.map_function)
127+
128+
# Set termination conditions
129+
self.active = True
130+
if max_ngen <= 0:
131+
max_ngen = 100 + 50 * (self.problem_size + 3) ** 2 / numpy.sqrt(
132+
self.lambda_)
133+
134+
self.stopping_conditions = [MaxNGen(max_ngen)]
135+
136+
def hyper_volume(self, front):
137+
138+
wobj = numpy.array([ind.fitness.values for ind in front])
139+
obj_ranges = (numpy.max(wobj, axis=0) - numpy.min(wobj, axis=0))
140+
ref = numpy.max(wobj, axis=0) + 1
141+
142+
# Above 23 dim, hypervolume is too slow, so I settle for an approximation
143+
max_ndim = 23
144+
if len(ref) > max_ndim:
145+
idxs = list(range(len(ref)))
146+
idxs = [idxs[k] for k in numpy.argsort(obj_ranges)]
147+
idxs = idxs[::-1]
148+
idxs = idxs[:max_ndim]
149+
wobj = wobj[:, idxs]
150+
ref = ref[idxs]
151+
152+
to_evaluate = []
153+
for i in range(len(front)):
154+
to_evaluate.append([i, numpy.copy(wobj), numpy.copy(ref)])
155+
156+
contrib_values = self.toolbox.map(contribution, to_evaluate)
157+
158+
return list(contrib_values)
159+
160+
def _select(self, candidates):
161+
if len(candidates) <= self.mu:
162+
return candidates, []
163+
164+
pareto_fronts = deap.tools.sortLogNondominated(candidates,
165+
len(candidates))
166+
167+
chosen = list()
168+
mid_front = None
169+
not_chosen = list()
170+
171+
# Fill the next population (chosen) with the fronts until there is not enouch space
172+
# When an entire front does not fit in the space left we rely on the hypervolume
173+
# for this front
174+
# The remaining fronts are explicitly not chosen
175+
full = False
176+
for front in pareto_fronts:
177+
if len(chosen) + len(front) <= self.mu and not full:
178+
chosen += front
179+
elif mid_front is None and len(chosen) < self.mu:
180+
mid_front = front
181+
# With this front, we selected enough individuals
182+
full = True
183+
else:
184+
not_chosen += front
185+
186+
k = self.mu - len(chosen)
187+
if k > 0:
188+
hyperv = self.hyper_volume(mid_front)
189+
_ = [mid_front[k] for k in numpy.argsort(hyperv)]
190+
chosen += _[:k]
191+
not_chosen += _[k:]
192+
193+
return chosen, not_chosen
194+
195+
def get_population(self, to_space):
196+
"""Returns the population in the original parameter space"""
197+
pop = copy.deepcopy(self.population)
198+
for i, ind in enumerate(pop):
199+
for j, v in enumerate(ind):
200+
pop[i][j] = to_space[j](v)
201+
return pop
202+
203+
def get_parents(self, to_space):
204+
"""Returns the population in the original parameter space"""
205+
pop = copy.deepcopy(self.parents)
206+
for i, ind in enumerate(pop):
207+
for j, v in enumerate(ind):
208+
pop[i][j] = to_space[j](v)
209+
return pop
210+
211+
def generate_new_pop(self, lbounds, ubounds):
212+
"""Generate a new population bounded in the normalized space"""
213+
self.population = self.toolbox.generate()
214+
return _bound(self.population, lbounds, ubounds)
215+
216+
def update_strategy(self):
217+
self.toolbox.update(self.population)
218+
219+
def set_fitness(self, fitnesses):
220+
for f, ind in zip(fitnesses, self.population):
221+
ind.fitness.values = f
222+
223+
def set_fitness_parents(self, fitnesses):
224+
for f, ind in zip(fitnesses, self.parents):
225+
ind.fitness.values = f
226+
227+
def check_termination(self, ngen):
228+
stopping_params = {
229+
"ngen": ngen,
230+
"population": self.population,
231+
}
232+
233+
[c.check(stopping_params) for c in self.stopping_conditions]
234+
for c in self.stopping_conditions:
235+
if c.criteria_met:
236+
logger.info('CMA stopped because of termination criteria: ' +
237+
' '.join(type(c).__name__))
238+
self.active = False

0 commit comments

Comments
 (0)