Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Canonicalise LearnerND hull seeding and add regression test
  • Loading branch information
basnijholt committed Oct 1, 2025
commit 95cf36e7dadbade7714a51862ffdd83f8930129e
64 changes: 37 additions & 27 deletions adaptive/learner/learnerND.py
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,8 @@ def __init__(self, func, bounds, loss_per_simplex=None):
# been returned has not been deleted. This checking is done by
# _pop_highest_existing_simplex
self._simplex_queue = SortedKeyList(key=_simplex_evaluation_priority)
self._skipped_bound_points: set[tuple[float, ...]] = set()
self._next_bound_idx = 0
self._bound_match_tol = 1e-10

def new(self) -> LearnerND:
"""Create a new learner with the same function and bounds."""
Expand Down Expand Up @@ -489,6 +490,7 @@ def load_dataframe( # type: ignore[override]
self.function = partial_function_from_dataframe(
self.function, df, function_prefix
)
self._next_bound_idx = 0

@property
def bounds_are_done(self):
Expand Down Expand Up @@ -556,6 +558,27 @@ def _simplex_exists(self, simplex):
simplex = tuple(sorted(simplex))
return simplex in self.tri.simplices

def _is_known_point(self, point):
point = tuple(map(float, point))
if point in self.data or point in self.pending_points:
return True

tolerances = [
max(self._bound_match_tol, self._bound_match_tol * (hi - lo))
for lo, hi in self._bbox
]

def _close(other):
return all(abs(a - b) <= tol for (a, b, tol) in zip(point, other, tolerances))

for existing in self.data.keys():
if _close(existing):
return True
for existing in self.pending_points:
if _close(existing):
return True
return False

def inside_bounds(self, point):
"""Check whether a point is inside the bounds."""
if self._interior is not None:
Expand Down Expand Up @@ -633,24 +656,18 @@ def ask(self, n, tell_pending=True):

def _ask_bound_point(self):
# get the next bound point that is still available
while True:
new_point = next(
p
for p in self._bounds_points
if p not in self.data
and p not in self.pending_points
and p not in self._skipped_bound_points
)
try:
self.tell_pending(new_point)
except ValueError as exc:
if str(exc) == "Point already in triangulation.":
self.pending_points.discard(new_point)
self._skipped_bound_points.add(new_point)
continue
raise
while self._next_bound_idx < len(self._bounds_points):
new_point = self._bounds_points[self._next_bound_idx]
self._next_bound_idx += 1

if self._is_known_point(new_point):
continue

self.tell_pending(new_point)
return new_point, np.inf

raise StopIteration

def _ask_point_without_known_simplices(self):
assert not self._bounds_available
# pick a random point inside the bounds
Expand Down Expand Up @@ -715,20 +732,13 @@ def _ask_best_point(self):
@property
def _bounds_available(self):
return any(
(
p not in self.pending_points
and p not in self.data
and p not in self._skipped_bound_points
)
for p in self._bounds_points
not self._is_known_point(p)
for p in self._bounds_points[self._next_bound_idx :]
)

def _ask(self):
if self._bounds_available:
try:
return self._ask_bound_point() # O(1)
except StopIteration:
pass
return self._ask_bound_point() # O(1)

if self.tri is None:
# All bound points are pending or have been evaluated, but we do not
Expand Down
1 change: 0 additions & 1 deletion adaptive/tests/data/issue_470_boundaries.json

This file was deleted.

Loading
Loading