From 0196d1e1e949ac89f2f36f54e55dd2cb4472f8df Mon Sep 17 00:00:00 2001 From: wkrokosz Date: Wed, 20 Aug 2025 13:57:34 +0200 Subject: [PATCH 1/4] Add customizable number of points for simple runner ask [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- adaptive/runner.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/adaptive/runner.py b/adaptive/runner.py index b9812505..c79496fe 100644 --- a/adaptive/runner.py +++ b/adaptive/runner.py @@ -911,11 +911,12 @@ def simple( npoints_goal: int | None = None, end_time_goal: datetime | None = None, duration_goal: timedelta | int | float | None = None, + points_per_ask: int | None = 1, ): """Run the learner until the goal is reached. - Requests a single point from the learner, evaluates - the function to be learned, and adds the point to the + Requests points from the learner, evaluates + the function to be learned, and adds the points to the learner, until the goal is reached, blocking the current thread. @@ -946,6 +947,9 @@ def simple( calculation. Stop when the current time is larger or equal than ``start_time + duration_goal``. ``duration_goal`` can be a number indicating the number of seconds. + points_per_ask : int, optional + The number of points to ask for between every interpolation rerun. Defaults + to 1, which can introduce significant overhead on long runs. """ goal = _goal( learner, @@ -958,7 +962,7 @@ def simple( ) assert goal is not None while not goal(learner): - xs, _ = learner.ask(1) + xs, _ = learner.ask(points_per_ask) for x in xs: y = learner.function(x) learner.tell(x, y) From 3fc503a928d85635d52cc69035d4ec646256d747 Mon Sep 17 00:00:00 2001 From: wkrokosz Date: Wed, 20 Aug 2025 14:37:57 +0200 Subject: [PATCH 2/4] Fix minor typing issue in learner 2D --- adaptive/learner/learner2D.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adaptive/learner/learner2D.py b/adaptive/learner/learner2D.py index 4d738a71..125fc055 100644 --- a/adaptive/learner/learner2D.py +++ b/adaptive/learner/learner2D.py @@ -817,7 +817,7 @@ def remove_unfinished(self) -> None: if p not in self.data: self._stack[p] = np.inf - def plot(self, n=None, tri_alpha=0): + def plot(self, n=None, tri_alpha=0.0): r"""Plot the Learner2D's current state. This plot function interpolates the data on a regular grid. From 5668510988fa2cac5b0e3dc43db00b486e63a74b Mon Sep 17 00:00:00 2001 From: Bas Nijholt Date: Thu, 21 Aug 2025 13:47:30 -0700 Subject: [PATCH 3/4] Add test for points_per_ask parameter in simple runner Test the new points_per_ask parameter added in PR #484 which allows customizing the number of points requested between interpolation reruns. This reduces overhead for fast functions by batching point requests. The test verifies: - Parameter works with 1D and 2D learners - Default behavior (points_per_ask=1) is preserved - Performance improvement via reduced ask() calls with higher values --- adaptive/tests/test_runner.py | 62 +++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/adaptive/tests/test_runner.py b/adaptive/tests/test_runner.py index f6bb6031..0bb68c59 100644 --- a/adaptive/tests/test_runner.py +++ b/adaptive/tests/test_runner.py @@ -201,3 +201,65 @@ def test_auto_goal(): simple(learner, auto_goal(duration=1e-2, learner=learner)) t_end = time.time() assert t_end - t_start >= 1e-2 + + +def test_simple_points_per_ask(): + """Test that the simple runner respects the points_per_ask parameter (PR #484).""" + + def f(x): + return x**2 + + # Test with 1D learner asking for multiple points at once + learner1 = Learner1D(f, (-1, 1)) + simple(learner1, npoints_goal=20, points_per_ask=5) + assert learner1.npoints >= 20 + + # Test with 2D learner + def f2d(xy): + x, y = xy + return x**2 + y**2 + + learner2 = Learner2D(f2d, ((-1, 1), (-1, 1))) + simple(learner2, npoints_goal=32, points_per_ask=8) + assert learner2.npoints >= 32 + + # Test that default behavior (points_per_ask=1) is preserved + learner3 = Learner1D(f, (-1, 1)) + simple(learner3, npoints_goal=15) + assert learner3.npoints >= 15 + + # Test performance improvement: more points per ask = fewer ask calls + ask_count = 0 + original_ask = Learner1D.ask + + def counting_ask(self, n, tell_pending=True): + nonlocal ask_count + ask_count += 1 + return original_ask(self, n, tell_pending) + + # Monkey patch to count ask calls + Learner1D.ask = counting_ask + + try: + # Test with points_per_ask=1 (default) + learner4 = Learner1D(f, (-1, 1)) + ask_count = 0 + simple(learner4, npoints_goal=10, points_per_ask=1) + ask_count_single = ask_count + + # Test with points_per_ask=5 + learner5 = Learner1D(f, (-1, 1)) + ask_count = 0 + simple(learner5, npoints_goal=10, points_per_ask=5) + ask_count_batch = ask_count + + # When asking for 5 points at a time, we should have fewer ask calls + assert ask_count_batch < ask_count_single + + # Both learners should have reached their goal + assert learner4.npoints >= 10 + assert learner5.npoints >= 10 + + finally: + # Restore original method + Learner1D.ask = original_ask From e67d5e36977a46771a23b37eebf47726bed26a36 Mon Sep 17 00:00:00 2001 From: Bas Nijholt Date: Thu, 21 Aug 2025 13:50:21 -0700 Subject: [PATCH 4/4] use `points_per_ask: int = 1` --- adaptive/runner.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adaptive/runner.py b/adaptive/runner.py index c79496fe..b2e5ec9a 100644 --- a/adaptive/runner.py +++ b/adaptive/runner.py @@ -911,7 +911,7 @@ def simple( npoints_goal: int | None = None, end_time_goal: datetime | None = None, duration_goal: timedelta | int | float | None = None, - points_per_ask: int | None = 1, + points_per_ask: int = 1, ): """Run the learner until the goal is reached.