Skip to content

Commit e8d48e5

Browse files
committed
api: fix numpy broadcasting with mpi
1 parent f6ec38d commit e8d48e5

3 files changed

Lines changed: 30 additions & 4 deletions

File tree

‎devito/builtins/initializers.py‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -278,18 +278,20 @@ def _extend_padding_mpi(function, nbl):
278278
distributed `Data` instead resolves global indices on whichever rank owns
279279
them.
280280
"""
281+
distributor = function.grid.distributor
282+
281283
# `TimeFunction`s are rejected upstream, so `axis` indexes `glb_shape` too
282284
for axis, (nl, nr) in enumerate(as_tuple(nbl)):
283-
glb_max = function.grid.distributor.glb_shape[axis] - 1
285+
glb_max = distributor.glb_shape[axis] - 1
284286

285287
# One Dimension at a time, each reading the planes left by the previous
286288
# ones, so that corners come out filled
287289
for nb, src, lo in ((nl, nl, 0), (nr, glb_max - nr, glb_max - nr + 1)):
288290
if nb <= 0:
289291
continue
292+
# `plane` is size 1 along `axis`, so it broadcasts over the slot
290293
plane = _global_plane(function, axis, src)
291-
function.data[axis_slice(function.ndim, axis, lo, lo + nb)] = \
292-
np.repeat(plane, nb, axis=axis)
294+
function.data[axis_slice(function.ndim, axis, lo, lo + nb)] = plane
293295

294296

295297
def _write_interior(function, data, slices):

‎devito/data/data.py‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,11 @@ def __setitem__(self, glb_idx, val):
347347
processed.append(slice(j.start, j.stop, 1))
348348
else:
349349
processed.append(j)
350-
val_idx = as_tuple(processed)
350+
# An axis `val` broadcasts along carries the same value for every
351+
# rank, so it must be taken whole rather than cut down to this
352+
# rank's share of the destination
353+
val_idx = tuple(slice(None) if n == 1 else j
354+
for j, n in zip(processed, val.shape, strict=True))
351355
elif is_windowed(val):
352356
# Read all of it, there is no decomposition to restrict it to
353357
val_idx = tuple(slice(0, s) for s in val.shape)

‎tests/test_data.py‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1217,6 +1217,26 @@ def test_big_steps(self, mode):
12171217
(slice(6, None, -3), slice(6, None, -3))]:
12181218
self._assert_induced(f.data[gslice], a[gslice])
12191219

1220+
@pytest.mark.parallel(mode=4)
1221+
def test_setitem_broadcast(self, mode):
1222+
"""
1223+
An RHS that is size 1 along an axis broadcasts over that axis, rather
1224+
than being cut down to each rank's share of the destination.
1225+
"""
1226+
grid = Grid(shape=(4, 8), topology=(1, 4))
1227+
f = Function(name='f', grid=grid, space_order=0, dtype=np.int32)
1228+
f.data[:] = 0
1229+
1230+
col = np.array([10, 20, 30, 40], dtype=np.int32)[:, None]
1231+
f.data[:, 0:3] = col
1232+
1233+
expected = np.zeros((4, 8), dtype=np.int32)
1234+
expected[:, 0:3] = col
1235+
1236+
glb = f.data_gather(rank=0)
1237+
if grid.distributor.myrank == 0:
1238+
assert np.array_equal(np.asarray(glb), expected)
1239+
12201240
@pytest.mark.parallel(mode=4)
12211241
def test_setitem(self, mode):
12221242
# __setitem__ mpi slicing tests

0 commit comments

Comments
 (0)