Skip to content

Commit e54f7eb

Browse files
author
Release Manager
committed
gh-37313: Fix package install instructions in "Numerical Sage" tutorial <!-- ^^^^^ Please provide a concise, informative and self-explanatory title. Don't put issue numbers in there, do this in the PR body below. For example, instead of "Fixes #1234" use "Introduce new method to calculate 1+1" --> <!-- Describe your changes here in detail --> <!-- Why is this change required? What problem does it solve? --> Fixes #19198 <!-- If this PR resolves an open issue, please link to it here. For example "Fixes #12345". --> <!-- If your change requires a documentation PR, please link it appropriately. --> ### 📝 Checklist <!-- Put an `x` in all the boxes that apply. --> <!-- If your change requires a documentation PR, please link it appropriately --> <!-- If you're unsure about any of these, don't hesitate to ask. We're here to help! --> <!-- Feel free to remove irrelevant items. --> - [x] The title is concise, informative, and self-explanatory. - [ ] The description explains in detail what this PR is about. - [x] I have linked a relevant issue or discussion. - [ ] I have created tests covering the changes. - [ ] I have updated the documentation accordingly. ### ⌛ Dependencies <!-- List all open PRs that this PR logically depends on - #12345: short description why this is a dependency - #34567: ... --> <!-- If you're unsure about any of these, don't hesitate to ask. We're here to help! --> URL: #37313 Reported by: Matthias Köppe Reviewer(s): grhkm21
2 parents 0e819af + 5e6af18 commit e54f7eb

File tree

1 file changed

+88
-109
lines changed
  • src/doc/en/thematic_tutorials/numerical_sage

1 file changed

+88
-109
lines changed
Lines changed: 88 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,20 @@
11
mpi4py
22
======
33

4-
MPI which stands for message passing interface is a common library
5-
for parallel programming. There is a package mpi4py that builds on
6-
the top of mpi, and lets arbitrary python objects be passed between
7-
different processes. These packages are not part of the default
8-
sage install. To install them do
4+
MPI, which stands for Message Passing Interface, is a common library
5+
for parallel programming. There is a package ``mpi4py`` that builds on
6+
the top of MPI, and lets arbitrary python objects be passed between
7+
different processes. These packages are not available from the
8+
Sage distribution. Install ``openmpi`` using your distribution's
9+
package manager. Then install ``mpi4py`` using
910

1011
.. skip
1112
1213
::
1314

14-
sage: optional_packages()
15+
sage: !pip install mpi4py
1516

16-
Find the package name openmpi-\* and mpi4py-\*and do
17-
18-
.. skip
19-
20-
::
21-
22-
sage: install_package('openmpi-*')
23-
sage: install_package('mpi4py-*')
24-
25-
Note that openmpi takes a while to compile (15-20 minutes or so).
26-
Openmpi can be run on a cluster, however this requires some set up
27-
so that processes on different machines can communicate (though if
28-
you are on a cluster this is probably already set up). The simplest
29-
case is if you are on a shared memory or multicore system where
30-
openmpi will just work with no configuration from you. To be
31-
honest, I have never tried to run mpi4py on a cluster, though there
32-
is much information about these topics online.
33-
34-
Now, the way that mpi works is you start a group of mpi processes,
17+
Now, the way that MPI works is you start a group of MPI processes,
3518
all of the processes run the same code. Each process has a rank,
3619
that is a number that identifies it. The following pseudocode
3720
indicates the general format of MPI programs.
@@ -48,163 +31,159 @@ indicates the general format of MPI programs.
4831
else if my rank is n+1:
4932
....
5033
51-
Each processes looks for what it's supposed to do (specified by its
52-
rank) and processes can send data and receive data. Lets give an
53-
example. Create a script with the following code in a file mpi_1.py
34+
Each process looks for what it's supposed to do (specified by its
35+
rank), and processes can send data and receive data. Let's give an
36+
example. Create a script with the following code in a file ``mpi_1.py``
5437

5538
.. CODE-BLOCK:: python
5639
5740
from mpi4py import MPI
5841
comm = MPI.COMM_WORLD
5942
print("hello world")
60-
print("my rank is: %d"%comm.rank)
43+
print(f"my rank is: {comm.rank}")
6144
62-
To run it you can do (from the command line in your sage
45+
To run it you can do (from the command line in your Sage
6346
directory)
6447

6548
.. CODE-BLOCK:: shell-session
6649
67-
./local/bin/mpirun -np 5 ./sage -python mpi_1.py
50+
mpirun -np 5 ./sage -python mpi_1.py
51+
52+
The command ``mpirun -np 5`` starts 5 copies of a program under MPI. In
53+
this case we have 5 copies of Sage in pure Python mode running the
54+
script ``mpi_1.py``. The result should be 5 "hello worlds" plus 5 distinct ranks.
6855

69-
The command mpirun -np 5 starts 5 copies of a program under mpi. In
70-
this case we have 5 copies of sage in pure python mode run the
71-
script mpi_1.py. The result should be 5 "hello worlds" plus 5 distinct ranks.
72-
The two most important mpi operations are sending and receiving.
73-
Consider the following example which you should put in a script mpi_2.py
56+
The two most important MPI operations are sending and receiving.
57+
Consider the following example which you should put in a script ``mpi_2.py``
7458

7559
.. CODE-BLOCK:: python
7660
7761
from mpi4py import MPI
7862
import numpy
7963
comm = MPI.COMM_WORLD
80-
rank=comm.rank
81-
size=comm.size
82-
v=numpy.array([rank]*5,dtype=float)
83-
comm.send(v,dest=(rank+1)%size)
84-
data=comm.recv(source=(rank-1)%size)
85-
print("my rank is %d"%rank)
64+
rank = comm.rank
65+
size = comm.size
66+
v = numpy.array([rank] * 5, dtype=float)
67+
comm.send(v, dest=(rank+1) % size)
68+
data = comm.recv(source=(rank-1) % size)
69+
print(f"my rank is: {rank}")
8670
print("I received this:")
8771
print(data)
8872
89-
The same command as above with mpi_1.py replaced by mpi_2.py will
90-
produce 5 outputs and you will see each process creates an array and
91-
then passes it to the next guy (where the last guy passes to the
92-
first.) Note that MPI.size is the total number of mpi
93-
processes. MPI.COMM WORLD is the communication world.
73+
The same command as above with ``mpi_1.py`` replaced by ``mpi_2.py`` will
74+
produce 5 outputs. Each process will create an array and pass
75+
it to the next process, where the last process passes to the
76+
first. Note that ``MPI.size`` is the total number of MPI
77+
processes. ``MPI.COMM_WORLD`` is the communication world.
9478

9579
There are some subtleties regarding MPI to be aware of. Small sends
9680
are buffered. This means if a process sends a small object it will
9781
be stored by openmpi and that process will continue its execution
9882
and the object it sent will be received whenever the destination
99-
executes a receive. However, if an object is large a process will
83+
executes a receive. However, if an object is large, a process will
10084
hang until its destination executes a corresponding receive. In
101-
fact the above code will hang if [rank]\*5 is replaced by
102-
[rank]\*500. It would be better to do
85+
fact, the above code will hang if ``[rank]*5`` is replaced by
86+
``[rank]*500``. It would be better to do
10387

10488
.. CODE-BLOCK:: python
10589
10690
from mpi4py import MPI
10791
import numpy
10892
comm = MPI.COMM_WORLD
109-
rank=comm.rank
110-
size=comm.size
111-
v=numpy.array([rank]*500,dtype=float)
112-
if comm.rank==0:
113-
comm.send(v,dest=(rank+1)%size)
93+
rank = comm.rank
94+
size = comm.size
95+
v = numpy.array([rank] * 500, dtype=float)
96+
if comm.rank == 0:
97+
comm.send(v, dest=(rank+1) % size)
11498
if comm.rank > 0:
115-
data=comm.recv(source=(rank-1)%size)
116-
comm.send(v,dest=(rank+1)%size)
117-
if comm.rank==0:
118-
data=comm.recv(source=size-1)
99+
data = comm.recv(source=(rank-1) % size)
100+
comm.send(v, dest=(rank+1) % size)
101+
if comm.rank == 0:
102+
data = comm.recv(source=size - 1)
119103
120-
print("my rank is %d"%rank)
104+
print(f"my rank is: {rank}")
121105
print("I received this:")
122106
print(data)
123107
124-
Now the first process initiates a send, and then process 1 will be
125-
ready to receive and then he will send and process 2 will be
126-
waiting to receive, etc. This will not lock regardless of how large
127-
of an array we pass.
108+
Now, process 0 sends the data to process 1, then waits to receive from
109+
process ``MPI.size - 1``. Simultaneously, process 1 will send the
110+
data to process 2, then receives the data from process 0. This will
111+
not lock even if the array transmitted is huge.
128112

129-
A common idiom is to have one process, usually the one with rank 0
130-
act as a leader. That processes sends data out to the other
131-
processes and processes the results and decides how further
113+
A common idiom is to have one process, usually the one with rank 0,
114+
act as a leader. That process sends data out to the other
115+
processes, compute on the results, and decides how much further
132116
computation should proceed. Consider the following code
133117

134118
.. CODE-BLOCK:: python
135119
136120
from mpi4py import MPI
137121
import numpy
138-
sendbuf=[]
139-
root=0
122+
sendbuf = []
123+
root = 0
140124
comm = MPI.COMM_WORLD
141-
if comm.rank==0:
142-
m=numpy.random.randn(comm.size,comm.size)
125+
if comm.rank == 0:
126+
m = numpy.random.randn(comm.size, comm.size)
143127
print(m)
144128
sendbuf=m
145129
146-
v=comm.scatter(sendbuf,root)
130+
v = comm.scatter(sendbuf, root)
147131
148132
print("I got this array:")
149133
print(v)
150134
151-
The scatter command takes a list and evenly divides it amongst all
135+
The ``scatter`` command takes a list and evenly divides it amongst all
152136
the processes. Here the root process creates a matrix (which is
153-
viewed as a list of rows) and then scatters it to everybody (roots
154-
sendbuf is divided equally amongst the processes). Each process
155-
prints the row it got. Note that the scatter command is executed by
156-
everyone, but when root executes it, it acts as a send and a
157-
receive (root gets one row from itself), while for everyone else it
158-
is just a receive.
159-
160-
There is a complementary gather command that collects results from
161-
all the processes into a list. The next example uses scatter and
162-
gather together. Now the root process scatters the rows of a
163-
matrix, each process then squares the elements of the row it gets.
164-
Then the rows are all gathered up again by the root process who
165-
collects them into a new matrix.
137+
viewed as a list of rows) and then scatters it to everybody (root's
138+
``sendbuf`` is divided equally amongst the processes). Each process
139+
prints the row it got. Note that the ``scatter`` command is executed by
140+
everyone, but when root executes it, it acts as a ``send`` and a
141+
``receive`` (root gets one row from itself), while for everyone else it
142+
is just a ``receive``.
143+
144+
There is a complementary ``gather`` command that collects results from
145+
all the processes into a list. The next example uses ``scatter`` and
146+
``gather`` together. Now the root process scatters the rows of a
147+
matrix. Each process squares the elements of the row it receives.
148+
The root process then gathers the rows into a new matrix.
166149

167150
.. CODE-BLOCK:: python
168151
169152
from mpi4py import MPI
170153
import numpy
171154
comm = MPI.COMM_WORLD
172-
sendbuf=[]
173-
root=0
174-
if comm.rank==0:
175-
m=numpy.array(range(comm.size*comm.size),dtype=float)
176-
m.shape=(comm.size,comm.size)
155+
sendbuf = []
156+
root = 0
157+
if comm.rank == 0:
158+
m = numpy.array(range(comm.size * comm.size), dtype=float)
159+
m.shape = (comm.size, comm.size)
177160
print(m)
178-
sendbuf=m
161+
sendbuf = m
179162
180-
v=comm.scatter(sendbuf,root)
163+
v = comm.scatter(sendbuf, root)
181164
print("I got this array:")
182165
print(v)
183-
v=v*v
184-
recvbuf=comm.gather(v,root)
185-
if comm.rank==0:
166+
v = v*v
167+
recvbuf = comm.gather(v, root)
168+
if comm.rank == 0:
186169
print(numpy.array(recvbuf))
187170
188-
There is also a broadcast command that sends a single object to
171+
There is also a ``broadcast`` command that sends a single object to
189172
every process. Consider the following small extension. This is the
190-
same as before, but now at the end the root process sends everyone
173+
same as before, but now at the end, the root process sends everyone
191174
the string "done", which is printed out.
192175

193176
.. CODE-BLOCK:: python
194177
195-
v=MPI.COMM_WORLD.scatter(sendbuf,root)
178+
v = MPI.COMM_WORLD.scatter(sendbuf, root)
196179
print("I got this array:")
197180
print(v)
198-
v=v*v
199-
recvbuf=MPI.COMM_WORLD.gather(v,root)
200-
if MPI.COMM_WORLD.rank==0:
181+
v = v*v
182+
recvbuf = MPI.COMM_WORLD.gather(v, root)
183+
if MPI.COMM_WORLD.rank == 0:
201184
print(numpy.array(recvbuf))
202185
203-
if MPI.COMM_WORLD.rank==0:
204-
sendbuf="done"
205-
recvbuf=MPI.COMM_WORLD.bcast(sendbuf,root)
186+
if MPI.COMM_WORLD.rank == 0:
187+
sendbuf = "done"
188+
recvbuf = MPI.COMM_WORLD.bcast(sendbuf,root)
206189
print(recvbuf)
207-
208-
MPI programming is difficult. It is "schizophrenic programming" in
209-
that you are writing a single programming with multiple threads of
210-
execution "many voices in one head".

0 commit comments

Comments
 (0)