Calculating π using parallel Monte Carlo
The code below contains a program that computes the value of constant π = 3.1415… to single precision using the standard Monte Carlo technique (see e.g. Press et al. 1992, Numerical Recipes in C, section 7.6).
The parent process starts a user defined number of managed Dragon processes as
workers executing the function pi_helper
to parallelize the radius
computation. The parent process sends two random numbers to the workers using an
outgoing Dragon queue q_send
and collects the radii from an incoming queue
q_recv
to compute the result. It also computes a residual delta
with respect
to the previous iteration. This is repeated
until convergence is reached.
This example demonstrates two important paradigms of parallel programming with Dragon: How to execute Python processes using Dragon native and how to use Dragon native Queues to communicate objects between processes.
1""" Calculate pi in parallel using the standard Monte Carlo technique
2 with the Dragon native interface.
3
4 NOTE: due to the missing Process object in Dragon native for version v0.3
5 we are using the Multiprocessing process API here. This will change with
6 v1.0
7"""
8
9import sys
10import os
11import random
12import pickle
13import math
14
15import dragon
16from dragon.native.queue import Queue
17
18from multiprocessing import Process as Process # only for Dragon v0.3
19
20
21def pi_helper(q_in: Queue, q_out: Queue) -> None:
22
23 while True: # keep working
24
25 x, y = q_in.get(timeout=None)
26
27 if x == -1 and y == -1: # we're done here
28 break
29
30 r = x * x + y * y
31
32 if r > 1.0:
33 q_out.put(False)
34 else:
35 q_out.put(True)
36
37
38def main(num_workers):
39
40 # create Dragon queues
41 q_send = Queue()
42 q_recv = Queue()
43
44 # create & start processes
45 processes = []
46 for _ in range(num_workers):
47 p = Process(target=pi_helper, args=(q_send, q_recv))
48 p.start()
49 processes.append(p)
50
51 # start calculation
52 random.seed(a=42)
53
54 num_true = 0
55 num_send = 0
56 delta = 1
57 count = 0
58
59 while abs(delta) > 1e-6:
60
61 # send new values to everyone
62 for _ in range(num_workers):
63 x = random.uniform(0.0, 1.0)
64 y = random.uniform(0.0, 1.0)
65
66 q_send.put((x, y), timeout=0)
67 num_send += 1
68
69 # receive results from everyone
70 for _ in range(num_workers):
71
72 is_inside = q_recv.get(timeout=None)
73
74 if is_inside:
75 num_true += 1
76
77 # calculate result of this iteration
78 value = 4.0 * float(num_true) / float(num_send)
79 delta = (value - math.pi) / math.pi
80
81 if count % 512 == 0:
82 print(f"{count:04}: pi={value:10.7f}, error={delta:8.1e}", flush=True)
83
84 count += 1
85
86 print(f"Final value after {count} iterations: pi={value}, error={delta}")
87
88 # shut down all managed processes
89 for _ in range(num_workers):
90 q_send.put((-1.0, -1.0), timeout=0) # termination message
91
92 # wait for all processes to be finished
93 for p in processes:
94 p.join(timeout=None)
95
96
97if __name__ == "__main__":
98
99 print(f"\npi-demo: Calculate π = 3.1415 ... in parallel using the Dragon native API.\n")
100
101 try:
102 num_workers = int(sys.argv[1])
103 except:
104 print(f"USAGE: dragon pi_demo.py $N")
105 print(f" N : number of worker puids to start")
106 sys.exit(f"Wrong argument '{sys.argv[1]}'")
107
108 print(f"Got num_workers = {num_workers}", flush=True)
109 main(num_workers)
The program can be run using 2 workers with dragon pi-demo.py 2
and results in
the following output:
1>$dragon pi_demo.py 2
2
3pi-demo: Calculate π = 3.1415 ... in parallel using the Dragon native API.
4
5Got num_workers = 2
60000: pi= 4.0000000, error= 2.7e-01
70512: pi= 3.1189084, error=-7.2e-03
81024: pi= 3.1531707, error= 3.7e-03
91536: pi= 3.1659076, error= 7.7e-03
102048: pi= 3.1449488, error= 1.1e-03
112560: pi= 3.1409606, error=-2.0e-04
123072: pi= 3.1389522, error=-8.4e-04
133584: pi= 3.1391911, error=-7.6e-04
144096: pi= 3.1354650, error=-2.0e-03
154608: pi= 3.1277934, error=-4.4e-03
165120: pi= 3.1267331, error=-4.7e-03
175632: pi= 3.1319013, error=-3.1e-03
186144: pi= 3.1446705, error= 9.8e-04
19Final value after 6342 iterations: pi=3.141595711132135, error=9.732459548770225e-07
20+++ head proc exited, code 0