Skip to content

Commit

Permalink
Complete draft of the Dynamic Map tutorial
Browse files Browse the repository at this point in the history
  • Loading branch information
jlstevens committed Feb 10, 2016
1 parent 22cbf18 commit 48155ec
Showing 1 changed file with 196 additions and 9 deletions.
205 changes: 196 additions & 9 deletions doc/Tutorials/Dynamic_Map.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -549,8 +549,7 @@
},
"outputs": [],
"source": [
"def sine_gen(freq=0.5):\n",
" phase=0\n",
"def sine_gen(phase=0, freq=0.5):\n",
" while True:\n",
" yield hv.Image(np.sin(phase + (freq*x**2+freq*y**2)))\n",
" phase+=0.2"
Expand Down Expand Up @@ -608,8 +607,7 @@
},
"outputs": [],
"source": [
"def sine_kv_gen(freq=0.5):\n",
" phase=0\n",
"def sine_kv_gen(phase=0, freq=0.5):\n",
" while True:\n",
" yield (phase, hv.Image(np.sin(phase + (freq*x**2+freq*y**2))))\n",
" phase+=0.2\n",
Expand Down Expand Up @@ -646,8 +644,7 @@
},
"outputs": [],
"source": [
"def sine_kv_gen_2D(freq=0.5):\n",
" phase=0\n",
"def sine_kv_gen_2D(phase=0, freq=0.5):\n",
" while True:\n",
" yield ((phase, freq), hv.Image(np.sin(phase + (freq*x**2+freq*y**2))))\n",
" phase+=0.2\n",
Expand Down Expand Up @@ -747,7 +744,23 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"We are given only the repr to reflect that the generator is exhausted. However, as the process of iteration has populated the cache, we can still view the output as a ``HoloMap``:"
"We are given only the repr to reflect that the generator is exhausted. However, as the process of iteration has populated the cache, we can still view the output as a ``HoloMap`` using ``hv.HoloMap(dmap)`` as before."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Counter mode and temporal state"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Open mode is intended to interface live data streams or simulations with HoloViews. The ``DynamicMap`` to generate live visualizations for as long as new data is requested. Although this works for simple cases, Python generators have problematic limitations that can be resolved using 'counter' mode.\n",
"\n",
"In this example, lets say we have a simulation or data recording where time increases in integer steps:"
]
},
{
Expand All @@ -758,7 +771,91 @@
},
"outputs": [],
"source": [
"hv.HoloMap(dmap)"
"def time_gen(time=1):\n",
" while True:\n",
" yield time\n",
" time += 1\n",
" \n",
"time = time_gen()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now we have two generators that return Images that are a function of the simulation time. Here, they have identical output except one of the outputs includes additive noise:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"ls = np.linspace(0, 10, 200)\n",
"xx, yy = np.meshgrid(ls, ls)\n",
"\n",
"def cells():\n",
" while True:\n",
" t = time.next()\n",
" arr = np.sin(xx+t)*np.cos(yy+t)\n",
" yield hv.Image(arr)\n",
"\n",
"def cells_noisy():\n",
" while True:\n",
" t = time.next()\n",
" arr = np.sin(xx+t)*np.cos(yy+t)\n",
" yield hv.Image(arr + 0.2*np.random.rand(200,200))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now let's create a Layout using these two generators:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"hv.DynamicMap(cells(), kdims=['time']) + hv.DynamicMap(cells_noisy(), kdims=['time'])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If you pause the animation, you'll see that these two outputs are *not* in phase, despite the fact that the generators are defined identically (modulo the additive noise)!\n",
"\n",
"The issue is that generators are used via the ``next()`` interface and when either generator is called, the simultation time is increased. In other words, the noisy version in subfigure **B** is actually displayed at a later time than in subfigure **A**.\n",
"\n",
"This is a fundamental issue as the ``next`` method does not take arguments. What we want is for all the ``DynamicMaps`` presented in a Layout to share a common simulation time that is only incremented by interaction with the scrubber widget. This is exactly the sort of situation where you want to use counter mode."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Handling time-dependent state"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To define a ``DynamicMap`` in counter mode:\n",
"\n",
"* Leave one or more dimensions *unbounded* (as in open mode)\n",
"* Supply a callable (as in bounded mode) that accepts *one* argument\n",
"\n",
"This callable should act in the same way as the generators of open mode except the output in controlled by the single counter argument."
]
},
{
Expand All @@ -768,7 +865,97 @@
"collapsed": true
},
"outputs": [],
"source": []
"source": [
"ls = np.linspace(0, 10, 200)\n",
"xx, yy = np.meshgrid(ls, ls)\n",
"\n",
"def cells_counter(t):\n",
" while True:\n",
" arr = np.sin(xx+t)*np.cos(yy+t)\n",
" return hv.Image(arr)\n",
"\n",
"def cells_noisy_counter(t):\n",
" while True:\n",
" arr = np.sin(xx+t)*np.cos(yy+t)\n",
" return hv.Image(arr + 0.2*np.random.rand(200,200))"
]

This comment has been minimized.

Copy link
@jbednar

jbednar May 11, 2016

Member

What does while True do here in either of these functions? Does it actually achieve anything? It does in the generator case, but this isn't a generator...

This comment has been minimized.

Copy link
@jlstevens

jlstevens May 11, 2016

Author Contributor

good question! From the code I can see..it does nothing.

},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now if we supply these functions instead of generators:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"hv.DynamicMap(cells_counter, kdims=['time']) + hv.DynamicMap(cells_noisy_counter, kdims=['time'])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now **A** and **B** are correctly in phase.\n",
"\n",
"Unfortunately, a counter is too simple to describe simultation time which is typically a float with real world units. To address this, we can simply return the actual key values we want along the time dimension, just as was demonstrated in open mode using generators:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"ls = np.linspace(0, 10, 200)\n",
"xx, yy = np.meshgrid(ls, ls)\n",
"\n",
"# Example of a global simulation time\n",
"# typical in many applications\n",
"t = 0 \n",
" \n",
"def cells_counter_kv(c):\n",
" global t\n",
" t = 0.1 * c\n",
" while True:\n",
" arr = np.sin(xx+t)*np.cos(yy+t)\n",
" return (t, hv.Image(arr))\n",
"\n",
"def cells_noisy_counter_kv(c):\n",
" global t\n",
" t = 0.1 * c\n",
" while True:\n",
" arr = np.sin(xx+t)*np.cos(yy+t)\n",
" return (t, hv.Image(arr + 0.2*np.random.rand(200,200)))\n",
" \n",
"hv.DynamicMap(cells_counter_kv, kdims=['time']) + hv.DynamicMap(cells_noisy_counter_kv, kdims=['time'])"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"print(\"The global simulation time is now t=%f\" % t)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ensuring that the HoloViews counter maps to a suitable simulation time is the responsibility of the user. However, once a consistent scheme is configured, the callable in each ``DynamicMap`` can specify the desired simulation time. If the requested simulation time is the same as the current simulation time, nothing needs to happen. Otherwise, the simulator can be run forward by the requested amount."
]
}
],
"metadata": {
Expand Down

0 comments on commit 48155ec

Please sign in to comment.