|
16 | 16 | import unittest |
17 | 17 | from absl.testing import parameterized |
18 | 18 | import jax |
19 | | -from jax.experimental import multihost_utils |
20 | 19 | import jax.numpy as jnp |
21 | 20 | import numpy as np |
22 | 21 |
|
@@ -77,43 +76,6 @@ def _assert_allclose(self, x, y, **kwargs): |
77 | 76 | self.assertTupleEqual(x.shape, y.shape) |
78 | 77 | np.testing.assert_allclose(x, y, **kwargs) |
79 | 78 |
|
80 | | - def assert_allclose_mcjax(self, x, y, *, rtol, atol): |
81 | | - """`allclose` that is safe under multi-controller (multi-host) JAX. |
82 | | -
|
83 | | - Some tests build their device mesh from a subset of the global devices -- |
84 | | - e.g. `jax.devices()[:ring_size]`, which all live on process 0 -- so the |
85 | | - result `jax.Array`s are only addressable on that one process. Pulling them |
86 | | - to host with `np.testing.assert_allclose` (as `_assert_allclose` does) |
87 | | - raises `RuntimeError: ... spans non-addressable devices` on every *other* |
88 | | - process, failing the test on all but one host. |
89 | | -
|
90 | | - Instead, evaluate the comparison on-device (works on all processes, no host |
91 | | - fetch), read the scalar verdict only on the owning process (process 0, which |
92 | | - holds `jax.devices()[:ring_size]`), and broadcast it to every process with a |
93 | | - single collective. Every process runs the same two `broadcast_one_to_all` |
94 | | - calls in the same order, so there is no collective-participation mismatch / |
95 | | - deadlock. The result is identical on all hosts and genuinely reflects the |
96 | | - owner's computation. |
97 | | -
|
98 | | - Only use this for tests whose mesh is a subset of one process's devices; for |
99 | | - fully-sharded (every-process-owns-a-shard) arrays use `_assert_allclose`. |
100 | | - """ |
101 | | - if x.dtype == np.dtype(jnp.bfloat16): |
102 | | - x = x.astype(jnp.float32) |
103 | | - if y.dtype == np.dtype(jnp.bfloat16): |
104 | | - y = y.astype(jnp.float32) |
105 | | - self.assertTupleEqual(x.shape, y.shape) |
106 | | - ok = jnp.all(jnp.abs(x - y) <= atol + rtol * jnp.abs(y)) |
107 | | - max_err = jnp.max(jnp.abs(x - y)) |
108 | | - is_owner = jax.process_index() == 0 |
109 | | - local_ok = np.asarray(ok) if is_owner else np.array(True) |
110 | | - local_err = np.asarray(max_err) if is_owner else np.array(0.0, np.float32) |
111 | | - global_err = float(multihost_utils.broadcast_one_to_all(local_err)) |
112 | | - self.assertTrue( |
113 | | - bool(multihost_utils.broadcast_one_to_all(local_ok)), |
114 | | - f"arrays differ: max abs err {global_err:.3e} exceeds rtol={rtol} atol={atol}", |
115 | | - ) |
116 | | - |
117 | 79 |
|
118 | 80 | def create_segment_ids(seq_len: int, num_breaks: int = 2) -> base.SegmentIds: |
119 | 81 | break_indices = np.random.choice(range(1, seq_len), num_breaks, replace=False) |
|
0 commit comments