================= Parallel examples ================= In this section we describe two more involved examples of using an IPython cluster to perform a parallel computation. In these examples, we will be using IPython's "pylab" mode, which enables interactive plotting using the Matplotlib package. IPython can be started in this mode by typing:: ipython --pylab at the system command line. 150 million digits of pi ======================== In this example we would like to study the distribution of digits in the number pi (in base 10). While it is not known if pi is a normal number (a number is normal in base 10 if 0-9 occur with equal likelihood) numerical investigations suggest that it is. We will begin with a serial calculation on 10,000 digits of pi and then perform a parallel calculation involving 150 million digits. In both the serial and parallel calculation we will be using functions defined in the :file:`pidigits.py` file, from the IPython examples. These functions provide basic facilities for working with the digits of pi and can be loaded into IPython by locating :file:`pidigits.py`, and doing: .. sourcecode:: ipython In [1]: run pidigits.py Serial calculation ------------------ For the serial calculation, we will use `SymPy `_ to calculate 10,000 digits of pi and then look at the frequencies of the digits 0-9. Out of 10,000 digits, we expect each digit to occur 1,000 times. While SymPy is capable of calculating many more digits of pi, our purpose here is to set the stage for the much larger parallel calculation. In this example, we use two functions from :file:`pidigits.py`: :func:`one_digit_freqs` (which calculates how many times each digit occurs) and :func:`plot_one_digit_freqs` (which uses Matplotlib to plot the result). Here is an interactive IPython session that uses these functions with SymPy: .. sourcecode:: ipython In [7]: import sympy In [8]: pi = sympy.pi.evalf(40) In [9]: pi Out[9]: 3.141592653589793238462643383279502884197 In [10]: pi = sympy.pi.evalf(10000) In [11]: digits = (d for d in str(pi)[2:]) # create a sequence of digits In [12]: run pidigits.py # load one_digit_freqs/plot_one_digit_freqs In [13]: freqs = one_digit_freqs(digits) In [14]: plot_one_digit_freqs(freqs) Out[14]: [] The resulting plot of the single digit counts shows that each digit occurs approximately 1,000 times, but that with only 10,000 digits the statistical fluctuations are still rather large: .. image:: figs/single_digits.* It is clear that to reduce the relative fluctuations in the counts, we need to look at many more digits of pi. That brings us to the parallel calculation. Parallel calculation -------------------- Calculating many digits of pi is a challenging computational problem in itself. Because we want to focus on the distribution of digits in this example, we will use pre-computed digit of pi from the website of Professor Yasumasa Kanada at the University of Tokyo (http://www.super-computing.org). These digits come in a set of text files (ftp://pi.super-computing.org/.2/pi200m/) that each have 10 million digits of pi. For the parallel calculation, we download these files to the compute nodes, and use one file per node. To make things a little more interesting we will calculate the frequencies of all 2 digits sequences (00-99) and then plot the result using a 2D matrix in Matplotlib. The overall idea of the calculation is simple: each IPython engine will compute the two digit counts for the digits in a single file. Then in a final step the counts from each engine will be added up. To perform this calculation, we will need two top-level functions from :file:`pidigits.py`: .. literalinclude:: _static/code/pi/pidigits.py :language: python :lines: 47-62 We will also use the :func:`plot_two_digit_freqs` function to plot the results. The resulting plot generated by Matplotlib is shown below. The colors indicate which two digit sequences are more (red) or less (blue) likely to occur in the first 150 million digits of pi. We clearly see that the sequence "41" is most likely and that "06" and "07" are least likely. Further analysis would show that the relative size of the statistical fluctuations have decreased compared to the 10,000 digit calculation. .. image:: figs/two_digit_counts.* Parallel options pricing ======================== An option is a financial contract that gives the buyer of the contract the right to buy (a "call") or sell (a "put") a secondary asset (a stock for example) at a particular date in the future (the expiration date) for a pre-agreed upon price (the strike price). For this right, the buyer pays the seller a premium (the option price). There are a wide variety of flavors of options (American, European, Asian, etc.) that are useful for different purposes: hedging against risk, speculation, etc. Much of modern finance is driven by the need to price these contracts accurately based on what is known about the properties (such as volatility) of the underlying asset. One method of pricing options is to use a Monte Carlo simulation of the underlying asset price. In this example we use this approach to price both European and Asian (path dependent) options for various strike prices and volatilities. The function :func:`price_options` in :file:`mcpricer.py` implements the basic Monte Carlo pricing algorithm using the NumPy package and is shown here: .. literalinclude:: _static/code/mcpricer/mcpricer.py :language: python Exercise ======== Write a script that will run this code for a variety of parameters, using the LoadBalancedView. :ref:`Proceed ` to see a solution.