Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions pyproject.rocm.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ classifiers = [
requires-python = ">=3.10,<3.14"
dependencies = [
"numpy",
"unisim-core==2.0.1",
"unisim-core==2.1.0",
"prettytable>=3.10",
"torch==2.14.0",
"triton-rocm==3.8.0 ; sys_platform == 'linux' and platform_machine == 'x86_64'",
Expand Down Expand Up @@ -72,7 +72,7 @@ mujoco = [
"mujoco~=3.11.0",
"mjbatch-uni~=0.2.5",
]
motrix = ["unisim-core[motrix]==2.0.1"]
motrix = ["unisim-core[motrix]==2.1.0"]

[dependency-groups]
# The RL stack (APPO/off-policy/multi-GPU) is merged into unilab itself
Expand Down
6 changes: 3 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ classifiers = [
requires-python = ">=3.10,<3.14"
dependencies = [
"numpy",
"unisim-core==2.0.1",
"unisim-core==2.1.0",
"prettytable>=3.10",
# A range (not an exact pin) lets ROCm users substitute a ROCm torch
# build; uv.lock pins the tested CUDA builds via tool.uv.sources.
Expand Down Expand Up @@ -106,12 +106,12 @@ newton = [
"pyglet>=2.1.6,<3",
"imgui-bundle>=1.92.0",
]
motrix = ["unisim-core[motrix]==2.0.1"]
motrix = ["unisim-core[motrix]==2.1.0"]
genesis = ["genesis-world==1.3.3"]
# Wheels are CPython 3.12/3.13 Linux x86_64 only; the marker keeps uv lock
# resolvable for the other required-environments.
superdex = [
"unisim-core[superdex]==2.0.1 ; python_version >= '3.12' and sys_platform == 'linux' and platform_machine == 'x86_64'",
"unisim-core[superdex]==2.1.0 ; python_version >= '3.12' and sys_platform == 'linux' and platform_machine == 'x86_64'",
]

[dependency-groups]
Expand Down
16 changes: 10 additions & 6 deletions tests/base/test_genesis_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -566,12 +566,14 @@ def test_dr_capabilities_and_tensor_reset_randomization_boundary(
def test_interval_randomization_and_body_force(fake_genesis, tiny_model_file: str) -> None:
backend = _backend(tiny_model_file)
solver = backend._scene.sim.rigid_solver
force = np.ones((4, 2, 3), dtype=np.float32)
force = torch.ones((4, 2, 3), dtype=torch.float32)
backend.apply_interval_randomization(
IntervalRandomizationPlan(
ops=(
IntervalTermOp(
INTERVAL_TERM_BODY_FORCE, force, body_ids=np.array([1, 3], dtype=np.int32)
INTERVAL_TERM_BODY_FORCE,
force,
body_ids=torch.as_tensor([1, 3], dtype=torch.int32),
),
)
)
Expand All @@ -583,7 +585,7 @@ def test_interval_randomization_and_body_force(fake_genesis, tiny_model_file: st

with pytest.raises(NotImplementedError, match="does not support interval term 'push'"):
backend.apply_interval_randomization(
IntervalRandomizationPlan(ops=(IntervalTermOp(INTERVAL_TERM_PUSH, np.ones((1, 3))),))
IntervalRandomizationPlan(ops=(IntervalTermOp(INTERVAL_TERM_PUSH, torch.ones((1, 3))),))
)
with pytest.raises(
NotImplementedError, match="does not support interval term 'body_linear_velocity_delta'"
Expand All @@ -593,8 +595,8 @@ def test_interval_randomization_and_body_force(fake_genesis, tiny_model_file: st
ops=(
IntervalTermOp(
INTERVAL_TERM_BODY_LINEAR_VELOCITY_DELTA,
np.zeros((4, 1, 3), dtype=np.float32),
body_ids=np.array([1], dtype=np.int32),
torch.zeros((4, 1, 3), dtype=torch.float32),
body_ids=torch.as_tensor([1], dtype=torch.int32),
),
)
)
Expand All @@ -603,7 +605,9 @@ def test_interval_randomization_and_body_force(fake_genesis, tiny_model_file: st
backend.apply_interval_randomization(
IntervalRandomizationPlan(
ops=(
IntervalTermOp(INTERVAL_TERM_BODY_FORCE, np.zeros((4, 1, 3), dtype=np.float32)),
IntervalTermOp(
INTERVAL_TERM_BODY_FORCE, torch.zeros((4, 1, 3), dtype=torch.float32)
),
)
)
)
Expand Down
7 changes: 4 additions & 3 deletions tests/base/test_mjwarp_capabilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import numpy as np
import pytest
import torch
from unisim.backend.mjwarp.dependencies import load_mjwarp_dependencies
from unisim.dr.types import (
INTERVAL_TERM_BODY_LINEAR_VELOCITY_DELTA,
Expand Down Expand Up @@ -73,7 +74,7 @@ def test_interval_push_and_velocity_require_named_bodies() -> None:
with pytest.raises(NotImplementedError, match="push target body"):
backend.apply_interval_randomization(
IntervalRandomizationPlan(
ops=(IntervalTermOp(INTERVAL_TERM_PUSH, np.ones((3,), dtype=np.float32)),)
ops=(IntervalTermOp(INTERVAL_TERM_PUSH, torch.ones((3,), dtype=torch.float32)),)
)
)
with pytest.raises(NotImplementedError, match="exactly one free joint"):
Expand All @@ -82,8 +83,8 @@ def test_interval_push_and_velocity_require_named_bodies() -> None:
ops=(
IntervalTermOp(
INTERVAL_TERM_BODY_LINEAR_VELOCITY_DELTA,
np.zeros((1, 1, 3), dtype=np.float32),
body_ids=np.asarray([1], dtype=np.int32),
torch.zeros((1, 1, 3), dtype=torch.float32),
body_ids=torch.as_tensor([1], dtype=torch.int32),
),
)
)
Expand Down
24 changes: 15 additions & 9 deletions tests/base/test_mjwarp_dr.py
Original file line number Diff line number Diff line change
Expand Up @@ -316,15 +316,15 @@ def test_heavier_world_accelerates_less_under_identical_body_force(backend: Any)
randomization=_tensor_payload(body_mass=body_mass),
)

force = np.zeros((backend.num_envs, 1, 3), dtype=np.float32)
force = torch.zeros((backend.num_envs, 1, 3), dtype=torch.float32)
force[:, 0, 0] = 300.0
backend.apply_interval_randomization(
IntervalRandomizationPlan(
ops=(
IntervalTermOp(
INTERVAL_TERM_BODY_FORCE,
force,
body_ids=np.asarray([backend._base_body_id]),
body_ids=torch.as_tensor([backend._base_body_id]),
),
)
)
Expand All @@ -348,7 +348,11 @@ def test_push_robots_stages_one_step_force(backend: Any) -> None:
_reset_all(backend)
backend.apply_interval_randomization(
IntervalRandomizationPlan(
ops=(IntervalTermOp(INTERVAL_TERM_PUSH, np.asarray([80.0, 0.0, 0.0])),)
ops=(
IntervalTermOp(
INTERVAL_TERM_PUSH, torch.tensor([80.0, 0.0, 0.0], dtype=torch.float64)
),
)
)
)
assert backend._xfrc_pending
Expand All @@ -363,21 +367,23 @@ def test_push_robots_stages_one_step_force(backend: Any) -> None:
def test_interval_velocity_delta_kicks_selected_rows(backend: Any) -> None:
_reset_all(backend)
before = backend.get_base_lin_vel().copy()
delta = np.zeros((backend.num_envs, 1, 3), dtype=np.float32)
delta[[0, 3], 0, :] = np.asarray([[0.4, -0.2, 0.0], [-0.3, 0.1, 0.0]], dtype=np.float32)
delta = torch.zeros((backend.num_envs, 1, 3), dtype=torch.float32)
delta[[0, 3], 0, :] = torch.tensor([[0.4, -0.2, 0.0], [-0.3, 0.1, 0.0]], dtype=torch.float32)
backend.apply_interval_randomization(
IntervalRandomizationPlan(
ops=(
IntervalTermOp(
INTERVAL_TERM_BODY_LINEAR_VELOCITY_DELTA,
delta,
body_ids=np.asarray([backend._base_body_id], dtype=np.int32),
body_ids=torch.as_tensor([backend._base_body_id], dtype=torch.int32),
),
)
)
)
after = backend.get_base_lin_vel()
np.testing.assert_allclose(after[[0, 3]], before[[0, 3]] + delta[[0, 3], 0, :], atol=1e-6)
np.testing.assert_allclose(
after[[0, 3]], before[[0, 3]] + delta[[0, 3], 0, :].numpy(), atol=1e-6
)
np.testing.assert_allclose(after[[1, 2]], before[[1, 2]], atol=1e-6)
# The kick commits through the forward barrier, keeping sensors coherent.
assert np.isfinite(backend.get_sensor_data("torso_upvector")).all()
Expand Down Expand Up @@ -419,15 +425,15 @@ def test_low_friction_world_changes_kick_response(backend: Any) -> None:
randomization=_tensor_payload(geom_friction=geom_friction),
)
start = backend.get_base_pos()[:, :2].copy()
delta = np.zeros((backend.num_envs, 1, 3), dtype=np.float32)
delta = torch.zeros((backend.num_envs, 1, 3), dtype=torch.float32)
delta[:, 0, 0] = 1.5
backend.apply_interval_randomization(
IntervalRandomizationPlan(
ops=(
IntervalTermOp(
INTERVAL_TERM_BODY_LINEAR_VELOCITY_DELTA,
delta,
body_ids=np.asarray([backend._base_body_id], dtype=np.int32),
body_ids=torch.as_tensor([backend._base_body_id], dtype=torch.int32),
),
)
)
Expand Down
5 changes: 3 additions & 2 deletions tests/base/test_motrix_backend_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import numpy as np
import pytest
import torch
from unisim.dr.types import (
INTERVAL_TERM_BODY_FORCE,
RESET_TERM_BODY_IPOS,
Expand Down Expand Up @@ -517,8 +518,8 @@ def test_motrix_backend_interval_body_force_uses_link_external_force_delta() ->
ops=(
IntervalTermOp(
INTERVAL_TERM_BODY_FORCE,
np.asarray([[[1.0, 2.0, 3.0]], [[4.0, 5.0, 6.0]]], dtype=np.float64),
body_ids=np.asarray([0], dtype=np.int32),
torch.as_tensor([[[1.0, 2.0, 3.0]], [[4.0, 5.0, 6.0]]], dtype=torch.float64),
body_ids=torch.as_tensor([0], dtype=torch.int32),
),
)
)
Expand Down
58 changes: 35 additions & 23 deletions tests/base/test_sim_backend_smoke.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,13 +176,17 @@ def test_mujoco_interval_root_velocity_kick_is_row_selective_and_refreshes_senso
body_ids = bkd.get_body_ids(("base",))
state_before = bkd.get_physics_state().copy()
body_velocity_before = _sensor(bkd, "track_linvel_w_base").copy()
delta = np.zeros((NUM_ENVS, 1, 3), dtype=np.float64)
delta[1, 0] = (0.25, -0.4, 0.15)
delta = torch.zeros((NUM_ENVS, 1, 3), dtype=torch.float64)
delta[1, 0] = torch.tensor([0.25, -0.4, 0.15], dtype=torch.float64)

bkd.apply_interval_randomization(
IntervalRandomizationPlan(
ops=(
IntervalTermOp(INTERVAL_TERM_BODY_LINEAR_VELOCITY_DELTA, delta, body_ids=body_ids),
IntervalTermOp(
INTERVAL_TERM_BODY_LINEAR_VELOCITY_DELTA,
delta,
body_ids=torch.as_tensor(body_ids),
),
)
)
)
Expand All @@ -203,12 +207,12 @@ def test_mujoco_interval_root_velocity_kick_is_row_selective_and_refreshes_senso
)
np.testing.assert_allclose(
state_after[1, idx_qvel : idx_qvel + 3],
state_before[1, idx_qvel : idx_qvel + 3] + delta[1, 0],
state_before[1, idx_qvel : idx_qvel + 3] + delta[1, 0].numpy(),
atol=1e-7,
)
np.testing.assert_allclose(
body_velocity_after[1],
body_velocity_before[1] + delta[1, 0],
body_velocity_before[1] + delta[1, 0].numpy(),
atol=1e-6,
)

Expand All @@ -223,14 +227,16 @@ def test_mujoco_interval_root_velocity_kick_rejects_invalid_contracts():
base_name="base",
)
body_ids = bkd.get_body_ids(("base",))
delta = np.zeros((NUM_ENVS, 1, 3), dtype=np.float64)
delta = torch.zeros((NUM_ENVS, 1, 3), dtype=torch.float64)

with pytest.raises(RuntimeError, match="requires a materialized backend"):
bkd.apply_interval_randomization(
IntervalRandomizationPlan(
ops=(
IntervalTermOp(
INTERVAL_TERM_BODY_LINEAR_VELOCITY_DELTA, delta, body_ids=body_ids
INTERVAL_TERM_BODY_LINEAR_VELOCITY_DELTA,
delta,
body_ids=torch.as_tensor(body_ids),
),
)
)
Expand All @@ -244,7 +250,7 @@ def test_mujoco_interval_root_velocity_kick_rejects_invalid_contracts():
IntervalTermOp(
INTERVAL_TERM_BODY_LINEAR_VELOCITY_DELTA,
delta,
body_ids=body_ids.astype(np.float64),
body_ids=torch.as_tensor(body_ids.astype(np.float64)),
),
)
)
Expand All @@ -256,7 +262,7 @@ def test_mujoco_interval_root_velocity_kick_rejects_invalid_contracts():
IntervalTermOp(
INTERVAL_TERM_BODY_LINEAR_VELOCITY_DELTA,
delta,
body_ids=np.asarray([body_ids[0] + 1], dtype=np.int32),
body_ids=torch.as_tensor([int(body_ids[0]) + 1], dtype=torch.int32),
),
)
)
Expand All @@ -267,8 +273,8 @@ def test_mujoco_interval_root_velocity_kick_rejects_invalid_contracts():
ops=(
IntervalTermOp(
INTERVAL_TERM_BODY_LINEAR_VELOCITY_DELTA,
np.zeros((NUM_ENVS + 1, 1, 3), dtype=np.float64),
body_ids=body_ids,
torch.zeros((NUM_ENVS + 1, 1, 3), dtype=torch.float64),
body_ids=torch.as_tensor(body_ids),
),
)
)
Expand All @@ -279,22 +285,22 @@ def test_mujoco_interval_root_velocity_kick_rejects_invalid_contracts():
ops=(
IntervalTermOp(
INTERVAL_TERM_BODY_LINEAR_VELOCITY_DELTA,
np.zeros((NUM_ENVS, 3), dtype=np.float64),
body_ids=body_ids,
torch.zeros((NUM_ENVS, 3), dtype=torch.float64),
body_ids=torch.as_tensor(body_ids),
),
)
)
)
invalid = delta.copy()
invalid[0, 0, 0] = np.nan
invalid = delta.clone()
invalid[0, 0, 0] = torch.nan
with pytest.raises(ValueError, match="contains NaN or Inf"):
bkd.apply_interval_randomization(
IntervalRandomizationPlan(
ops=(
IntervalTermOp(
INTERVAL_TERM_BODY_LINEAR_VELOCITY_DELTA,
invalid,
body_ids=body_ids,
body_ids=torch.as_tensor(body_ids),
),
)
)
Expand Down Expand Up @@ -322,13 +328,17 @@ def test_mujoco_interval_root_angular_velocity_kick_converts_to_body_frame():
_set_state(bkd, np.arange(NUM_ENVS, dtype=np.int32), qpos, qvel)

body_ids = bkd.get_body_ids(("base",))
delta = np.zeros((NUM_ENVS, 1, 3), dtype=np.float64)
delta[1, 0] = (0.5, 0.0, 0.0) # world-frame +x angular rate
delta = torch.zeros((NUM_ENVS, 1, 3), dtype=torch.float64)
delta[1, 0] = torch.tensor([0.5, 0.0, 0.0], dtype=torch.float64) # world-frame +x angular rate

bkd.apply_interval_randomization(
IntervalRandomizationPlan(
ops=(
IntervalTermOp(INTERVAL_TERM_BODY_ANGULAR_VELOCITY_DELTA, delta, body_ids=body_ids),
IntervalTermOp(
INTERVAL_TERM_BODY_ANGULAR_VELOCITY_DELTA,
delta,
body_ids=torch.as_tensor(body_ids),
),
)
)
)
Expand Down Expand Up @@ -396,17 +406,19 @@ def test_mujoco_interval_body_force_and_torque_have_observable_effect(tmp_path):
idx_qvel = 1 + bkd.nq
baseline = bkd.get_physics_state()[:, idx_qvel : idx_qvel + 6].copy()

force = np.zeros((NUM_ENVS, 1, 3), dtype=np.float64)
force = torch.zeros((NUM_ENVS, 1, 3), dtype=torch.float64)
force[:, 0, 2] = 10.0 # N, world +z
torque = np.zeros((NUM_ENVS, 1, 3), dtype=np.float64)
torque = torch.zeros((NUM_ENVS, 1, 3), dtype=torch.float64)
torque[:, 0, 2] = 1.0 # Nm, world z

_set_state(bkd, ids, qpos, qvel0)
bkd.apply_interval_randomization(
IntervalRandomizationPlan(
ops=(
IntervalTermOp(INTERVAL_TERM_BODY_FORCE, force, body_ids=body_ids),
IntervalTermOp(INTERVAL_TERM_BODY_TORQUE, torque, body_ids=body_ids),
IntervalTermOp(INTERVAL_TERM_BODY_FORCE, force, body_ids=torch.as_tensor(body_ids)),
IntervalTermOp(
INTERVAL_TERM_BODY_TORQUE, torque, body_ids=torch.as_tensor(body_ids)
),
)
)
)
Expand Down
10 changes: 5 additions & 5 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading