... | ... |
@@ -31,7 +31,7 @@ def notebook_extension(*, _inline_js=True): |
31 | 31 |
_holoviews_enabled = True |
32 | 32 |
except ModuleNotFoundError: |
33 | 33 |
warnings.warn( |
34 |
- "holoviews is not installed; plotting " "is disabled.", RuntimeWarning |
|
34 |
+ "holoviews is not installed; plotting is disabled.", RuntimeWarning |
|
35 | 35 |
) |
36 | 36 |
|
37 | 37 |
# Load ipywidgets |
... | ... |
@@ -42,7 +42,7 @@ def notebook_extension(*, _inline_js=True): |
42 | 42 |
_ipywidgets_enabled = True |
43 | 43 |
except ModuleNotFoundError: |
44 | 44 |
warnings.warn( |
45 |
- "ipywidgets is not installed; live_info " "is disabled.", RuntimeWarning |
|
45 |
+ "ipywidgets is not installed; live_info is disabled.", RuntimeWarning |
|
46 | 46 |
) |
47 | 47 |
|
48 | 48 |
# Enable asyncio integration |
... | ... |
@@ -233,11 +233,10 @@ def live_info(runner, *, update_interval=0.5): |
233 | 233 |
|
234 | 234 |
def _table_row(i, key, value): |
235 | 235 |
"""Style the rows of a table. Based on the default Jupyterlab table style.""" |
236 |
- style_odd = "text-align: right; padding: 0.5em 0.5em; line-height: 1.0;" |
|
237 |
- style_even = style_odd + "background: var(--md-grey-100);" |
|
238 |
- template = '<tr><th style="{style}">{key}</th><th style="{style}">{value}</th></tr>' |
|
239 |
- style = style_odd if i % 2 == 1 else style_even |
|
240 |
- return template.format(style=style, key=key, value=value) |
|
236 |
+ style = "text-align: right; padding: 0.5em 0.5em; line-height: 1.0;" |
|
237 |
+ if i % 2 == 1: |
|
238 |
+ style += " background: var(--md-grey-100);" |
|
239 |
+ return f'<tr><th style="{style}">{key}</th><th style="{style}">{value}</th></tr>' |
|
241 | 240 |
|
242 | 241 |
|
243 | 242 |
def _info_html(runner): |
color the overhead between red and green
Bas Nijholt authored on 18/12/2019 18:26:30 • Joseph Weston committed on 18/12/2019 18:26:30... | ... |
@@ -250,10 +250,14 @@ def _info_html(runner): |
250 | 250 |
"finished": "green", |
251 | 251 |
}[status] |
252 | 252 |
|
253 |
+ overhead = runner.overhead() |
|
254 |
+ red_level = max(0, min(int(255 * overhead / 100), 255)) |
|
255 |
+ overhead_color = "#{:02x}{:02x}{:02x}".format(red_level, 255 - red_level, 0) |
|
256 |
+ |
|
253 | 257 |
info = [ |
254 | 258 |
("status", f'<font color="{color}">{status}</font>'), |
255 | 259 |
("elapsed time", datetime.timedelta(seconds=runner.elapsed_time())), |
256 |
- ("overhead", f"{runner.overhead():.2f}%"), |
|
260 |
+ ("overhead", f'<font color="{overhead_color}">{overhead:.2f}%</font>'), |
|
257 | 261 |
] |
258 | 262 |
|
259 | 263 |
with suppress(Exception): |
Based on the default style of the Jupyterlab table.
Bas Nijholt authored on 18/12/2019 17:36:23 • GitHub committed on 18/12/2019 17:36:23... | ... |
@@ -228,14 +228,16 @@ def live_info(runner, *, update_interval=0.5): |
228 | 228 |
|
229 | 229 |
runner.ioloop.create_task(update()) |
230 | 230 |
|
231 |
- display( |
|
232 |
- ipywidgets.HBox( |
|
233 |
- (status, cancel), |
|
234 |
- layout=ipywidgets.Layout( |
|
235 |
- border="solid 1px", width="200px", align_items="center" |
|
236 |
- ), |
|
237 |
- ) |
|
238 |
- ) |
|
231 |
+ display(ipywidgets.VBox((status, cancel))) |
|
232 |
+ |
|
233 |
+ |
|
234 |
+def _table_row(i, key, value): |
|
235 |
+ """Style the rows of a table. Based on the default Jupyterlab table style.""" |
|
236 |
+ style_odd = "text-align: right; padding: 0.5em 0.5em; line-height: 1.0;" |
|
237 |
+ style_even = style_odd + "background: var(--md-grey-100);" |
|
238 |
+ template = '<tr><th style="{style}">{key}</th><th style="{style}">{value}</th></tr>' |
|
239 |
+ style = style_odd if i % 2 == 1 else style_even |
|
240 |
+ return template.format(style=style, key=key, value=value) |
|
239 | 241 |
|
240 | 242 |
|
241 | 243 |
def _info_html(runner): |
... | ... |
@@ -260,11 +262,10 @@ def _info_html(runner): |
260 | 262 |
with suppress(Exception): |
261 | 263 |
info.append(("latest loss", f'{runner.learner._cache["loss"]:.3f}')) |
262 | 264 |
|
263 |
- template = '<dt class="ignore-css">{}</dt><dd>{}</dd>' |
|
264 |
- table = "\n".join(template.format(k, v) for k, v in info) |
|
265 |
+ table = "\n".join(_table_row(i, k, v) for i, (k, v) in enumerate(info)) |
|
265 | 266 |
|
266 | 267 |
return f""" |
267 |
- <dl> |
|
268 |
+ <table> |
|
268 | 269 |
{table} |
269 |
- </dl> |
|
270 |
+ </table> |
|
270 | 271 |
""" |
... | ... |
@@ -136,6 +136,7 @@ def live_plot(runner, *, plotter=None, update_interval=2, name=None, normalize=T |
136 | 136 |
|
137 | 137 |
streams = [hv.streams.Stream.define("Next")()] |
138 | 138 |
dm = hv.DynamicMap(plot_generator(), streams=streams) |
139 |
+ dm.cache_size = 1 |
|
139 | 140 |
|
140 | 141 |
if normalize: |
141 | 142 |
# XXX: change when https://github.com/pyviz/holoviews/issues/3637 |
... | ... |
@@ -96,7 +96,7 @@ def live_plot(runner, *, plotter=None, update_interval=2, name=None, normalize=T |
96 | 96 |
|
97 | 97 |
Parameters |
98 | 98 |
---------- |
99 |
- runner : `Runner` |
|
99 |
+ runner : `~adaptive.Runner` |
|
100 | 100 |
plotter : function |
101 | 101 |
A function that takes the learner as a argument and returns a |
102 | 102 |
holoviews object. By default ``learner.plot()`` will be called. |
... | ... |
@@ -16,8 +16,10 @@ _plotly_enabled = False |
16 | 16 |
def notebook_extension(*, _inline_js=True): |
17 | 17 |
"""Enable ipywidgets, holoviews, and asyncio notebook integration.""" |
18 | 18 |
if not in_ipynb(): |
19 |
- raise RuntimeError('"adaptive.notebook_extension()" may only be run ' |
|
20 |
- 'from a Jupyter notebook.') |
|
19 |
+ raise RuntimeError( |
|
20 |
+ '"adaptive.notebook_extension()" may only be run ' |
|
21 |
+ "from a Jupyter notebook." |
|
22 |
+ ) |
|
21 | 23 |
|
22 | 24 |
global _async_enabled, _holoviews_enabled, _ipywidgets_enabled |
23 | 25 |
|
... | ... |
@@ -26,54 +28,60 @@ def notebook_extension(*, _inline_js=True): |
26 | 28 |
_holoviews_enabled = False # After closing a notebook the js is gone |
27 | 29 |
if not _holoviews_enabled: |
28 | 30 |
import holoviews |
29 |
- holoviews.notebook_extension('bokeh', logo=False, inline=_inline_js) |
|
31 |
+ |
|
32 |
+ holoviews.notebook_extension("bokeh", logo=False, inline=_inline_js) |
|
30 | 33 |
_holoviews_enabled = True |
31 | 34 |
except ModuleNotFoundError: |
32 |
- warnings.warn("holoviews is not installed; plotting " |
|
33 |
- "is disabled.", RuntimeWarning) |
|
35 |
+ warnings.warn( |
|
36 |
+ "holoviews is not installed; plotting " "is disabled.", RuntimeWarning |
|
37 |
+ ) |
|
34 | 38 |
|
35 | 39 |
# Load ipywidgets |
36 | 40 |
try: |
37 | 41 |
if not _ipywidgets_enabled: |
38 |
- import ipywidgets |
|
42 |
+ import ipywidgets # noqa: F401 |
|
43 |
+ |
|
39 | 44 |
_ipywidgets_enabled = True |
40 | 45 |
except ModuleNotFoundError: |
41 |
- warnings.warn("ipywidgets is not installed; live_info " |
|
42 |
- "is disabled.", RuntimeWarning) |
|
46 |
+ warnings.warn( |
|
47 |
+ "ipywidgets is not installed; live_info " "is disabled.", RuntimeWarning |
|
48 |
+ ) |
|
43 | 49 |
|
44 | 50 |
# Enable asyncio integration |
45 | 51 |
if not _async_enabled: |
46 |
- get_ipython().magic('gui asyncio') |
|
52 |
+ get_ipython().magic("gui asyncio") # noqa: F821 |
|
47 | 53 |
_async_enabled = True |
48 | 54 |
|
49 | 55 |
|
50 | 56 |
def ensure_holoviews(): |
51 | 57 |
try: |
52 |
- return importlib.import_module('holoviews') |
|
58 |
+ return importlib.import_module("holoviews") |
|
53 | 59 |
except ModuleNotFoundError: |
54 |
- raise RuntimeError('holoviews is not installed; plotting is disabled.') |
|
60 |
+ raise RuntimeError("holoviews is not installed; plotting is disabled.") |
|
55 | 61 |
|
56 | 62 |
|
57 | 63 |
def ensure_plotly(): |
58 | 64 |
global _plotly_enabled |
59 | 65 |
try: |
60 | 66 |
import plotly |
67 |
+ |
|
61 | 68 |
if not _plotly_enabled: |
62 | 69 |
import plotly.graph_objs |
63 | 70 |
import plotly.figure_factory |
64 | 71 |
import plotly.offline |
72 |
+ |
|
65 | 73 |
# This injects javascript and should happen only once |
66 | 74 |
plotly.offline.init_notebook_mode() |
67 | 75 |
_plotly_enabled = True |
68 | 76 |
return plotly |
69 | 77 |
except ModuleNotFoundError: |
70 |
- raise RuntimeError('plotly is not installed; plotting is disabled.') |
|
78 |
+ raise RuntimeError("plotly is not installed; plotting is disabled.") |
|
71 | 79 |
|
72 | 80 |
|
73 | 81 |
def in_ipynb(): |
74 | 82 |
try: |
75 | 83 |
# If we are running in IPython, then `get_ipython()` is always a global |
76 |
- return get_ipython().__class__.__name__ == 'ZMQInteractiveShell' |
|
84 |
+ return get_ipython().__class__.__name__ == "ZMQInteractiveShell" |
|
77 | 85 |
except NameError: |
78 | 86 |
return False |
79 | 87 |
|
... | ... |
@@ -83,8 +91,7 @@ def in_ipynb(): |
83 | 91 |
active_plotting_tasks = dict() |
84 | 92 |
|
85 | 93 |
|
86 |
-def live_plot(runner, *, plotter=None, update_interval=2, |
|
87 |
- name=None, normalize=True): |
|
94 |
+def live_plot(runner, *, plotter=None, update_interval=2, name=None, normalize=True): |
|
88 | 95 |
"""Live plotting of the learner's data. |
89 | 96 |
|
90 | 97 |
Parameters |
... | ... |
@@ -135,14 +142,17 @@ def live_plot(runner, *, plotter=None, update_interval=2, |
135 | 142 |
# is fixed. |
136 | 143 |
dm = dm.map(lambda obj: obj.opts(framewise=True), hv.Element) |
137 | 144 |
|
138 |
- cancel_button = ipywidgets.Button(description='cancel live-plot', |
|
139 |
- layout=ipywidgets.Layout(width='150px')) |
|
145 |
+ cancel_button = ipywidgets.Button( |
|
146 |
+ description="cancel live-plot", layout=ipywidgets.Layout(width="150px") |
|
147 |
+ ) |
|
140 | 148 |
|
141 | 149 |
# Could have used dm.periodic in the following, but this would either spin |
142 | 150 |
# off a thread (and learner is not threadsafe) or block the kernel. |
143 | 151 |
|
144 | 152 |
async def updater(): |
145 |
- event = lambda: hv.streams.Stream.trigger(dm.streams) # XXX: used to be dm.event() |
|
153 |
+ event = lambda: hv.streams.Stream.trigger( # noqa: E731 |
|
154 |
+ dm.streams |
|
155 |
+ ) # XXX: used to be dm.event() |
|
146 | 156 |
# see https://github.com/pyviz/holoviews/issues/3564 |
147 | 157 |
try: |
148 | 158 |
while not runner.task.done(): |
... | ... |
@@ -152,7 +162,7 @@ def live_plot(runner, *, plotter=None, update_interval=2, |
152 | 162 |
finally: |
153 | 163 |
if active_plotting_tasks[name] is asyncio.Task.current_task(): |
154 | 164 |
active_plotting_tasks.pop(name, None) |
155 |
- cancel_button.layout.display = 'none' # remove cancel button |
|
165 |
+ cancel_button.layout.display = "none" # remove cancel button |
|
156 | 166 |
|
157 | 167 |
def cancel(_): |
158 | 168 |
with suppress(KeyError): |
... | ... |
@@ -177,7 +187,7 @@ def should_update(status): |
177 | 187 |
# i.e. we're offline for 12h, with an update_interval of 0.5s, |
178 | 188 |
# and without the reduced probability, we have buffer_size=86400. |
179 | 189 |
# With the correction this is np.log(86400) / np.log(1.1) = 119.2 |
180 |
- return 1.1**buffer_size * random.random() < 1 |
|
190 |
+ return 1.1 ** buffer_size * random.random() < 1 |
|
181 | 191 |
except Exception: |
182 | 192 |
# We catch any Exception because we are using a private API. |
183 | 193 |
return True |
... | ... |
@@ -190,16 +200,19 @@ def live_info(runner, *, update_interval=0.5): |
190 | 200 |
visualized in a Jupyter notebook. |
191 | 201 |
""" |
192 | 202 |
if not _holoviews_enabled: |
193 |
- raise RuntimeError("Live plotting is not enabled; did you run " |
|
194 |
- "'adaptive.notebook_extension()'?") |
|
203 |
+ raise RuntimeError( |
|
204 |
+ "Live plotting is not enabled; did you run " |
|
205 |
+ "'adaptive.notebook_extension()'?" |
|
206 |
+ ) |
|
195 | 207 |
|
196 | 208 |
import ipywidgets |
197 | 209 |
from IPython.display import display |
198 | 210 |
|
199 | 211 |
status = ipywidgets.HTML(value=_info_html(runner)) |
200 | 212 |
|
201 |
- cancel = ipywidgets.Button(description='cancel runner', |
|
202 |
- layout=ipywidgets.Layout(width='100px')) |
|
213 |
+ cancel = ipywidgets.Button( |
|
214 |
+ description="cancel runner", layout=ipywidgets.Layout(width="100px") |
|
215 |
+ ) |
|
203 | 216 |
cancel.on_click(lambda _: runner.cancel()) |
204 | 217 |
|
205 | 218 |
async def update(): |
... | ... |
@@ -212,43 +225,47 @@ def live_info(runner, *, update_interval=0.5): |
212 | 225 |
await asyncio.sleep(0.05) |
213 | 226 |
|
214 | 227 |
status.value = _info_html(runner) |
215 |
- cancel.layout.display = 'none' |
|
228 |
+ cancel.layout.display = "none" |
|
216 | 229 |
|
217 | 230 |
runner.ioloop.create_task(update()) |
218 | 231 |
|
219 |
- display(ipywidgets.HBox( |
|
220 |
- (status, cancel), |
|
221 |
- layout=ipywidgets.Layout(border='solid 1px', |
|
222 |
- width='200px', |
|
223 |
- align_items='center'), |
|
224 |
- )) |
|
232 |
+ display( |
|
233 |
+ ipywidgets.HBox( |
|
234 |
+ (status, cancel), |
|
235 |
+ layout=ipywidgets.Layout( |
|
236 |
+ border="solid 1px", width="200px", align_items="center" |
|
237 |
+ ), |
|
238 |
+ ) |
|
239 |
+ ) |
|
225 | 240 |
|
226 | 241 |
|
227 | 242 |
def _info_html(runner): |
228 | 243 |
status = runner.status() |
229 | 244 |
|
230 |
- color = {'cancelled': 'orange', |
|
231 |
- 'failed': 'red', |
|
232 |
- 'running': 'blue', |
|
233 |
- 'finished': 'green'}[status] |
|
245 |
+ color = { |
|
246 |
+ "cancelled": "orange", |
|
247 |
+ "failed": "red", |
|
248 |
+ "running": "blue", |
|
249 |
+ "finished": "green", |
|
250 |
+ }[status] |
|
234 | 251 |
|
235 | 252 |
info = [ |
236 |
- ('status', f'<font color="{color}">{status}</font>'), |
|
237 |
- ('elapsed time', datetime.timedelta(seconds=runner.elapsed_time())), |
|
238 |
- ('overhead', f'{runner.overhead():.2f}%'), |
|
253 |
+ ("status", f'<font color="{color}">{status}</font>'), |
|
254 |
+ ("elapsed time", datetime.timedelta(seconds=runner.elapsed_time())), |
|
255 |
+ ("overhead", f"{runner.overhead():.2f}%"), |
|
239 | 256 |
] |
240 | 257 |
|
241 | 258 |
with suppress(Exception): |
242 |
- info.append(('# of points', runner.learner.npoints)) |
|
259 |
+ info.append(("# of points", runner.learner.npoints)) |
|
243 | 260 |
|
244 | 261 |
with suppress(Exception): |
245 |
- info.append(('latest loss', f'{runner.learner._cache["loss"]:.3f}')) |
|
262 |
+ info.append(("latest loss", f'{runner.learner._cache["loss"]:.3f}')) |
|
246 | 263 |
|
247 | 264 |
template = '<dt class="ignore-css">{}</dt><dd>{}</dd>' |
248 |
- table = '\n'.join(template.format(k, v) for k, v in info) |
|
265 |
+ table = "\n".join(template.format(k, v) for k, v in info) |
|
249 | 266 |
|
250 |
- return f''' |
|
267 |
+ return f""" |
|
251 | 268 |
<dl> |
252 | 269 |
{table} |
253 | 270 |
</dl> |
254 |
- ''' |
|
271 |
+ """ |
... | ... |
@@ -127,7 +127,7 @@ def live_plot(runner, *, plotter=None, update_interval=2, |
127 | 127 |
else: |
128 | 128 |
yield plotter(runner.learner) |
129 | 129 |
|
130 |
- steams = [hv.streams.Stream.define("Next")()] |
|
130 |
+ streams = [hv.streams.Stream.define("Next")()] |
|
131 | 131 |
dm = hv.DynamicMap(plot_generator(), streams=streams) |
132 | 132 |
|
133 | 133 |
if normalize: |
see https://github.com/pyviz/holoviews/issues/3637
Bas Nijholt authored on 25/04/2019 23:07:57... | ... |
@@ -132,6 +132,8 @@ def live_plot(runner, *, plotter=None, update_interval=2, |
132 | 132 |
dm = hv.DynamicMap(plot_generator(), streams=streams) |
133 | 133 |
|
134 | 134 |
if normalize: |
135 |
+ # XXX: change when https://github.com/pyviz/holoviews/issues/3637 |
|
136 |
+ # is fixed. |
|
135 | 137 |
dm = dm.map(lambda obj: obj.opts(framewise=True), hv.Element) |
136 | 138 |
|
137 | 139 |
cancel_button = ipywidgets.Button(description='cancel live-plot', |
... | ... |
@@ -84,7 +84,8 @@ def in_ipynb(): |
84 | 84 |
active_plotting_tasks = dict() |
85 | 85 |
|
86 | 86 |
|
87 |
-def live_plot(runner, *, plotter=None, update_interval=2, name=None): |
|
87 |
+def live_plot(runner, *, plotter=None, update_interval=2, |
|
88 |
+ name=None, normalize=True): |
|
88 | 89 |
"""Live plotting of the learner's data. |
89 | 90 |
|
90 | 91 |
Parameters |
... | ... |
@@ -99,6 +100,8 @@ def live_plot(runner, *, plotter=None, update_interval=2, name=None): |
99 | 100 |
Name for the `live_plot` task in `adaptive.active_plotting_tasks`. |
100 | 101 |
By default the name is None and if another task with the same name |
101 | 102 |
already exists that other `live_plot` is canceled. |
103 |
+ normalize : bool |
|
104 |
+ Normalize (scale to fit) the frame upon each update. |
|
102 | 105 |
|
103 | 106 |
Returns |
104 | 107 |
------- |
... | ... |
@@ -106,8 +109,10 @@ def live_plot(runner, *, plotter=None, update_interval=2, name=None): |
106 | 109 |
The plot that automatically updates every `update_interval`. |
107 | 110 |
""" |
108 | 111 |
if not _holoviews_enabled: |
109 |
- raise RuntimeError("Live plotting is not enabled; did you run " |
|
110 |
- "'adaptive.notebook_extension()'?") |
|
112 |
+ raise RuntimeError( |
|
113 |
+ "Live plotting is not enabled; did you run " |
|
114 |
+ "'adaptive.notebook_extension()'?" |
|
115 |
+ ) |
|
111 | 116 |
|
112 | 117 |
import holoviews as hv |
113 | 118 |
import ipywidgets |
... | ... |
@@ -123,8 +128,12 @@ def live_plot(runner, *, plotter=None, update_interval=2, name=None): |
123 | 128 |
else: |
124 | 129 |
yield plotter(runner.learner) |
125 | 130 |
|
126 |
- dm = hv.DynamicMap(plot_generator(), |
|
127 |
- streams=[hv.streams.Stream.define('Next')()]) |
|
131 |
+ steams = [hv.streams.Stream.define("Next")()] |
|
132 |
+ dm = hv.DynamicMap(plot_generator(), streams=streams) |
|
133 |
+ |
|
134 |
+ if normalize: |
|
135 |
+ dm = dm.map(lambda obj: obj.opts(framewise=True), hv.Element) |
|
136 |
+ |
|
128 | 137 |
cancel_button = ipywidgets.Button(description='cancel live-plot', |
129 | 138 |
layout=ipywidgets.Layout(width='150px')) |
130 | 139 |
|
... | ... |
@@ -14,7 +14,7 @@ _ipywidgets_enabled = False |
14 | 14 |
_plotly_enabled = False |
15 | 15 |
|
16 | 16 |
|
17 |
-def notebook_extension(): |
|
17 |
+def notebook_extension(*, _inline_js=True): |
|
18 | 18 |
"""Enable ipywidgets, holoviews, and asyncio notebook integration.""" |
19 | 19 |
if not in_ipynb(): |
20 | 20 |
raise RuntimeError('"adaptive.notebook_extension()" may only be run ' |
... | ... |
@@ -27,7 +27,7 @@ def notebook_extension(): |
27 | 27 |
_holoviews_enabled = False # After closing a notebook the js is gone |
28 | 28 |
if not _holoviews_enabled: |
29 | 29 |
import holoviews |
30 |
- holoviews.notebook_extension('bokeh', logo=False) |
|
30 |
+ holoviews.notebook_extension('bokeh', logo=False, inline=_inline_js) |
|
31 | 31 |
_holoviews_enabled = True |
32 | 32 |
except ModuleNotFoundError: |
33 | 33 |
warnings.warn("holoviews is not installed; plotting " |
See https://github.com/pyviz/holoviews/issues/3564
and https://github.com/python-adaptive/adaptive/issues/166
... | ... |
@@ -132,11 +132,13 @@ def live_plot(runner, *, plotter=None, update_interval=2, name=None): |
132 | 132 |
# off a thread (and learner is not threadsafe) or block the kernel. |
133 | 133 |
|
134 | 134 |
async def updater(): |
135 |
+ event = lambda: hv.streams.Stream.trigger(dm.streams) # XXX: used to be dm.event() |
|
136 |
+ # see https://github.com/pyviz/holoviews/issues/3564 |
|
135 | 137 |
try: |
136 | 138 |
while not runner.task.done(): |
137 |
- dm.event() |
|
139 |
+ event() |
|
138 | 140 |
await asyncio.sleep(update_interval) |
139 |
- dm.event() # fire off one last update before we die |
|
141 |
+ event() # fire off one last update before we die |
|
140 | 142 |
finally: |
141 | 143 |
if active_plotting_tasks[name] is asyncio.Task.current_task(): |
142 | 144 |
active_plotting_tasks.pop(name, None) |
See https://www.python.org/dev/peps/pep-0008/#imports
Bas Nijholt authored on 26/11/2018 13:39:12Because after closing the notebook and then opening it, the javascript
is gone. Without this change, one could not load it again.
... | ... |
@@ -3,6 +3,7 @@ import importlib |
3 | 3 |
import asyncio |
4 | 4 |
from contextlib import suppress |
5 | 5 |
import datetime |
6 |
+import random |
|
6 | 7 |
import warnings |
7 | 8 |
|
8 | 9 |
|
... | ... |
@@ -150,6 +151,24 @@ def live_plot(runner, *, plotter=None, update_interval=2, name=None): |
150 | 151 |
return dm |
151 | 152 |
|
152 | 153 |
|
154 |
+def should_update(status): |
|
155 |
+ try: |
|
156 |
+ # Get the length of the write buffer size |
|
157 |
+ buffer_size = len(status.comm.kernel.iopub_thread._events) |
|
158 |
+ |
|
159 |
+ # Make sure to only keep all the messages when the notebook |
|
160 |
+ # is viewed, this means 'buffer_size == 1'. However, when not |
|
161 |
+ # viewing the notebook the buffer fills up. When this happens |
|
162 |
+ # we decide to only add messages to it when a certain probability. |
|
163 |
+ # i.e. we're offline for 12h, with an update_interval of 0.5s, |
|
164 |
+ # and without the reduced probability, we have buffer_size=86400. |
|
165 |
+ # With the correction this is np.log(86400) / np.log(1.1) = 119.2 |
|
166 |
+ return 1.1**buffer_size * random.random() < 1 |
|
167 |
+ except Exception: |
|
168 |
+ # We catch any Exception because we are using a private API. |
|
169 |
+ return True |
|
170 |
+ |
|
171 |
+ |
|
153 | 172 |
def live_info(runner, *, update_interval=0.5): |
154 | 173 |
"""Display live information about the runner. |
155 | 174 |
|
... | ... |
@@ -172,7 +191,12 @@ def live_info(runner, *, update_interval=0.5): |
172 | 191 |
async def update(): |
173 | 192 |
while not runner.task.done(): |
174 | 193 |
await asyncio.sleep(update_interval) |
175 |
- status.value = _info_html(runner) |
|
194 |
+ |
|
195 |
+ if should_update(status): |
|
196 |
+ status.value = _info_html(runner) |
|
197 |
+ else: |
|
198 |
+ await asyncio.sleep(0.05) |
|
199 |
+ |
|
176 | 200 |
status.value = _info_html(runner) |
177 | 201 |
cancel.layout.display = 'none' |
178 | 202 |
|
... | ... |
@@ -7,28 +7,42 @@ import warnings |
7 | 7 |
|
8 | 8 |
|
9 | 9 |
_async_enabled = False |
10 |
-_plotting_enabled = False |
|
10 |
+_holoviews_enabled = False |
|
11 |
+_ipywidgets_enabled = False |
|
12 |
+_plotly_enabled = False |
|
11 | 13 |
|
12 | 14 |
|
13 | 15 |
def notebook_extension(): |
16 |
+ """Enable ipywidgets, holoviews, and asyncio notebook integration.""" |
|
14 | 17 |
if not in_ipynb(): |
15 | 18 |
raise RuntimeError('"adaptive.notebook_extension()" may only be run ' |
16 | 19 |
'from a Jupyter notebook.') |
17 | 20 |
|
18 |
- global _plotting_enabled |
|
19 |
- _plotting_enabled = False |
|
21 |
+ global _async_enabled, _holoviews_enabled, _ipywidgets_enabled |
|
22 |
+ |
|
23 |
+ # Load holoviews |
|
24 |
+ try: |
|
25 |
+ if not _holoviews_enabled: |
|
26 |
+ import holoviews |
|
27 |
+ holoviews.notebook_extension('bokeh', logo=False) |
|
28 |
+ _holoviews_enabled = True |
|
29 |
+ except ModuleNotFoundError: |
|
30 |
+ warnings.warn("holoviews is not installed; plotting " |
|
31 |
+ "is disabled.", RuntimeWarning) |
|
32 |
+ |
|
33 |
+ # Load ipywidgets |
|
20 | 34 |
try: |
21 |
- import ipywidgets |
|
22 |
- import holoviews |
|
23 |
- holoviews.notebook_extension('bokeh', logo=False) |
|
24 |
- _plotting_enabled = True |
|
35 |
+ if not _ipywidgets_enabled: |
|
36 |
+ import ipywidgets |
|
37 |
+ _ipywidgets_enabled = True |
|
25 | 38 |
except ModuleNotFoundError: |
26 |
- warnings.warn("holoviews and (or) ipywidgets are not installed; plotting " |
|
39 |
+ warnings.warn("ipywidgets is not installed; live_info " |
|
27 | 40 |
"is disabled.", RuntimeWarning) |
28 | 41 |
|
29 |
- global _async_enabled |
|
30 |
- get_ipython().magic('gui asyncio') |
|
31 |
- _async_enabled = True |
|
42 |
+ # Enable asyncio integration |
|
43 |
+ if not _async_enabled: |
|
44 |
+ get_ipython().magic('gui asyncio') |
|
45 |
+ _async_enabled = True |
|
32 | 46 |
|
33 | 47 |
|
34 | 48 |
def ensure_holoviews(): |
... | ... |
@@ -38,6 +52,22 @@ def ensure_holoviews(): |
38 | 52 |
raise RuntimeError('holoviews is not installed; plotting is disabled.') |
39 | 53 |
|
40 | 54 |
|
55 |
+def ensure_plotly(): |
|
56 |
+ global _plotly_enabled |
|
57 |
+ try: |
|
58 |
+ import plotly |
|
59 |
+ if not _plotly_enabled: |
|
60 |
+ import plotly.graph_objs |
|
61 |
+ import plotly.figure_factory |
|
62 |
+ import plotly.offline |
|
63 |
+ # This injects javascript and should happen only once |
|
64 |
+ plotly.offline.init_notebook_mode() |
|
65 |
+ _plotly_enabled = True |
|
66 |
+ return plotly |
|
67 |
+ except ModuleNotFoundError: |
|
68 |
+ raise RuntimeError('plotly is not installed; plotting is disabled.') |
|
69 |
+ |
|
70 |
+ |
|
41 | 71 |
def in_ipynb(): |
42 | 72 |
try: |
43 | 73 |
# If we are running in IPython, then `get_ipython()` is always a global |
... | ... |
@@ -72,7 +102,7 @@ def live_plot(runner, *, plotter=None, update_interval=2, name=None): |
72 | 102 |
dm : `holoviews.core.DynamicMap` |
73 | 103 |
The plot that automatically updates every `update_interval`. |
74 | 104 |
""" |
75 |
- if not _plotting_enabled: |
|
105 |
+ if not _holoviews_enabled: |
|
76 | 106 |
raise RuntimeError("Live plotting is not enabled; did you run " |
77 | 107 |
"'adaptive.notebook_extension()'?") |
78 | 108 |
|
... | ... |
@@ -126,7 +156,7 @@ def live_info(runner, *, update_interval=0.5): |
126 | 156 |
Returns an interactive ipywidget that can be |
127 | 157 |
visualized in a Jupyter notebook. |
128 | 158 |
""" |
129 |
- if not _plotting_enabled: |
|
159 |
+ if not _holoviews_enabled: |
|
130 | 160 |
raise RuntimeError("Live plotting is not enabled; did you run " |
131 | 161 |
"'adaptive.notebook_extension()'?") |
132 | 162 |
|
... | ... |
@@ -176,7 +176,7 @@ def _info_html(runner): |
176 | 176 |
with suppress(Exception): |
177 | 177 |
info.append(('latest loss', f'{runner.learner._cache["loss"]:.3f}')) |
178 | 178 |
|
179 |
- template = '<dt>{}</dt><dd>{}</dd>' |
|
179 |
+ template = '<dt class="ignore-css">{}</dt><dd>{}</dd>' |
|
180 | 180 |
table = '\n'.join(template.format(k, v) for k, v in info) |
181 | 181 |
|
182 | 182 |
return f''' |
... | ... |
@@ -20,7 +20,7 @@ def notebook_extension(): |
20 | 20 |
try: |
21 | 21 |
import ipywidgets |
22 | 22 |
import holoviews |
23 |
- holoviews.notebook_extension('bokeh') |
|
23 |
+ holoviews.notebook_extension('bokeh', logo=False) |
|
24 | 24 |
_plotting_enabled = True |
25 | 25 |
except ModuleNotFoundError: |
26 | 26 |
warnings.warn("holoviews and (or) ipywidgets are not installed; plotting " |
... | ... |
@@ -56,21 +56,21 @@ def live_plot(runner, *, plotter=None, update_interval=2, name=None): |
56 | 56 |
|
57 | 57 |
Parameters |
58 | 58 |
---------- |
59 |
- runner : Runner |
|
59 |
+ runner : `Runner` |
|
60 | 60 |
plotter : function |
61 | 61 |
A function that takes the learner as a argument and returns a |
62 |
- holoviews object. By default learner.plot() will be called. |
|
62 |
+ holoviews object. By default ``learner.plot()`` will be called. |
|
63 | 63 |
update_interval : int |
64 | 64 |
Number of second between the updates of the plot. |
65 | 65 |
name : hasable |
66 | 66 |
Name for the `live_plot` task in `adaptive.active_plotting_tasks`. |
67 |
- By default the name is `None` and if another task with the same name |
|
68 |
- already exists that other live_plot is canceled. |
|
67 |
+ By default the name is None and if another task with the same name |
|
68 |
+ already exists that other `live_plot` is canceled. |
|
69 | 69 |
|
70 | 70 |
Returns |
71 | 71 |
------- |
72 |
- dm : holoviews.DynamicMap |
|
73 |
- The plot that automatically updates every update_interval. |
|
72 |
+ dm : `holoviews.core.DynamicMap` |
|
73 |
+ The plot that automatically updates every `update_interval`. |
|
74 | 74 |
""" |
75 | 75 |
if not _plotting_enabled: |
76 | 76 |
raise RuntimeError("Live plotting is not enabled; did you run " |
... | ... |
@@ -174,6 +174,9 @@ def _info_html(runner): |
174 | 174 |
with suppress(Exception): |
175 | 175 |
info.append(('# of points', runner.learner.npoints)) |
176 | 176 |
|
177 |
+ with suppress(Exception): |
|
178 |
+ info.append(('latest loss', f'{runner.learner._cache["loss"]:.3f}')) |
|
179 |
+ |
|
177 | 180 |
template = '<dt>{}</dt><dd>{}</dd>' |
178 | 181 |
table = '\n'.join(template.format(k, v) for k, v in info) |
179 | 182 |
|
... | ... |
@@ -1,6 +1,7 @@ |
1 | 1 |
# -*- coding: utf-8 -*- |
2 | 2 |
import importlib |
3 | 3 |
import asyncio |
4 |
+from contextlib import suppress |
|
4 | 5 |
import datetime |
5 | 6 |
from pkg_resources import parse_version |
6 | 7 |
import warnings |
... | ... |
@@ -110,10 +111,8 @@ def live_plot(runner, *, plotter=None, update_interval=2, name=None): |
110 | 111 |
cancel_button.layout.display = 'none' # remove cancel button |
111 | 112 |
|
112 | 113 |
def cancel(_): |
113 |
- try: |
|
114 |
+ with suppress(KeyError): |
|
114 | 115 |
active_plotting_tasks[name].cancel() |
115 |
- except KeyError: |
|
116 |
- pass |
|
117 | 116 |
|
118 | 117 |
active_plotting_tasks[name] = runner.ioloop.create_task(updater()) |
119 | 118 |
cancel_button.on_click(cancel) |
... | ... |
@@ -172,10 +171,8 @@ def _info_html(runner): |
172 | 171 |
('overhead', f'{runner.overhead():.2f}%'), |
173 | 172 |
] |
174 | 173 |
|
175 |
- try: |
|
174 |
+ with suppress(Exception): |
|
176 | 175 |
info.append(('# of points', runner.learner.npoints)) |
177 |
- except Exception: |
|
178 |
- pass |
|
179 | 176 |
|
180 | 177 |
template = '<dt>{}</dt><dd>{}</dd>' |
181 | 178 |
table = '\n'.join(template.format(k, v) for k, v in info) |
... | ... |
@@ -161,19 +161,15 @@ def live_info(runner, *, update_interval=0.5): |
161 | 161 |
def _info_html(runner): |
162 | 162 |
status = runner.status() |
163 | 163 |
|
164 |
- stat_color = {'cancelled': 'orange', |
|
165 |
- 'failed': 'red', |
|
166 |
- 'running': 'blue', |
|
167 |
- 'finished': 'green'}[status] |
|
168 |
- |
|
169 |
- performance = runner.performance() |
|
170 |
- perf_color = 'green' if performance > 1 else 'red' |
|
164 |
+ color = {'cancelled': 'orange', |
|
165 |
+ 'failed': 'red', |
|
166 |
+ 'running': 'blue', |
|
167 |
+ 'finished': 'green'}[status] |
|
171 | 168 |
|
172 | 169 |
info = [ |
173 |
- ('status', f'<font color="{stat_color}">{status}</font>'), |
|
170 |
+ ('status', f'<font color="{color}">{status}</font>'), |
|
174 | 171 |
('elapsed time', datetime.timedelta(seconds=runner.elapsed_time())), |
175 |
- ('performance', f'<font color="{perf_color}">{performance:.1f}</font>'), |
|
176 |
- ('efficiency', f'{runner.efficiency():.2f}%'), |
|
172 |
+ ('overhead', f'{runner.overhead():.2f}%'), |
|
177 | 173 |
] |
178 | 174 |
|
179 | 175 |
try: |
... | ... |
@@ -173,6 +173,7 @@ def _info_html(runner): |
173 | 173 |
('status', f'<font color="{stat_color}">{status}</font>'), |
174 | 174 |
('elapsed time', datetime.timedelta(seconds=runner.elapsed_time())), |
175 | 175 |
('performance', f'<font color="{perf_color}">{performance:.1f}</font>'), |
176 |
+ ('efficiency', f'{runner.efficiency():.2f}%'), |
|
176 | 177 |
] |
177 | 178 |
|
178 | 179 |
try: |
... | ... |
@@ -161,15 +161,18 @@ def live_info(runner, *, update_interval=0.5): |
161 | 161 |
def _info_html(runner): |
162 | 162 |
status = runner.status() |
163 | 163 |
|
164 |
- color = {'cancelled': 'orange', |
|
165 |
- 'failed': 'red', |
|
166 |
- 'running': 'blue', |
|
167 |
- 'finished': 'green'}[status] |
|
164 |
+ stat_color = {'cancelled': 'orange', |
|
165 |
+ 'failed': 'red', |
|
166 |
+ 'running': 'blue', |
|
167 |
+ 'finished': 'green'}[status] |
|
168 |
+ |
|
169 |
+ performance = runner.performance() |
|
170 |
+ perf_color = 'green' if performance > 1 else 'red' |
|
168 | 171 |
|
169 | 172 |
info = [ |
170 |
- ('status', f'<font color="{color}">{status}</font>'), |
|
173 |
+ ('status', f'<font color="{stat_color}">{status}</font>'), |
|
171 | 174 |
('elapsed time', datetime.timedelta(seconds=runner.elapsed_time())), |
172 |
- (f'efficiency', f'{runner.efficiency():.1f}%'), |
|
175 |
+ ('performance', f'<font color="{perf_color}">{performance:.1f}</font>'), |
|
173 | 176 |
] |
174 | 177 |
|
175 | 178 |
try: |
... | ... |
@@ -166,13 +166,10 @@ def _info_html(runner): |
166 | 166 |
'running': 'blue', |
167 | 167 |
'finished': 'green'}[status] |
168 | 168 |
|
169 |
- t_total = runner.elapsed_time() |
|
170 |
- efficiency = (t_total - runner.time_ask_tell) / t_total * 100 |
|
171 |
- |
|
172 | 169 |
info = [ |
173 | 170 |
('status', f'<font color="{color}">{status}</font>'), |
174 |
- ('elapsed time', datetime.timedelta(seconds=t_total)), |
|
175 |
- (f'efficiency', f'{efficiency:.1f}%'), |
|
171 |
+ ('elapsed time', datetime.timedelta(seconds=runner.elapsed_time())), |
|
172 |
+ (f'efficiency', f'{runner.efficiency():.1f}%'), |
|
176 | 173 |
] |
177 | 174 |
|
178 | 175 |
try: |
... | ... |
@@ -166,9 +166,13 @@ def _info_html(runner): |
166 | 166 |
'running': 'blue', |
167 | 167 |
'finished': 'green'}[status] |
168 | 168 |
|
169 |
+ t_total = runner.elapsed_time() |
|
170 |
+ efficiency = (t_total - runner.time_ask_tell) / t_total * 100 |
|
171 |
+ |
|
169 | 172 |
info = [ |
170 | 173 |
('status', f'<font color="{color}">{status}</font>'), |
171 |
- ('elapsed time', datetime.timedelta(seconds=runner.elapsed_time())), |
|
174 |
+ ('elapsed time', datetime.timedelta(seconds=t_total)), |
|
175 |
+ (f'efficiency', f'{efficiency:.1f}%'), |
|
172 | 176 |
] |
173 | 177 |
|
174 | 178 |
try: |
> This is deprecated in traitlets 4.2. This error will be raised in a future release of traitlets.
Bas Nijholt authored on 24/05/2018 07:47:31... | ... |
@@ -23,7 +23,7 @@ def notebook_extension(): |
23 | 23 |
holoviews.notebook_extension('bokeh') |
24 | 24 |
_plotting_enabled = True |
25 | 25 |
except ModuleNotFoundError: |
26 |
- warnings.warn("holoviews and ipywidgets are not installed; plotting " |
|
26 |
+ warnings.warn("holoviews and (or) ipywidgets are not installed; plotting " |
|
27 | 27 |
"is disabled.", RuntimeWarning) |
28 | 28 |
|
29 | 29 |
global _async_enabled |
This provides a better UX than leaving a useless button dangling.
Joseph Weston authored on 19/02/2018 14:45:12... | ... |
@@ -80,6 +80,9 @@ def live_plot(runner, *, plotter=None, update_interval=2, name=None): |
80 | 80 |
import ipywidgets |
81 | 81 |
from IPython.display import display |
82 | 82 |
|
83 |
+ if name in active_plotting_tasks: |
|
84 |
+ active_plotting_tasks[name].cancel() |
|
85 |
+ |
|
83 | 86 |
def plot_generator(): |
84 | 87 |
while True: |
85 | 88 |
if not plotter: |
... | ... |
@@ -89,7 +92,8 @@ def live_plot(runner, *, plotter=None, update_interval=2, name=None): |
89 | 92 |
|
90 | 93 |
dm = hv.DynamicMap(plot_generator(), |
91 | 94 |
streams=[hv.streams.Stream.define('Next')()]) |
92 |
- |
|
95 |
+ cancel_button = ipywidgets.Button(description='cancel live-plot', |
|
96 |
+ layout=ipywidgets.Layout(width='150px')) |
|
93 | 97 |
|
94 | 98 |
# Could have used dm.periodic in the following, but this would either spin |
95 | 99 |
# off a thread (and learner is not threadsafe) or block the kernel. |
... | ... |
@@ -103,12 +107,7 @@ def live_plot(runner, *, plotter=None, update_interval=2, name=None): |
103 | 107 |
finally: |
104 | 108 |
if active_plotting_tasks[name] is asyncio.Task.current_task(): |
105 | 109 |
active_plotting_tasks.pop(name, None) |
106 |
- |
|
107 |
- global active_plotting_tasks |
|
108 |
- if name in active_plotting_tasks: |
|
109 |
- active_plotting_tasks[name].cancel() |
|
110 |
- |
|
111 |
- active_plotting_tasks[name] = asyncio.get_event_loop().create_task(updater()) |
|
110 |
+ cancel_button.layout.display = 'none' # remove cancel button |
|
112 | 111 |