Skip to content

Commit f2ce50d

Browse files
authored
Merge pull request #1548 from Libensemble/examples/aposmm_nb_animation
Replace flashing live animation with clean post-run version
2 parents deb58af + 026371c commit f2ce50d

1 file changed

Lines changed: 92 additions & 32 deletions

File tree

examples/tutorials/aposmm/aposmm_tutorial_notebook.ipynb

Lines changed: 92 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"source": [
77
"# Parallel Optimization with APOSMM\n",
88
"\n",
9-
"This tutorial demonstrates libEnsemble’s capability to identify multiple minima of simulation output using the built-in APOSMM (Asynchronously Parallel Optimization Solver for finding Multiple Minima) generator function (`gen_f`). In this tutorial, we’ll create a simple simulation function (`sim_f`) that defines a function with multiple minima, then write a libEnsemble calling script that imports APOSMM and parameterizes it to check for minima over a domain of outputs from our `sim_f`.\n",
9+
"This tutorial demonstrates libEnsemble’s capability to identify multiple minima from simulation outputs using the built-in APOSMM (Asynchronously Parallel Optimization Solver for finding Multiple Minima) generator function (`gen_f`). In this tutorial, we’ll create a simple simulation function (`sim_f`) that defines a function with multiple minima, then write a libEnsemble calling script that imports APOSMM and parameterizes it to check for minima over a domain of outputs from our `sim_f`.\n",
1010
"\n",
1111
"Besides libEnsemble and NumPy, SciPy and mpmath are also required dependencies.\n",
1212
"\n",
@@ -43,7 +43,6 @@
4343
"outputs": [],
4444
"source": [
4545
"# Define our simulation function\n",
46-
"\n",
4746
"import numpy as np\n",
4847
"\n",
4948
"\n",
@@ -122,9 +121,9 @@
122121
"cell_type": "markdown",
123122
"metadata": {},
124123
"source": [
125-
"This allocation function starts a single Persistent APOSMM routine and provides ``sim_f`` output for points requested by APOSMM. Points can be sampled points or points from local optimization runs.\n",
124+
"This allocation function starts a single Persistent APOSMM generator to generate points (simulation input parameters), and returns the resulting values from each simulation (run in parallel). Points can be sampled points or points from the parallel local optimization runs.\n",
126125
"\n",
127-
"APOSMM supports a wide variety of external optimizers. The following statements set optimizer settings to ``'scipy'`` to indicate to APOSMM which optimization method to use, and help prevent unnecessary imports or package installations:"
126+
"APOSMM supports a wide variety of external optimizers. The following statement sets the optimizer settings to ``'scipy'`` to indicate to APOSMM which optimization method to use, so it is imported at global scope:"
128127
]
129128
},
130129
{
@@ -142,6 +141,7 @@
142141
"cell_type": "markdown",
143142
"metadata": {},
144143
"source": [
144+
"This script uses the dictionary interface to configure the run (the newer object interface is equally valid).\n",
145145
"Set up ``nworkers``, ``libE_specs``, ``sim_specs``, ``gen_specs``, and ``alloc_specs``:"
146146
]
147147
},
@@ -226,28 +226,7 @@
226226
"source": [
227227
"## Run the Ensemble\n",
228228
"\n",
229-
"Optionally run the next cell to set up a live graphic of the optimization progress during execution.\n",
230-
"\n",
231-
"**WARNING**: The graphic may flicker when the ensemble is running."
232-
]
233-
},
234-
{
235-
"cell_type": "code",
236-
"execution_count": null,
237-
"metadata": {},
238-
"outputs": [],
239-
"source": [
240-
"# Configure to view live progress\n",
241-
"from libensemble.tools.live_data.plot2n import Plot2N\n",
242-
"\n",
243-
"libE_specs[\"live_data\"] = Plot2N(plot_type=\"2d\") # Alt: '3d'"
244-
]
245-
},
246-
{
247-
"cell_type": "markdown",
248-
"metadata": {},
249-
"source": [
250-
"Finally, set `persis_info` (to provide random seeds to workers) and run the ensemble:"
229+
"Finally, set persis_info (to provide random seeds to workers) and run the ensemble."
251230
]
252231
},
253232
{
@@ -290,29 +269,110 @@
290269
" \n",
291270
"The first six values correspond to the local minima for the Six-Hump Camel simulation function.\n",
292271
"\n",
293-
"The 7th value is a repeat minimum, as APOSMM will continue to start local optimization runs.\n",
272+
"The 7th value is a repeat minimum, as APOSMM will continue to start local optimization runs."
273+
]
274+
},
275+
{
276+
"cell_type": "markdown",
277+
"metadata": {},
278+
"source": [
279+
"## Viewing Animation\n",
294280
"\n",
295-
"Please see the [API reference](https://libensemble.readthedocs.io/en/main/examples/aposmm.html) for more APOSMM configuration options and other information.\n",
281+
"The following cell produces a 3D animation showing the random sampling points, \n",
282+
"the points produced by the optimization runs, and the local Minima.\n",
296283
"\n",
284+
"This may take up to about 30 seconds to produce the 3D animation, depending on system."
285+
]
286+
},
287+
{
288+
"cell_type": "code",
289+
"execution_count": null,
290+
"metadata": {},
291+
"outputs": [],
292+
"source": [
293+
"import numpy as np\n",
294+
"import matplotlib.pyplot as plt\n",
295+
"import matplotlib.animation as animation\n",
296+
"from IPython.display import HTML\n",
297+
"from matplotlib.lines import Line2D\n",
298+
"\n",
299+
"def animate_aposmm_3d(H, batch_size):\n",
300+
" x_vals = np.linspace(-2, 2, 50)\n",
301+
" y_vals = np.linspace(-1, 1.1, 50)\n",
302+
" X, Y = np.meshgrid(x_vals, y_vals)\n",
303+
" Z = np.array([six_hump_camel_func(np.array([x, y])) for x, y in zip(X.ravel(), Y.ravel())]).reshape(X.shape)\n",
304+
" fig = plt.figure(figsize=(10, 8))\n",
305+
" ax = fig.add_subplot(111, projection=\"3d\")\n",
306+
" ax.plot_surface(X, Y, Z, cmap=\"winter\", edgecolor='k', linewidth=0.1, antialiased=True, alpha=0.5) \n",
307+
" sc_normal = ax.scatter3D([], [], [], s=6, color=\"black\", marker=\"o\", label=\"Point\")\n",
308+
" sc_localp = ax.scatter3D([], [], [], s=40, color=\"red\", marker=\"^\", label=\"Optimization point\")\n",
309+
" custom_M_marker = Line2D([0], [0], linestyle='None', marker='$\\\\mathrm{M}$',\n",
310+
" markersize=8, markerfacecolor='black', markeredgecolor='black', color='white')\n",
311+
" ax.legend([sc_normal, sc_localp, custom_M_marker], [\"Point\", \"Optimization point\", \"Local minimum\"],loc=\"upper left\")\n",
312+
" fig.tight_layout()\n",
313+
" annotations = []\n",
314+
"\n",
315+
" def update(frame):\n",
316+
" for ann in annotations:\n",
317+
" ann.remove()\n",
318+
" annotations.clear()\n",
319+
" end = min((frame + 1) * batch_size, len(H))\n",
320+
" H_sub = H[:end]\n",
321+
" masks = [~H_sub[\"local_pt\"] & ~H_sub[\"local_min\"], H_sub[\"local_pt\"], H_sub[\"local_min\"]]\n",
322+
" (x_n, y_n, f_n), (x_lp, y_lp, f_lp), (x_lm, y_lm, f_lm) = [\n",
323+
" (H_sub[\"x\"][m, 0], H_sub[\"x\"][m, 1], H_sub[\"f\"][m]) for m in masks\n",
324+
" ]\n",
325+
" sc_normal._offsets3d = (x_n, y_n, f_n)\n",
326+
" sc_localp._offsets3d = (x_lp, y_lp, f_lp)\n",
327+
" for i in range(len(x_lm)):\n",
328+
" annotations.append(ax.text(x_lm[i], y_lm[i], f_lm[i], \"M\", color=\"white\", fontsize=12,\n",
329+
" bbox=dict(facecolor=\"black\", alpha=0.7, pad=2), zorder=999\n",
330+
" ))\n",
331+
" return sc_normal, sc_localp\n",
332+
" total_frames = (len(H) + batch_size - 1) // batch_size\n",
333+
" ani = animation.FuncAnimation(fig, update, frames=total_frames, interval=500, blit=False, repeat=False)\n",
334+
" plt.close(fig)\n",
335+
" return HTML(ani.to_jshtml())\n",
336+
"\n",
337+
"# Reduce batch_size for more refined steps\n",
338+
"animate_aposmm_3d(H, batch_size=50)"
339+
]
340+
},
341+
{
342+
"cell_type": "markdown",
343+
"metadata": {},
344+
"source": [
297345
"## Applications\n",
298346
"\n",
299347
"APOSMM is not limited to evaluating minima from pure Python simulation functions.\n",
300348
"Many common libEnsemble use-cases involve using libEnsemble's Executor to launch user\n",
301349
"applications with parameters requested by APOSMM, then evaluate their output using\n",
302350
"APOSMM, and repeat until minima are identified. A currently supported example\n",
303-
"can be found in libEnsemble's [WarpX Scaling Test](https://github.com/Libensemble/libensemble/tree/main/libensemble/tests/scaling_tests/warpx)"
351+
"can be found in libEnsemble's [WarpX Scaling Test](https://github.com/Libensemble/libensemble/tree/main/libensemble/tests/scaling_tests/warpx)\n",
352+
"\n",
353+
"Please see the [API reference](https://libensemble.readthedocs.io/en/main/examples/aposmm.html) for more APOSMM configuration options and other information."
304354
]
305355
}
306356
],
307357
"metadata": {
308358
"kernelspec": {
309-
"display_name": "Python 3",
359+
"display_name": "Python 3 (ipykernel)",
360+
"language": "python",
310361
"name": "python3"
311362
},
312363
"language_info": {
313-
"name": "python"
364+
"codemirror_mode": {
365+
"name": "ipython",
366+
"version": 3
367+
},
368+
"file_extension": ".py",
369+
"mimetype": "text/x-python",
370+
"name": "python",
371+
"nbconvert_exporter": "python",
372+
"pygments_lexer": "ipython3",
373+
"version": "3.13.1"
314374
}
315375
},
316376
"nbformat": 4,
317-
"nbformat_minor": 2
377+
"nbformat_minor": 4
318378
}

0 commit comments

Comments
 (0)