Merge pull request #2377 from aftersomemath:rollout-fix-ndata-check

PiperOrigin-RevId: 718181763
Change-Id: Ie88bf1d26c77478ce3a5ca8fdf29ae90e54b5834
This commit is contained in:
Copybara-Service
2025-01-21 20:11:33 -08:00
3 changed files with 89 additions and 6 deletions
+8 -4
View File
@@ -254,16 +254,20 @@ class Rollout {
} }
// check length d and nthread are consistent // check length d and nthread are consistent
if (this->nthread_ == 0 && py::len(d) > 1) { if (py::len(d) == 0) {
std::ostringstream msg;
msg << "The list of data instances is empty";
throw py::value_error(msg.str());
} else if (this->nthread_ == 0 && py::len(d) > 1) {
std::ostringstream msg; std::ostringstream msg;
msg << "More than one data instance passed but " msg << "More than one data instance passed but "
<< "rollout is configured to run on main thread"; << "rollout is configured to run on main thread";
py::value_error(msg.str()); throw py::value_error(msg.str());
} else if (this->nthread_ != py::len(d)) { } else if (this->nthread_ > 0 && this->nthread_ != py::len(d)) {
std::ostringstream msg; std::ostringstream msg;
msg << "Length of data: " << py::len(d) msg << "Length of data: " << py::len(d)
<< " not equal to nthread: " << this->nthread_; << " not equal to nthread: " << this->nthread_;
py::value_error(msg.str()); throw py::value_error(msg.str());
} }
std::vector<raw::MjData*> data_ptrs(py::len(d)); std::vector<raw::MjData*> data_ptrs(py::len(d));
+1 -1
View File
@@ -172,7 +172,7 @@ class Rollout:
if isinstance(model, list) and nroll == 1: if isinstance(model, list) and nroll == 1:
nroll = len(model) nroll = len(model)
if isinstance(model, list) and len(model) != nroll: if isinstance(model, list) and len(model) > 1 and len(model) != nroll:
raise ValueError( raise ValueError(
f'nroll inferred as {nroll} but model is length {len(model)}' f'nroll inferred as {nroll} but model is length {len(model)}'
) )
+80 -1
View File
@@ -20,9 +20,10 @@ import threading
from absl.testing import absltest from absl.testing import absltest
from absl.testing import parameterized from absl.testing import parameterized
import numpy as np
import mujoco import mujoco
from mujoco import rollout from mujoco import rollout
import numpy as np
# -------------------------- models used for testing --------------------------- # -------------------------- models used for testing ---------------------------
@@ -796,6 +797,84 @@ class MuJoCoRolloutTest(parameterized.TestCase):
np.testing.assert_array_equal(state, state2) np.testing.assert_array_equal(state, state2)
np.testing.assert_array_equal(sensordata, sensordata2) np.testing.assert_array_equal(sensordata, sensordata2)
def test_length_one_model_list(self):
model = mujoco.MjModel.from_xml_string(TEST_XML)
nstate = mujoco.mj_stateSize(model, mujoco.mjtState.mjSTATE_FULLPHYSICS)
data = mujoco.MjData(model)
initial_state = np.random.randn(nstate)
control = np.random.randn(3, 3, model.nu)
state, sensordata = rollout.rollout(model, data, initial_state, control)
state2, sensordata2 = rollout.rollout([model], data, initial_state, control)
# assert that we get same outputs
np.testing.assert_array_equal(state, state2)
np.testing.assert_array_equal(sensordata, sensordata2)
def test_data_sizes(self):
model = mujoco.MjModel.from_xml_string(TEST_XML)
nstate = mujoco.mj_stateSize(model, mujoco.mjtState.mjSTATE_FULLPHYSICS)
data = mujoco.MjData(model)
initial_state = np.random.randn(nstate)
control = np.random.randn(3, 3, model.nu)
# Test passing empty lists for data
with self.assertRaisesWithLiteralMatch(
ValueError, 'The list of data instances is empty'
):
rollout.rollout(model, [], initial_state, control)
with self.assertRaisesWithLiteralMatch(
ValueError, 'The list of data instances is empty'
):
with rollout.Rollout(nthread=0) as rollout_:
rollout_.rollout(model, [], initial_state, control)
with self.assertRaisesWithLiteralMatch(
ValueError, 'The list of data instances is empty'
):
with rollout.Rollout(nthread=1) as rollout_:
rollout_.rollout(model, [], initial_state, control)
with self.assertRaisesWithLiteralMatch(
ValueError, 'The list of data instances is empty'
):
with rollout.Rollout(nthread=2) as rollout_:
rollout_.rollout(model, [], initial_state, control)
# Test checking that len(data) equals nthread
with self.assertRaisesWithLiteralMatch(
ValueError,
'More than one data instance passed but rollout is configured to run on'
' main thread',
):
with rollout.Rollout(nthread=0) as rollout_:
rollout_.rollout(
model, [copy.copy(data) for i in range(2)], initial_state, control
)
with self.assertRaisesWithLiteralMatch(
ValueError, 'Length of data: 1 not equal to nthread: 2'
):
with rollout.Rollout(nthread=2) as rollout_:
rollout_.rollout(model, data, initial_state, control)
with self.assertRaisesWithLiteralMatch(
ValueError, 'Length of data: 1 not equal to nthread: 2'
):
with rollout.Rollout(nthread=2) as rollout_:
rollout_.rollout(model, [data], initial_state, control)
with self.assertRaisesWithLiteralMatch(
ValueError, 'Length of data: 3 not equal to nthread: 2'
):
with rollout.Rollout(nthread=2) as rollout_:
rollout_.rollout(
model, [copy.copy(data) for i in range(3)], initial_state, control
)
# -------------- Python implementation of rollout functionality ---------------- # -------------- Python implementation of rollout functionality ----------------