We continue exploring two-dimensional graphics with matplotlib, illustrating with examples how to program one-dimensional map tools: cobweb diagrams, bifurcation diagrams, and histograms. We use these, for example, to test my claim that the veils seen in the Logistic Map bifurcation diagram are determined by where the logistic map's maximum visits.
Complete programs will be described and links provided so you can download them for study and modification. Be sure to move them to a project directory so that you can modify and save them.
Simple plot simple_plot.py: We start with the simplest example of using matplotlib's plot() function, which we introduced last time. Here we illustrate how to label axes and provide a heading for the plot.
Recall that plot() has many, many options. You can easily remind yourself of these within iPython by using the plot? query.
Another plot: simpleplot_2.py shows how to plot a user-defined function and how to plot two different graphs in the same display with subplot(). The program also shows how to plot using various line styles and data tokens.
By uncommenting the line near the end you can also save the plot to a PNG bitmap file at a resolution of 600 dots-per-inch (dpi):
savefig('simple_plot_2', dpi=600) |
Run the program again and you'll see the screen display, of course. If you look in your working directory, though, you'll see a file simple_plot_2.png. In iPython you can use a shell escape (!) to see the file in a previewer:
In [12]: !open simple_plot_2.png |
The open command is the Macintosh method to view the graphic in its Preview application. But, generally, you can fire up a browser and open the file to see the saved figure. Assuming that the ImageMagick package was installed, you can use its display function to show filename.png on a system with X Windows.
In [13]: !display simple_plot_2.png |
For such a simple plot, the png graphic is a fairly big file (327K on my Mac!):
In [14]: ls -lat simple_plot_2.png -rw-r--r-- 1 chaos admin 327074 Apr 30 17:57 simple_plot_2.png |
This is generally true for bitmap graphics. This is also one reason using a vector-based format, such as PostScript or SVG, is often a good idea. Since only the plotted objects are stored, and not every pixel, vector-based formats save a good deal of space. The other benefits, in addition to saving disk space, is that vector-based formats have scalable resolution and are editable by (say) Adobe Illustrator, in the case of PostScript.
matplotlib allows for PostScript output. Uncomment the line
#savefig('simple_plot_2.ps', dpi=600) |
and re-run the program. The output will be in the specified file:
In [38]: cat simple_plot_2.ps %!PS-Adobe-3.0 %%Title: simple_plot_2.ps %%Creator: matplotlib version 0.90.0, http://matplotlib.sourceforge.net/ %%CreationDate: Sun Apr 29 17:58:08 2007 ... |
Lots of output. Try viewing simple_plot_2.ps in a PostScript viewer, if you have one.
It's markedly smaller:
In [16]: ls -lat simple_plot_2.ps -rw-r--r-- 1 chaos chaos 99477 Apr 30 18:02 simple_plot_2.ps |
As we saw matplotlib's subplot() function makes it easy to compose graphics with many plots. multiplot.py shows you how to put multiple plots under programmatic control. This is very useful when you have many outputs from a simulation (say), but want an integrated view to get the big picture.
multiplot.py as a graphic is way too busy, but you get the idea: Make Python work for you!
We now have enough Python and matplotlib use under our belts that we can put the basic elements together to look at time series of logistic map iterates.
The following gives a series of programs, increasing in sophistication, and a short description of the purpose of each. In addition to running them and varying them in the ways suggested, you should study their structure to see how the algorithms are designed to achieve the claimed purpose and how Python features are used.
Logistic map times series: LogisticMap.py produces a series of iterates, starting from an initial condition, storing the result in an array and then passing this array to the plot() function.
I set the control parameter to r = 3.83 which is in the period-3 window. Notice that there a complicated transient that eventually settles down to a stable period-3 orbit. The complicated transient is a hint of the chaos that is nearby in parameter space. That is, the period-3 orbit is in the midst of the chaotic parameter regime of the logistic map.
Sensitivity to initial conditions LogisticMap2ICs.py: It is very instructive to look at the time series from two different, but very close, initial conditions. This time we use a “generic” chaotic parameter value: r = 3.7.
The two initial conditions are only 10-5 away from each, but it takes only two dozen iterates before they diverge far away from each other.
Plotting the 1D map's function: To begin to understand what's creating the sensitivity to small variations in initial state (and other map properties), we have to go back to the logistic map's function: xn+1 = f(xn). LogisticFunction.py simply plots that function f(x) and its second iterate f2(x). The identity y = x is also plotted for reference. Recall that wherever the functions cross the identity there are fixed points. How many fixed points do you see for f(x) and for f2(x)? Which are part of a period-2 cycle of f(x)?
How the 1D map creates iterates: LogisticCobweb.py shows a quick way to visualize how iterates are generated by a 1D map—the so-called cobweb diagram. The iterates are “reflected off” of the identity. This is equivalent to taking the next value xn+1 as the new “initial condition” xn. Pay particular attention to how the lines illustrating the reflection are created. Trace through the code so that you understand how this is effected. Change nIterates from very small values to larger values.
1D map bifurcation diagrams LogisticBifn.py: The logistic map's complicated behavior is easier to understand at a larger scale when we plot the attractors as a function of parameter value in a bifurcation diagram. Compared to the previous programs this one, depending on how you set the number of parameter steps and the number of iterations, can take a long time to compute. Start with these set small. Notice that there is an iteration parameter nTransients that sets the number of iterations from the initial condition that are ignored and not plotted.
It turns out that the graphical window for the displayed graph has a pan and zoom control. This means you can make a very detailed bifurcation diagram once (be patient!) and the scroll around and zoom in. Again, depending on how many 1000s of points are in the diagram this might be very slow! Done with the right amount of data, though, it's nice to be able to zoom in on various pieces of the (parameter,attractor)-space diagram.
The veils in bifurcation diagrams LogisticXMax.py: The bifurcation diagram made it clear that there were high density regions, where iterates visited more often, and that these regions—veils— depended smoothly on the control parameter. What are these from? I mentioned in class that these are iterates of the map's maximum, which is at xmax = 1/2. We can check this claim by overlaying iterates of xmax on the bifurcation diagram.
Note that I use a slightly different way to plot the bifurcation diagram than above: storing all of the (r,x) pairs for all iterates. This is more wasteful in terms of storage.
Try the zoom on this graphic, too, to see the relationship between the veils and the iterates of the maximum. The latter do, in fact, varying smoothly as a function of r.
The important observation is that the iterates fn(x = 1/2) are well behaved at low r, but in the chaotic regime their r-dependence becomes quite wild. That is, once the map is nonlinear enough, the map is tall enough that it folds back into itself, and the iterates become complicated. The behavior of the iterates of the maximum is yet another signature of chaos.
The probability distribution of iterates LogisticHistogram.py: We can look at this density variation more closely by estimating the probability of how often iterates fall in some small bin on the interval. We do this by estimating a histogram of the map's iterates, which directly counts the number of visits to a pre-selected set of bins on the interval. The program builds its own histogram, rather than use some package's built-in histogram function. This gives us more control and speed and storage efficiency.
Here's a list of noteworthy parameter values where the logistic map does interesting things. Try a bunch of them. Several are already given at the beginning of the program: fully chaotic single band, where two bands merge into one, a typical chaotic value, and the onset of chaos.
Use the tools above to explore these behaviors.
There are many more phenomena to explore, even in the simple quadratic logistic map. For more plotting options, see the Matplotlib tutorial. The above programs just scratch the surface.