Browse code

interpolate loss, and do not interpolate the actual points

Jorn Hoofwijk authored on 24/04/2018 14:34:18
Showing 1 changed files
... ...
@@ -10,6 +10,7 @@ import sortedcontainers
10 10
 from ..notebook_integration import ensure_holoviews
11 11
 from .base_learner import BaseLearner
12 12
 
13
+
13 14
 def uniform_loss(interval, scale, function_values):
14 15
     """Loss function that samples the domain uniformly.
15 16
 
... ...
@@ -130,25 +131,55 @@ class Learner1D(BaseLearner):
130 131
         else:
131 132
             return max(losses.values())
132 133
 
133
-    def update_losses(self, x, data, neighbors, losses, real=True):
134
-        x_lower, x_upper = neighbors[x]
134
+    def update_losses(self, x, real=True):
135 135
         if real:
136
+            x_lower, x_upper = self.get_neighbors(x, self.neighbors)
136 137
             if x_lower is not None:
137
-                losses[x_lower, x] = self.loss_per_interval((x_lower, x),
138
-                                                            self._scale, data)
138
+                self.losses[x_lower, x] = self.loss_per_interval((x_lower, x),
139
+                                                                 self._scale, self.data)
140
+                start = self.neighbors_combined.bisect_right(x_lower)
141
+                end = self.neighbors_combined.bisect_left(x)
142
+                for i in range(start, end):
143
+                    a, b = self.neighbors_combined.iloc[i], self.neighbors_combined.iloc[i + 1]
144
+                    self.losses_combined[a, b] = (b - a) * self.losses[x_lower, x] / (x - x_lower)
145
+                if start == end:
146
+                    self.losses_combined[x_lower, x] = self.losses[x_lower, x]
147
+
139 148
             if x_upper is not None:
140
-                losses[x, x_upper] = self.loss_per_interval((x, x_upper),
141
-                                                            self._scale, data)
149
+                self.losses[x, x_upper] = self.loss_per_interval((x, x_upper),
150
+                                                                 self._scale, self.data)
151
+                start = self.neighbors_combined.bisect_right(x)
152
+                end = self.neighbors_combined.bisect_left(x_upper)
153
+                for i in range(start, end):
154
+                    a, b = self.neighbors_combined.iloc[i], self.neighbors_combined.iloc[i + 1]
155
+                    self.losses_combined[a, b] = (b - a) * self.losses[x, x_upper] / (x_upper - x)
156
+                if start == end:
157
+                    self.losses_combined[x, x_upper] = self.losses[x, x_upper]
158
+
159
+            try:
160
+                del self.losses[x_lower, x_upper]
161
+            except KeyError:
162
+                pass
142 163
         else:
164
+            x_lower, x_upper = self.get_neighbors(x, self.neighbors)
165
+            a, b = self.get_neighbors(x, self.neighbors_combined)
143 166
             if x_lower is not None and x_upper is not None:
144 167
                 # assert losses[x_lower, x_upper] exists
145
-                losses[x_lower, x] = (x - x_lower) * losses[x_lower, x_upper] / (x_upper - x_lower)
146
-                losses[x, x_upper] = (x_upper - x) * losses[x_lower, x_upper] / (x_upper - x_lower)
147
-
148
-        try:
149
-            del losses[x_lower, x_upper]
150
-        except KeyError:
151
-            pass
168
+                self.losses_combined[a, x] = (x - a) * self.losses[x_lower, x_upper] / (x_upper - x_lower)
169
+                self.losses_combined[x, b] = (b - x) * self.losses[x_lower, x_upper] / (x_upper - x_lower)
170
+            # else:
171
+            #     self.losses_combined[a, x] = float('inf')
172
+            #     self.losses_combined[x, b] = float('inf')
173
+
174
+            try:
175
+                del self.losses_combined[a, b]
176
+            except KeyError:
177
+                pass
178
+
179
+    def get_neighbors(self, x, neighbors):
180
+        if x in neighbors:
181
+            return neighbors[x]
182
+        return self.find_neighbors(x, neighbors)
152 183
 
153 184
     def find_neighbors(self, x, neighbors):
154 185
         pos = neighbors.bisect_left(x)
... ...
@@ -192,7 +223,7 @@ class Learner1D(BaseLearner):
192 223
 
193 224
     def add_point(self, x, y):
194 225
         real = y is not None
195
-        
226
+
196 227
         if real:
197 228
             # Add point to the real data dict and pop from the unfinished
198 229
             # data_interp dict.
... ...
@@ -235,37 +266,28 @@ class Learner1D(BaseLearner):
235 266
         # Update the scale
236 267
         self.update_scale(x, y)
237 268
 
238
-        # Interpolate
239
-        for _x, _y in self.data_interp.items():
240
-            if _y is None:
241
-                if len(self.data) >=2:
242
-                    i = self.data.bisect_left(_x)
243
-                    if i == 0:
244
-                        i_left, i_right = (0, 1)
245
-                    elif i == len(self.data):
246
-                        i_left, i_right = (-2, -1)
247
-                    else:
248
-                        i_left, i_right = (i - 1, i)
249
-                    x_left, x_right = self.data.iloc[i_left], self.data.iloc[i_right]
250
-                    y_left, y_right = self.data[x_left], self.data[x_right]
251
-                    dx = x_right - x_left
252
-                    dy = y_right - y_left
253
-                    self.data_interp[_x] = (dy / dx) * (_x - x_left) + y_left
254
-
255 269
         # Update the losses
256
-        self.update_losses(x, self.data_combined, self.neighbors_combined,
257
-                           self.losses_combined, real)
270
+        self.update_losses(x, real)
258 271
         if real:
259
-            self.update_losses(x, self.data, self.neighbors, self.losses)
272
+            self.update_losses(x)
260 273
 
261
-        # If the scale has doubled, recompute all losses.
262
-        if self._scale > self._oldscale * 2:
274
+        # If the scale has increased enough, recompute all losses.
275
+        if self._scale[1] > self._oldscale[1] * 1.1:
263 276
             self.losses = {xs: self.loss_per_interval(xs, self._scale, self.data)
264 277
                            for xs in self.losses}
265
-            self.losses_combined = {x: self.loss_per_interval(x, self._scale,
266
-                                                              self.data_combined)
267
-                                    for x in self.losses_combined}
268
-            self._oldscale = self._scale
278
+
279
+            def interpolated_loss(x):
280
+                x_lower, x_upper = self.get_neighbors((x[0] + x[1]) / 2, self.neighbors)
281
+                if x_lower is not None and x_upper is not None:
282
+                    return self.losses[x_lower, x_upper] / (x_upper - x_lower) * (x[1] - x[0])
283
+                return float('inf')
284
+
285
+            self.losses_combined = {
286
+                x: interpolated_loss(x)
287
+                for x in self.losses_combined
288
+            }
289
+            self._oldscale = deepcopy(self._scale)
290
+
269 291
 
270 292
     def choose_points(self, n, add_data=True):
271 293
         """Return n points that are expected to maximally reduce the loss."""