diff --git a/brax/training/agents/ppo/train.py b/brax/training/agents/ppo/train.py index 8929f44c3..458970e0e 100644 --- a/brax/training/agents/ppo/train.py +++ b/brax/training/agents/ppo/train.py @@ -753,16 +753,11 @@ def training_epoch_with_timing( {}, ) - devices = jax.local_devices()[:local_devices_to_use] - mesh = jax.sharding.Mesh(np.array(devices), ('_device_put_sharded',)) - sharding = jax.NamedSharding(mesh, jax.P('_device_put_sharded')) - - def _replicate(x): - if isinstance(x, jax.Array): - return jax.device_put(jnp.stack([x] * len(devices)), sharding) - return jax.device_put(np.stack([x] * len(devices)), sharding) - - training_state = jax.tree_util.tree_map(_replicate, training_state) + training_state = pmap.bcast_local_devices( + training_state, + local_devices_to_use, + axis_name=_PMAP_AXIS_NAME, + ) eval_env = _maybe_wrap_env( eval_env or environment, diff --git a/brax/training/pmap.py b/brax/training/pmap.py index abf452b17..5979d4380 100644 --- a/brax/training/pmap.py +++ b/brax/training/pmap.py @@ -22,11 +22,13 @@ import numpy as np -def bcast_local_devices(value, local_devices_to_use=1): +def bcast_local_devices( + value, local_devices_to_use=1, axis_name='_device_put_sharded' +): """Broadcasts an object to all local devices.""" devices = jax.local_devices()[:local_devices_to_use] - mesh = jax.sharding.Mesh(np.array(devices), ('_device_put_sharded',)) - sharding = jax.NamedSharding(mesh, jax.P('_device_put_sharded')) + mesh = jax.sharding.Mesh(np.array(devices), (axis_name,)) + sharding = jax.NamedSharding(mesh, jax.P(axis_name)) def _replicate(x): if isinstance(x, jax.Array): diff --git a/brax/training/pmap_test.py b/brax/training/pmap_test.py new file mode 100644 index 000000000..5960e2bfd --- /dev/null +++ b/brax/training/pmap_test.py @@ -0,0 +1,35 @@ +# Copyright 2026 The Brax Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tests for pmap utilities.""" + +from absl.testing import absltest +from brax.training import pmap +import jax +import jax.numpy as jnp + + +class PmapTest(absltest.TestCase): + + def testBcastLocalDevicesUsesRequestedAxisName(self): + value = pmap.bcast_local_devices( + jnp.arange(4), local_devices_to_use=1, axis_name='i' + ) + + self.assertEqual(value.sharding.mesh.axis_names, ('i',)) + self.assertEqual(value.sharding.spec, jax.P('i')) + + +if __name__ == '__main__': + absltest.main()