Skip to content

Commit

Permalink
Prevent DE/PSO interactions in ParaPortfolio (#1056)
Browse files Browse the repository at this point in the history
  • Loading branch information
jrapin authored Feb 19, 2021
1 parent 10e9d22 commit e944132
Show file tree
Hide file tree
Showing 3 changed files with 21 additions and 12 deletions.
11 changes: 7 additions & 4 deletions nevergrad/optimization/differentialevolution.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,10 @@ def _internal_ask_candidate(self) -> p.Parameter:
self._uid_queue.asked.add(candidate.uid)
return candidate
# init is done
parent = self.population[self._uid_queue.ask()]
lineage = self._uid_queue.ask()
parent = self.population[lineage]
candidate = parent.spawn_child()
candidate.heritage["lineage"] = lineage # tell-not-asked may have provided a different lineage
data = candidate.get_standardized_data(reference=self.parametrization)
# define all the different parents
uids = list(self.population)
Expand Down Expand Up @@ -188,9 +190,9 @@ def _internal_tell_not_asked(self, candidate: p.Parameter, loss: tp.FloatLoss) -
discardable: tp.Optional[str] = None
if len(self.population) >= self.llambda:
if self.num_objectives == 1: # monoobjective: replace if better
worst = max(self.population.values(), key=base._loss)
uid, worst = max(self.population.items(), key=lambda p: base._loss(p[1]))
if loss < base._loss(worst):
discardable = worst.heritage["lineage"]
discardable = uid
else: # multiobjective: replace if in pareto and some parents are not
pareto_uids = {c.uid for c in self.pareto_front()}
if candidate.uid in pareto_uids:
Expand All @@ -202,8 +204,9 @@ def _internal_tell_not_asked(self, candidate: p.Parameter, loss: tp.FloatLoss) -
del self.population[discardable]
self._uid_queue.discard(discardable)
if len(self.population) < self.llambda: # if there is space, add the new point
candidate.heritage["lineage"] = candidate.uid # new lineage
self.population[candidate.uid] = candidate
# this candidate lineage is not candidate.uid, but to avoid interfering with other optimizers (eg: PSO)
# we should not update the lineage (and lineage of children must therefore be enforced manually)
self._uid_queue.tell(candidate.uid)


Expand Down
13 changes: 5 additions & 8 deletions nevergrad/optimization/optimizerlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -809,6 +809,7 @@ def _internal_ask_candidate(self) -> p.Parameter:
return candidate
uid = self._uid_queue.ask()
candidate = self._spawn_mutated_particle(self.population[uid])
candidate.heritage["lineage"] = uid # override in case it was a tell-not-asked
return candidate

def _get_boxed_data(self, particle: p.Parameter) -> np.ndarray:
Expand Down Expand Up @@ -853,22 +854,19 @@ def _internal_tell_candidate(self, candidate: p.Parameter, loss: tp.FloatLoss) -

def _internal_tell_not_asked(self, candidate: p.Parameter, loss: tp.FloatLoss) -> None:
# nearly same as DE
candidate._meta["value"] = loss
worst: tp.Optional[p.Parameter] = None
if not len(self.population) < self.llambda:
worst = max(self.population.values(), key=lambda p: p._meta.get("value", float("inf")))
if worst._meta.get("value", float("inf")) < loss:
uid, worst = max(self.population.items(), key=lambda p: base._loss(p[1]))
if base._loss(worst) < loss:
return # no need to update
else:
uid = worst.heritage["lineage"]
del self.population[uid]
self._uid_queue.discard(uid)
candidate.heritage["lineage"] = candidate.uid # new lineage
if "speed" not in candidate.heritage:
candidate.heritage["speed"] = self._rng.uniform(-1.0, 1.0, self.parametrization.dimension)
self.population[candidate.uid] = candidate
self._uid_queue.tell(candidate.uid)
if loss < self._best._meta.get("loss", float("inf")):
if loss < base._loss(self._best):
self._best = candidate


Expand Down Expand Up @@ -2049,13 +2047,12 @@ def _internal_ask_candidate(self) -> p.Parameter:
return candidate

def _internal_tell_candidate(self, candidate: p.Parameter, loss: tp.FloatLoss) -> None:
candidate._meta["loss"] = loss
if self.population_size_adaptation:
self.popsize.add_value(loss)
self.children.append(candidate)
if len(self.children) >= self.popsize.llambda:
# Sorting the population.
self.children.sort(key=lambda c: c._meta["loss"])
self.children.sort(key=base._loss)
# Computing the new parent.
self.parents = self.children[: self.popsize.mu]
self.children = []
Expand Down
9 changes: 9 additions & 0 deletions nevergrad/optimization/test_optimizerlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -731,3 +731,12 @@ def constraint(arg: tp.Any) -> bool: # pylint: disable=unused-argument
optimizer.tell(point, _multiobjective(point.value))
if isinstance(optimizer, es._EvolutionStrategy):
assert optimizer._rank_method is not None # make sure the nsga2 ranker is used


def test_paraportfolio_de() -> None:
workers = 40
opt = optlib.ParaPortfolio(12, budget=100 * workers, num_workers=workers)
for _ in range(3):
cands = [opt.ask() for _ in range(workers)]
for cand in cands:
opt.tell(cand, np.random.rand())

0 comments on commit e944132

Please sign in to comment.