diff --git a/files/hpc-intro-code.tar.gz b/files/hpc-intro-code.tar.gz deleted file mode 100644 index 1b2e3d9e..00000000 Binary files a/files/hpc-intro-code.tar.gz and /dev/null differ diff --git a/files/hpc-intro-pi-code.tar.gz b/files/hpc-intro-pi-code.tar.gz new file mode 100644 index 00000000..33b43cb9 Binary files /dev/null and b/files/hpc-intro-pi-code.tar.gz differ diff --git a/files/pi-mpi-minimal.py b/files/pi-mpi-minimal.py deleted file mode 100755 index 407d90f6..00000000 --- a/files/pi-mpi-minimal.py +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env python3 -import numpy as np -import sys -from mpi4py import MPI - -def inside_circle(total_count): - x = np.random.uniform(size=total_count) - y = np.random.uniform(size=total_count) - radii = np.sqrt(x*x + y*y) - count = len(radii[np.where(radii<=1.0)]) - return count - -if __name__ == '__main__': - comm = MPI.COMM_WORLD - cpus = comm.Get_size() - rank = comm.Get_rank() - n_samples = int(sys.argv[1]) - if rank == 0: - partitions = [ int(n_samples / cpus) ] * cpus - counts = [ int(0) ] * cpus - else: - partitions = None - counts = None - partition_item = comm.scatter(partitions, root=0) - count_item = inside_circle(partition_item) - counts = comm.gather(count_item, root=0) - if rank == 0: - my_pi = 4.0 * sum(counts) / sum(partitions) - print(my_pi) diff --git a/files/pi-mpi.py b/files/pi-mpi.py deleted file mode 100755 index e4b2e53f..00000000 --- a/files/pi-mpi.py +++ /dev/null @@ -1,125 +0,0 @@ -#!/usr/bin/env python3 - -"""Parallel example code for estimating the value of π. - -We can estimate the value of π by a stochastic algorithm. Consider a -circle of radius 1, inside a square that bounds it, with vertices at -(1,1), (1,-1), (-1,-1), and (-1,1). The area of the circle is just π, -whereas the area of the square is 4. So, the fraction of the area of the -square which is covered by the circle is π/4. - -A point selected at random uniformly from the square thus has a -probability π/4 of being within the circle. - -We can estimate π by examining a large number of randomly-selected -points from the square, and seeing what fraction of them lie within the -circle. If this fraction is f, then our estimate for π is π ≈ 4f. - -Thanks to symmetry, we can compute points in one quadrant, rather -than within the entire unit square, and arrive at identical results. - -This task lends itself naturally to parallelization -- the task of -selecting a sample point and deciding whether or not it's inside the -circle is independent of all the other samples, so they can be done -simultaneously. We only need to aggregate the data at the end to compute -our fraction f and our estimate for π. -""" - -import numpy as np -import sys -import datetime -from mpi4py import MPI - - -def inside_circle(total_count): - """Single-processor task for a group of samples. - - Generates uniform random x and y arrays of size total_count, on the - interval [0,1), and returns the number of the resulting (x,y) pairs - which lie inside the unit circle. - """ - - host_name = MPI.Get_processor_name() - print("Rank {} generating {:n} samples on host {}.".format( - rank, total_count, host_name)) - x = np.float64(np.random.uniform(size=total_count)) - y = np.float64(np.random.uniform(size=total_count)) - - radii = np.sqrt(x*x + y*y) - - count = len(radii[np.where(radii<=1.0)]) - - return count - - -if __name__ == '__main__': - """Main executable. - - This function runs the 'inside_circle' function with a defined number - of samples. The results are then used to estimate π. - - An estimate of the required memory, elapsed calculation time, and - accuracy of calculating π are also computed. - """ - - # Declare an MPI Communicator for the parallel processes to talk through - comm = MPI.COMM_WORLD - - # Read the number of parallel processes tied into the comm channel - cpus = comm.Get_size() - - # Find out the index ("rank") of *this* process - rank = comm.Get_rank() - - if len(sys.argv) > 1: - n_samples = int(sys.argv[1]) - else: - n_samples = 8738128 # trust me, this number is not random :-) - - if rank == 0: - # Time how long it takes to estimate π. - start_time = datetime.datetime.now() - print("Generating {:n} samples.".format(n_samples)) - # Rank zero builds two arrays with one entry for each rank: - # one for the number of samples they should run, and - # one to store the count info each rank returns. - partitions = [ int(n_samples / cpus) ] * cpus - counts = [ int(0) ] * cpus - else: - partitions = None - counts = None - - # All ranks participate in the "scatter" operation, which assigns - # the local scalar values to their appropriate array components. - # partition_item is the number of samples this rank should generate, - # and count_item is the place to put the number of counts we see. - partition_item = comm.scatter(partitions, root=0) - count_item = comm.scatter(counts, root=0) - - # Each rank locally populates its count_item variable. - count_item = inside_circle(partition_item) - - # All ranks participate in the "gather" operation, which sums the - # rank's count_items into the total "counts". - counts = comm.gather(count_item, root=0) - - if rank == 0: - # Only rank zero writes the result, although it's known to all. - my_pi = 4.0 * sum(counts) / n_samples - elapsed_time = (datetime.datetime.now() - start_time).total_seconds() - - # Memory required is dominated by the size of x, y, and radii from - # inside_circle(), calculated in MiB - size_of_float = np.dtype(np.float64).itemsize - memory_required = 3 * n_samples * size_of_float / (1024**2) - - # accuracy is calculated as a percent difference from a known estimate - # of π. - pi_specific = np.pi - accuracy = 100*(1-my_pi/pi_specific) - - # Uncomment either summary format for verbose or terse output - # summary = "{:d} core(s), {:d} samples, {:f} MiB memory, {:f} seconds, {:f}% error" - summary = "{:d},{:d},{:f},{:f},{:f}" - print(summary.format(cpus, n_samples, memory_required, elapsed_time, - accuracy)) diff --git a/files/pi-serial-minimized.py b/files/pi-serial-minimized.py deleted file mode 100644 index acc99d31..00000000 --- a/files/pi-serial-minimized.py +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env python3 -import numpy as np -import sys - -def inside_circle(total_count): - x = np.random.uniform(size=total_count) - y = np.random.uniform(size=total_count) - radii = np.sqrt(x*x + y*y) - count = len(radii[np.where(radii<=1.0)]) - return count - -if __name__ == '__main__': - n_samples = int(sys.argv[1]) - counts = inside_circle(n_samples) - my_pi = 4.0 * counts / n_samples - print(my_pi) diff --git a/files/pi-serial.py b/files/pi-serial.py deleted file mode 100755 index c5289c84..00000000 --- a/files/pi-serial.py +++ /dev/null @@ -1,80 +0,0 @@ -#!/usr/bin/env python3 - -"""Serial example code for estimating the value of π. - -We can estimate the value of π by a stochastic algorithm. Consider a -circle of radius 1, inside a square that bounds it, with vertices at -(1,1), (1,-1), (-1,-1), and (-1,1). The area of the circle is just π, -whereas the area of the square is 4. So, the fraction of the area of the -square which is covered by the circle is π/4. - -A point selected at random uniformly from the square thus has a -probability π/4 of being within the circle. - -We can estimate π by examining a large number of randomly-selected -points from the square, and seeing what fraction of them lie within the -circle. If this fraction is f, then our estimate for π is π ≈ 4f. - -Thanks to symmetry, we can compute points in one quadrant, rather -than within the entire unit square, and arrive at identical results. -""" - -import numpy as np -import sys -import datetime - - -def inside_circle(total_count): - """Single-processor task for a group of samples. - - Generates uniform random x and y arrays of size total_count, on the - interval [0,1), and returns the number of the resulting (x,y) pairs - which lie inside the unit circle. - """ - - x = np.float64(np.random.uniform(size=total_count)) - y = np.float64(np.random.uniform(size=total_count)) - - radii = np.sqrt(x*x + y*y) - - count = len(radii[np.where(radii<=1.0)]) - - return count - - -if __name__ == '__main__': - """Main executable. - - This function runs the 'inside_circle' function with a defined number - of samples. The results are then used to estimate π. - - An estimate of the required memory, elapsed calculation time, and - accuracy of calculating π are also computed. - """ - - if len(sys.argv) > 1: - n_samples = int(sys.argv[1]) - else: - n_samples = 8738128 # trust me, this number is not random :-) - - # Time how long it takes to estimate π. - start_time = datetime.datetime.now() - counts = inside_circle(n_samples) - my_pi = 4.0 * counts / n_samples - elapsed_time = (datetime.datetime.now() - start_time).total_seconds() - - # Memory required is dominated by the size of x, y, and radii from - # inside_circle(), calculated in MiB - size_of_float = np.dtype(np.float64).itemsize - memory_required = 3 * n_samples * size_of_float / (1024**2) - - # accuracy is calculated as a percent difference from a known estimate - # of π. - pi_specific = np.pi - accuracy = 100*(1-my_pi/pi_specific) - - # Uncomment either summary format for verbose or terse output - # summary = "{:d} core(s), {:d} samples, {:f} MiB memory, {:f} seconds, {:f}% error" - summary = "{:d},{:d},{:f},{:f},{:f}" - print(summary.format(1, n_samples, memory_required, elapsed_time, - accuracy)) diff --git a/files/pi.py b/files/pi.py deleted file mode 100755 index bac13585..00000000 --- a/files/pi.py +++ /dev/null @@ -1,127 +0,0 @@ -#!/usr/bin/env python3 - -"""Parallel example code for estimating the value of π. - -We can estimate the value of π by a stochastic algorithm. Consider a -circle of radius 1, inside a square that bounds it, with vertices at -(1,1), (1,-1), (-1,-1), and (-1,1). The area of the circle is just π, -whereas the area of the square is 4. So, the fraction of the area of the -square which is covered by the circle is π/4. - -A point selected at random uniformly from the square thus has a -probability π/4 of being within the circle. - -We can estimate π by examining a large number of randomly-selected -points from the square, and seeing what fraction of them lie within the -circle. If this fraction is f, then our estimate for π is π ≈ 4f. - -This task lends itself naturally to parallelization -- the task of -selecting a sample point and deciding whether or not it's inside the -circle is independent of all the other samples, so they can be done -simultaneously. We only need to aggregate the data at the end to compute -our fraction f and our estimate for π. - -Thanks to symmetry, we can compute points in one quadrant, rather -than within the entire unit square, and arrive at identical results. -""" - -import locale as l10n -from mpi4py import MPI -import numpy as np -import sys - -l10n.setlocale(l10n.LC_ALL, "") - -# Declare an MPI Communicator for the parallel processes to talk through -comm = MPI.COMM_WORLD - -# Read the number of parallel processes tied into the comm channel -cpus = comm.Get_size() - - -# Find out the index ("rank") of *this* process -rank = comm.Get_rank() - -np.random.seed(14159265 + rank) - -def inside_circle(total_count): - """Single-processor task for a group of samples. - - Generates uniform random x and y arrays of size total_count, on the - interval [0,1), and returns the number of the resulting (x,y) pairs - which lie inside the unit circle. - """ - host_name = MPI.Get_processor_name() - print("Rank {} generating {:n} samples on host {}.".format( - rank, total_count, host_name)) - - x = np.float64(np.random.uniform(size=total_count)) - y = np.float64(np.random.uniform(size=total_count)) - - radii = np.sqrt(x*x + y*y) - - count = len(radii[np.where(radii<=1.0)]) - - return count - - -if __name__ == '__main__': - """Main MPI executable. - - This conditional is entered as many times as there are MPI processes. - - Each process knows its index, called 'rank', and the number of - ranks, called 'cpus', from the MPI calls at the top of the module. - - Rank 0 divides the data arrays among the ranks (including itself), - then each rank independently runs the 'inside_circle' function with - its share of the samples. The disparate results are then aggregated - via the 'gather' operation, and then the estimate for π is - computed. - - An estimate of the required memory is also computed. - """ - - n_samples = 8738128 # trust me, this number is not random :-) - - if len(sys.argv) > 1: - n_samples = int(sys.argv[1]) - - if rank == 0: - print("Generating {:n} samples.".format(n_samples)) - # Rank zero builds two arrays with one entry for each rank: - # one for the number of samples they should run, and - # one to store the count info each rank returns. - partitions = [ int(n_samples / cpus) for item in range(cpus)] - counts = [ int(0) ] * cpus - else: - partitions = None - counts = None - - # All ranks participate in the "scatter" operation, which assigns - # the local scalar values to their appropriate array components. - # partition_item is the number of samples this rank should generate, - # and count_item is the place to put the number of counts we see. - - partition_item = comm.scatter(partitions, root=0) - count_item = comm.scatter(counts, root=0) - - # Each rank locally populates its count_item variable. - - count_item = inside_circle(partition_item) - - # All ranks participate in the "gather" operation, which creates an array - # of all the rank's count_items on rank zero. - - counts = comm.gather(count_item, root=0) - - if rank == 0: - # Only rank zero has the entire array of results, so only it can - # compute and print the final answer. - my_pi = 4.0 * sum(counts) / n_samples - size_of_float = np.dtype(np.float64).itemsize - run_type = "serial" if cpus == 1 else "mpi" - print("[{:>8} version] required memory {:.1f} MB".format( - run_type, 3 * n_samples * size_of_float / (1024**2))) - print("[using {:>3} cores ] π is {:n} from {:n} samples".format( - cpus, my_pi, n_samples)) diff --git a/files/simple-pi-illustration.py b/files/simple-pi-illustration.py deleted file mode 100644 index e8ba7548..00000000 --- a/files/simple-pi-illustration.py +++ /dev/null @@ -1,39 +0,0 @@ -# -*- coding: utf-8 -*- - -# This program generates a picture of the algorithm used to estimate the value -# of π by random sampling. - -import numpy as np -import matplotlib.pyplot as plt -import matplotlib.patches as pltpatches - -np.random.seed(14159625) - -n = 128 -x = np.random.uniform(size=n) -y = np.random.uniform(size=n) - -with plt.xkcd(): - - plt.figure(figsize=(5,5)) - plt.axis("equal") - plt.xlim([-0.0125, 1.0125]) - plt.ylim([-0.0125, 1.0125]) - - for d in ["left", "top", "bottom", "right"]: - plt.gca().spines[d].set_visible(False) - - plt.xlabel("x", position=(0.8, 0)) - plt.ylabel("y", rotation=0, position=(0, 0.8)) - - plt.xticks([0, 0.5, 1], ["0", "1/2", "1"]) - plt.yticks([0, 0.5, 1], ["0", "1/2", "1"]) - - plt.scatter(x, y, s=8, c=np.random.uniform(size=(n,3))) - - circ = pltpatches.Arc((0, 0), width=2, height=2, angle=0, theta1=0, theta2=90, color="black", linewidth=3) - plt.gca().add_artist(circ) - squa = plt.Rectangle((0, 0), width=1, height=1, fill=None, linewidth=3) - plt.gca().add_artist(squa) - - plt.savefig("pi.png", bbox_inches="tight", dpi=400)