Skip to content
Snippets Groups Projects

More efficient 'tell_many'

Merged Bas Nijholt requested to merge efficient_tell_many into master
All threads resolved!
Compare and
2 files
+ 105
10
Compare changes
  • Side-by-side
  • Inline
Files
2
@@ -105,7 +105,7 @@ class Learner1D(BaseLearner):
self.losses = {}
self.losses_combined = {}
self.data = sortedcontainers.SortedDict()
self.data = {}
self.pending_points = set()
# A dict {x_n: [x_{n-1}, x_{n+1}]} for quick checking of local
@@ -129,7 +129,17 @@ class Learner1D(BaseLearner):
@property
def vdim(self):
return 1 if self._vdim is None else self._vdim
if self._vdim is None:
if self.data:
y = next(iter(self.data.values()))
try:
self._vdim = len(np.squeeze(y))
except TypeError:
# Means we are taking the length of a float
self._vdim = 1
else:
return 1
return self._vdim
@property
def npoints(self):
@@ -258,12 +268,6 @@ class Learner1D(BaseLearner):
# remove from set of pending points
self.pending_points.discard(x)
if self._vdim is None:
try:
self._vdim = len(np.squeeze(y))
except TypeError:
self._vdim = 1
if not self.bounds[0] <= x <= self.bounds[1]:
return
@@ -288,6 +292,71 @@ class Learner1D(BaseLearner):
self.update_neighbors(x, self.neighbors_combined)
self.update_losses(x, real=False)
def tell_many(self, xs, ys):
if len(xs) > 0.5 * len(self.data) and len(xs) > 2:
# Only run this more efficient method if there are
# at least 2 points and the amount of points added are
# at least half of the number of points already in 'data'.
super().tell_many(xs, ys)
return
# Add data points
for x, y in zip(xs, ys):
self.data[x] = y
# Generate neighbors
_xs = list(self.data.keys())
_xs_pending = list(self.pending_points)
xs = np.array(_xs)
xs_pending = np.array(_xs_pending)
xs_combined = np.array(_xs_pending + _xs)
for i, _xs in enumerate([xs, xs_combined]):
if _xs.size > 0:
indices = _xs.argsort()
xs_left = np.roll(_xs[indices], 1).tolist()
xs_left[0] = None
xs_right = np.roll(_xs[indices], -1).tolist()
xs_right[-1] = None
neighbors = {x: [x_L, x_R] for x, x_L, x_R
in zip(_xs[indices], xs_left, xs_right)}
if i == 0:
self.neighbors = sortedcontainers.SortedDict(neighbors)
else:
self.neighbors_combined = sortedcontainers.SortedDict(neighbors)
# Update scale
self._bbox[0][0] = xs_combined.min()
self._bbox[0][1] = xs_combined.max()
if self.data:
values = np.array(list(self.data.values()))
self._bbox[1] = [values.min(axis=0), values.max(axis=0)]
self._scale[0] = self._bbox[0][1] - self._bbox[0][0]
self._scale[1] = np.max(self._bbox[1][1] - self._bbox[1][0])
self._oldscale = deepcopy(self._scale)
# Calculate intervals for which the losses should be calcualted.
intervals = [(x_mid, x_right) for x_mid, (x_left, x_right)
in self.neighbors.items()][:-1]
for x_left, x_right in intervals:
self.losses[x_left, x_right] = (
self.loss_per_interval((x_left, x_right), self._scale, self.data)
if x_right - x_left >= self._dx_eps else 0)
intervals_combined = [(x_mid, x_right) for x_mid, (x_left, x_right)
in self.neighbors_combined.items()][:-1]
for ival in intervals_combined:
# If this interval exists in 'losses' then copy it otherwise
# calculate it.
if ival in self.losses:
self.losses_combined[ival] = self.losses[ival]
else:
self.update_interpolated_loss_in_interval(*ival)
def ask(self, n, tell_pending=True):
"""Return n points that are expected to maximally reduce the loss."""
points, loss_improvements = self._ask_points_without_adding(n)
@@ -379,8 +448,7 @@ class Learner1D(BaseLearner):
elif not self.vdim > 1:
p = hv.Scatter(self.data) * hv.Path([])
else:
xs = list(self.data.keys())
ys = list(self.data.values())
xs, ys = zip(*sorted(self.data.items()))
p = hv.Path((xs, ys)) * hv.Scatter([])
# Plot with 5% empty margins such that the boundary points are visible
Loading