From 884674a936ee7c3046c4b448ba3db2bc32ea15bc Mon Sep 17 00:00:00 2001 From: Bas Nijholt Date: Wed, 4 Sep 2019 17:43:33 +0200 Subject: [PATCH 1/5] fix problem when learner has no more points for BalancingLearner, closes #213 --- adaptive/learner/balancing_learner.py | 28 +++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/adaptive/learner/balancing_learner.py b/adaptive/learner/balancing_learner.py index d836bdbec..41672e7ad 100644 --- a/adaptive/learner/balancing_learner.py +++ b/adaptive/learner/balancing_learner.py @@ -119,20 +119,28 @@ def strategy(self, strategy): ' strategy="npoints", or strategy="cycle" is implemented.' ) + def _to_select(self, total_points): + to_select = [] + for index, learner in enumerate(self.learners): + # Take the points from the cache + if index not in self._ask_cache: + self._ask_cache[index] = learner.ask(n=1, tell_pending=False) + points, loss_improvements = self._ask_cache[index] + if not points: + # cannot ask for more points + return to_select + to_select.append( + ((index, points[0]), (loss_improvements[0], -total_points[index])) + ) + return to_select + def _ask_and_tell_based_on_loss_improvements(self, n): selected = [] # tuples ((learner_index, point), loss_improvement) total_points = [l.npoints + len(l.pending_points) for l in self.learners] for _ in range(n): - to_select = [] - for index, learner in enumerate(self.learners): - # Take the points from the cache - if index not in self._ask_cache: - self._ask_cache[index] = learner.ask(n=1, tell_pending=False) - points, loss_improvements = self._ask_cache[index] - to_select.append( - ((index, points[0]), (loss_improvements[0], -total_points[index])) - ) - + to_select = self._to_select(total_points) + if not to_select: + break # Choose the optimal improvement. (index, point), (loss_improvement, _) = max(to_select, key=itemgetter(1)) total_points[index] += 1 From 2fedb39e4650365afb952dc8448c51bbef60c506 Mon Sep 17 00:00:00 2001 From: Bas Nijholt Date: Wed, 4 Sep 2019 17:49:45 +0200 Subject: [PATCH 2/5] fix for strategy="loss" and "npoints" --- adaptive/learner/balancing_learner.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/adaptive/learner/balancing_learner.py b/adaptive/learner/balancing_learner.py index 41672e7ad..9898e8810 100644 --- a/adaptive/learner/balancing_learner.py +++ b/adaptive/learner/balancing_learner.py @@ -126,8 +126,7 @@ def _to_select(self, total_points): if index not in self._ask_cache: self._ask_cache[index] = learner.ask(n=1, tell_pending=False) points, loss_improvements = self._ask_cache[index] - if not points: - # cannot ask for more points + if not points: # cannot ask for more points return to_select to_select.append( ((index, points[0]), (loss_improvements[0], -total_points[index])) @@ -139,7 +138,7 @@ def _ask_and_tell_based_on_loss_improvements(self, n): total_points = [l.npoints + len(l.pending_points) for l in self.learners] for _ in range(n): to_select = self._to_select(total_points) - if not to_select: + if not to_select: # cannot ask for more points break # Choose the optimal improvement. (index, point), (loss_improvement, _) = max(to_select, key=itemgetter(1)) @@ -164,7 +163,8 @@ def _ask_and_tell_based_on_loss(self, n): if index not in self._ask_cache: self._ask_cache[index] = self.learners[index].ask(n=1) points, loss_improvements = self._ask_cache[index] - + if not points: # cannot ask for more points + break selected.append(((index, points[0]), loss_improvements[0])) self.tell_pending((index, points[0])) @@ -180,6 +180,8 @@ def _ask_and_tell_based_on_npoints(self, n): if index not in self._ask_cache: self._ask_cache[index] = self.learners[index].ask(n=1) points, loss_improvements = self._ask_cache[index] + if not points: # cannot ask for more points + break total_points[index] += 1 selected.append(((index, points[0]), loss_improvements[0])) self.tell_pending((index, points[0])) From 091d8176a815d84052e6a238ea63bac278102b74 Mon Sep 17 00:00:00 2001 From: Bas Nijholt Date: Wed, 4 Sep 2019 17:58:20 +0200 Subject: [PATCH 3/5] add a failing test --- adaptive/tests/test_balancing_learner.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/adaptive/tests/test_balancing_learner.py b/adaptive/tests/test_balancing_learner.py index fff0ff186..485cc0229 100644 --- a/adaptive/tests/test_balancing_learner.py +++ b/adaptive/tests/test_balancing_learner.py @@ -2,7 +2,7 @@ import pytest -from adaptive.learner import BalancingLearner, Learner1D +from adaptive.learner import BalancingLearner, Learner1D, SequenceLearner from adaptive.runner import simple @@ -44,6 +44,16 @@ def test_distribute_first_points_over_learners(strategy): assert len(set(i_learner)) == len(learners) +@pytest.mark.parametrize("strategy", strategies) +def test_asking_more_points_than_available(strategy): + def dummy(x): + return x + + bl = BalancingLearner([SequenceLearner(dummy, range(5))], strategy=strategy) + bl.ask(100) + bl.ask(100) + + @pytest.mark.parametrize("strategy", strategies) def test_ask_0(strategy): learners = [Learner1D(lambda x: x, bounds=(-1, 1)) for i in range(10)] From 8a798f7f8a6604cd715e5c4b05fd52a8daf26511 Mon Sep 17 00:00:00 2001 From: Bas Nijholt Date: Wed, 4 Sep 2019 17:59:13 +0200 Subject: [PATCH 4/5] make test_asking_more_points_than_available pass --- adaptive/learner/balancing_learner.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/adaptive/learner/balancing_learner.py b/adaptive/learner/balancing_learner.py index 9898e8810..ad5cd124c 100644 --- a/adaptive/learner/balancing_learner.py +++ b/adaptive/learner/balancing_learner.py @@ -146,7 +146,7 @@ def _ask_and_tell_based_on_loss_improvements(self, n): selected.append(((index, point), loss_improvement)) self.tell_pending((index, point)) - points, loss_improvements = map(list, zip(*selected)) + points, loss_improvements = map(list, zip(*selected)) if selected else [], [] return points, loss_improvements def _ask_and_tell_based_on_loss(self, n): @@ -168,7 +168,7 @@ def _ask_and_tell_based_on_loss(self, n): selected.append(((index, points[0]), loss_improvements[0])) self.tell_pending((index, points[0])) - points, loss_improvements = map(list, zip(*selected)) + points, loss_improvements = map(list, zip(*selected)) if selected else [], [] return points, loss_improvements def _ask_and_tell_based_on_npoints(self, n): @@ -186,7 +186,7 @@ def _ask_and_tell_based_on_npoints(self, n): selected.append(((index, points[0]), loss_improvements[0])) self.tell_pending((index, points[0])) - points, loss_improvements = map(list, zip(*selected)) + points, loss_improvements = map(list, zip(*selected)) if selected else [], [] return points, loss_improvements def _ask_and_tell_based_on_cycle(self, n): From 94645b3bb8cb356292d1fd27b68f549f6d4e31d3 Mon Sep 17 00:00:00 2001 From: Bas Nijholt Date: Wed, 4 Sep 2019 18:02:16 +0200 Subject: [PATCH 5/5] rename _to_select -> _ask_all_learners --- adaptive/learner/balancing_learner.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/adaptive/learner/balancing_learner.py b/adaptive/learner/balancing_learner.py index ad5cd124c..a86932f04 100644 --- a/adaptive/learner/balancing_learner.py +++ b/adaptive/learner/balancing_learner.py @@ -119,7 +119,7 @@ def strategy(self, strategy): ' strategy="npoints", or strategy="cycle" is implemented.' ) - def _to_select(self, total_points): + def _ask_all_learners(self, total_points): to_select = [] for index, learner in enumerate(self.learners): # Take the points from the cache @@ -137,7 +137,7 @@ def _ask_and_tell_based_on_loss_improvements(self, n): selected = [] # tuples ((learner_index, point), loss_improvement) total_points = [l.npoints + len(l.pending_points) for l in self.learners] for _ in range(n): - to_select = self._to_select(total_points) + to_select = self._ask_all_learners(total_points) if not to_select: # cannot ask for more points break # Choose the optimal improvement.