From 9ba68ad107a564d3d755a381fd7d40b5027bdfa4 Mon Sep 17 00:00:00 2001 From: Joseph Weston <joseph.weston08@gmail.com> Date: Thu, 31 Aug 2017 11:17:25 +0200 Subject: [PATCH] add logging to runners and add an example to the notebook --- adaptive/runner.py | 29 ++++++++++++++++++++++++- learner.ipynb | 54 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 1 deletion(-) diff --git a/adaptive/runner.py b/adaptive/runner.py index 3c7e72d5..4ad8f089 100644 --- a/adaptive/runner.py +++ b/adaptive/runner.py @@ -17,6 +17,8 @@ class Runner: goal : callable, optional The end condition for the calculation. This function must take the learner as its sole argument, and return True if we should stop. + log : bool, default: False + If True, record the method calls made to the learner by this runner ioloop : asyncio.AbstractEventLoop, optional The ioloop in which to run the learning algorithm. If not provided, the default event loop is used. @@ -27,12 +29,17 @@ class Runner: The underlying task. May be cancelled to stop the runner. learner : Learner The underlying learner. May be queried for its state + log : list or None + Record of the method calls made to the learner, in the format + '(method_name, *args)'. """ - def __init__(self, learner, executor=None, goal=None, *, ioloop=None): + def __init__(self, learner, executor=None, goal=None, *, + log=False, ioloop=None): self.ioloop = ioloop if ioloop else asyncio.get_event_loop() self.executor = _ensure_async_executor(executor, self.ioloop) self.learner = learner + self.log = [] if log else None if goal is None: def goal(_): @@ -50,6 +57,7 @@ class Runner: first_completed = asyncio.FIRST_COMPLETED xs = dict() done = [None] * _get_executor_ncores(self.executor) + do_log = self.log is not None if len(done) == 0: raise RuntimeError('Executor has no workers') @@ -58,6 +66,8 @@ class Runner: while not self.goal(self.learner): # Launch tasks to replace the ones that completed # on the last iteration. + if do_log: + self.log.append(('choose_points', len(done))) for x in self.learner.choose_points(len(done)): xs[self.executor.submit(self.learner.function, x)] = x @@ -69,6 +79,8 @@ class Runner: for fut in done: x = xs.pop(fut) y = await fut + if do_log: + self.log.append(('add_point', x, y)) self.learner.add_point(x, y) finally: # cancel any outstanding tasks @@ -77,6 +89,21 @@ class Runner: raise RuntimeError('Some futures remain uncancelled') +def replay_log(learner, log): + """Apply a sequence of method calls to a learner. + + This is useful for debugging runners. + + Parameters + ---------- + learner : learner.BaseLearner + log : list + contains tuples: '(method_name, *args)'. + """ + for method, *args in log: + getattr(learner, method)(*args) + + # Internal functionality class _AsyncExecutor: diff --git a/learner.ipynb b/learner.ipynb index 0b487462..5c99bbac 100644 --- a/learner.ipynb +++ b/learner.ipynb @@ -421,6 +421,60 @@ "runner.task.result()" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Logging runners" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Runners do their job in the background, which makes introspection quite cumbersome. One way to inspect runners is to instantiate one with `log=True`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "learner = adaptive.learner.Learner1D(f, bounds=(-1, 1))\n", + "runner = adaptive.Runner(learner, goal=lambda l: l.loss() < 0.1,\n", + " log=True)\n", + "adaptive.live_plot(runner)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This gives a the runner a `log` attribute, which is a list of the `learner` methods that were called, as well as their arguments. This is useful because executors typically execute their tasks in a non-deterministic order.\n", + "\n", + "This can be used with `adaptive.runner.replay_log` to perfom the same set of operations on another runner:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "reconstructed_learner = adaptive.learner.Learner1D(f, bounds=(-1, 1))\n", + "adaptive.runner.replay_log(reconstructed_learner, runner.log)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "learner.plot().opts(style=dict(size=6)) * reconstructed_learner.plot()" + ] + }, { "cell_type": "markdown", "metadata": {}, -- GitLab