Skip to content

Commit 26b5b8c

Browse files
committed
Merge branch 'week10' of https://github.com/nuttamas/rse-classwork-2020 into week10
2 parents 26489ae + 7fcf3b8 commit 26b5b8c

File tree

6 files changed

+427
-0
lines changed

6 files changed

+427
-0
lines changed

week10/calc_pi.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import argparse
2+
import math
3+
import random
4+
import timeit
5+
6+
from utils import format_time
7+
8+
def point_in_circle(x, y, radius=1):
9+
"""
10+
Checks whether a point (x, y) is part of a circle with a set radius.
11+
12+
example
13+
-------
14+
>>> point_in_circle(0, 0)
15+
True
16+
17+
"""
18+
r = math.sqrt(x ** 2 + y ** 2)
19+
return r <= radius
20+
21+
def calculate_pi_timeit(points):
22+
"""
23+
Wrapper function to build calculate_pi with a particular number of points
24+
and returns the function to be timed.
25+
"""
26+
def calculate_pi():
27+
"""
28+
Calculates an approximated value of pi by the Monte Carlo method.
29+
"""
30+
within_circle = [point_in_circle(random.random(), random.random())
31+
for _ in range(points)]
32+
return 4 * sum(within_circle)/points
33+
return calculate_pi
34+
35+
36+
def command():
37+
"""
38+
entry point of the script to accept arguments
39+
"""
40+
41+
parser = argparse.ArgumentParser(description="Calculates an approximate value of PI and how long it takes",
42+
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
43+
parser.add_argument('--npoints', '-np', default=10_000, type=int, help="Number of random points to use")
44+
parser.add_argument('--number', '-n', default=100, type=int, help="Number of times to execute the calculations")
45+
parser.add_argument('--repeat', '-r', default=5, type=int, help="How many times to repeat the timer")
46+
47+
arguments = parser.parse_args()
48+
49+
calc_pi = calculate_pi_timeit(arguments.npoints)
50+
print(f"pi = {calc_pi()} (with {arguments.npoints})")
51+
result = timeit.repeat(calc_pi, number=arguments.number, repeat=arguments.repeat)
52+
best = min(result) / arguments.number
53+
print(f"{arguments.number} loops, best of {arguments.repeat}: {format_time(best)} per loop")
54+
55+
56+
if __name__ == '__main__':
57+
command()

week10/calc_pi_cython.ipynb

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "markdown",
5+
"metadata": {},
6+
"source": [
7+
"# Using Cython in a Jupyter notebook\n",
8+
"\n",
9+
"To use Cython, we must first load an extension."
10+
]
11+
},
12+
{
13+
"cell_type": "code",
14+
"execution_count": null,
15+
"metadata": {},
16+
"outputs": [],
17+
"source": [
18+
"%load_ext cython"
19+
]
20+
},
21+
{
22+
"cell_type": "markdown",
23+
"metadata": {},
24+
"source": [
25+
"We can define our functions with pure Python code, and Cython will try to optimise them."
26+
]
27+
},
28+
{
29+
"cell_type": "code",
30+
"execution_count": null,
31+
"metadata": {},
32+
"outputs": [],
33+
"source": [
34+
"%%cython --annotate\n",
35+
"import argparse\n",
36+
"import math\n",
37+
"import random\n",
38+
"import time\n",
39+
"\n",
40+
"from utils import format_time\n",
41+
"\n",
42+
"def point_in_circle(x, y, radius=1):\n",
43+
" \"\"\"\n",
44+
" Checks whether a point (x, y) is part of a circle with a set radius.\n",
45+
" example\n",
46+
" -------\n",
47+
" >>> point_in_circle(0, 0)\n",
48+
" True\n",
49+
" \"\"\"\n",
50+
" r = math.sqrt(x ** 2 + y ** 2)\n",
51+
" return r <= radius\n",
52+
"\n",
53+
"def calculate_pi(points):\n",
54+
" \"\"\"\n",
55+
" Calculates an approximated value of pi by the Monte Carlo method.\n",
56+
" \"\"\"\n",
57+
" within_circle = 0\n",
58+
" for _ in range(points):\n",
59+
" within_circle += int(point_in_circle(random.random(), random.random()))\n",
60+
" return 4 * within_circle/points"
61+
]
62+
},
63+
{
64+
"cell_type": "code",
65+
"execution_count": null,
66+
"metadata": {},
67+
"outputs": [],
68+
"source": [
69+
"%%timeit\n",
70+
"number_points = 10000\n",
71+
"calculate_pi(number_points)"
72+
]
73+
}
74+
],
75+
"metadata": {
76+
"kernelspec": {
77+
"display_name": "Python 3",
78+
"language": "python",
79+
"name": "python3"
80+
},
81+
"language_info": {
82+
"codemirror_mode": {
83+
"name": "ipython",
84+
"version": 3
85+
},
86+
"file_extension": ".py",
87+
"mimetype": "text/x-python",
88+
"name": "python",
89+
"nbconvert_exporter": "python",
90+
"pygments_lexer": "ipython3",
91+
"version": "3.8.3"
92+
}
93+
},
94+
"nbformat": 4,
95+
"nbformat_minor": 4
96+
}

week10/calc_pi_mpi.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import argparse
2+
import random
3+
4+
from mpi4py import MPI
5+
6+
from calc_pi import point_in_circle, calculate_pi_timeit
7+
from utils import format_time
8+
9+
10+
COMM = MPI.COMM_WORLD
11+
SIZE = COMM.Get_size()
12+
RANK = COMM.Get_rank()
13+
14+
if RANK == 0:
15+
parser = argparse.ArgumentParser(description="PI value approximated using monte-carlo and MPI")
16+
parser.add_argument('--npoints', '-np', default=10_000, type=int, help="Number of random points to use")
17+
arguments = parser.parse_args()
18+
print(arguments.npoints)
19+
points = arguments.npoints // SIZE
20+
extra = arguments.npoints % SIZE
21+
else:
22+
points = None
23+
24+
points = COMM.bcast(points, root=0)
25+
26+
if RANK == 0:
27+
points += extra
28+
29+
WT = MPI.Wtime()
30+
31+
within_circle = [point_in_circle(random.random(), random.random())
32+
for _ in range(points)]
33+
34+
all_pi = COMM.gather(within_circle, root=0)
35+
if RANK == 0:
36+
pi = 4 * sum(map(sum, all_pi)) / arguments.npoints
37+
print(f"pi = {pi} (with {sum(map(len, all_pi))} points)")
38+
WT = MPI.Wtime() - WT
39+
print(f"It took: {format_time(WT)}")

week10/calc_pi_numba.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import argparse
2+
import math
3+
import random
4+
import time
5+
6+
from numba import jit
7+
8+
from utils import format_time
9+
10+
@jit(nopython=True)
11+
def point_in_circle(x, y, radius=1):
12+
"""
13+
Checks whether a point (x, y) is part of a circle with a set radius.
14+
15+
example
16+
-------
17+
>>> point_in_circle(0, 0)
18+
True
19+
20+
"""
21+
r = math.sqrt(x ** 2 + y ** 2)
22+
return r <= radius
23+
24+
@jit(nopython=True)
25+
def calculate_pi(points):
26+
"""
27+
Calculates an approximated value of pi by the Monte Carlo method.
28+
"""
29+
within_circle = 0
30+
for _ in range(points):
31+
within_circle += int(point_in_circle(random.random(), random.random()))
32+
return 4 * within_circle/points
33+
34+
35+
def command():
36+
"""
37+
entry point of the script to accept arguments
38+
"""
39+
40+
parser = argparse.ArgumentParser(description="Calculates an approximate value of PI and how long it takes",
41+
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
42+
parser.add_argument('--npoints', '-np', default=10_000, type=int, help="Number of random points to use")
43+
44+
arguments = parser.parse_args()
45+
46+
start = time.time()
47+
pi = calculate_pi(arguments.npoints)
48+
end = time.time()
49+
print(f"Elapsed (with compilation) = {format_time(end-start)}")
50+
print(f"pi = {pi} (with {arguments.npoints})")
51+
52+
start = time.time()
53+
pi = calculate_pi(arguments.npoints)
54+
end = time.time()
55+
print(f"Elapsed (after compilation) = {format_time(end-start)}")
56+
print(f"pi = {pi} (with {arguments.npoints})")
57+
58+
59+
if __name__ == '__main__':
60+
command()

0 commit comments

Comments
 (0)