...
|
...
|
@@ -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."""
|