Source: tests/test_model.py · Plain text

tests/test_model.py

import inspect

import pytest
import torch

from mcss.geometry import look_at, make_intrinsics
from mcss.model.system import ModelConfig, build_model
from mcss.types import Cameras


def _model_inputs() -> tuple[torch.Tensor, Cameras, Cameras, torch.Tensor]:
    torch.manual_seed(3)
    context_rgb = torch.rand(1, 2, 3, 8, 8)
    intrinsics = make_intrinsics((8, 8), 60.0)
    context_poses = torch.stack(
        (
            look_at(torch.tensor([0.0, 0.0, -3.0]), torch.zeros(3)),
            look_at(torch.tensor([2.0, 0.0, -2.0]), torch.zeros(3)),
        )
    )
    target_pose = look_at(torch.tensor([-2.0, 0.0, -2.0]), torch.zeros(3))
    context_cameras = Cameras(
        intrinsics.expand(1, 2, 3, 3).clone(), context_poses.unsqueeze(0), (8, 8)
    )
    target_cameras = Cameras(intrinsics.view(1, 1, 3, 3), target_pose.view(1, 1, 4, 4), (8, 8))
    bounds = torch.tensor([[[-1.2, -1.2, -1.2], [1.2, 1.2, 1.2]]])
    return context_rgb, context_cameras, target_cameras, bounds


def _config(mode: str) -> ModelConfig:
    return ModelConfig(
        mode=mode,
        voxel_resolution=8,
        image_feature_dim=8,
        state_feature_dim=8,
        refinement_blocks=1,
        n_samples=8,
        ray_chunk_size=128,
    )


@pytest.mark.parametrize("mode", ["fixed", "learned_heads", "free_decoder"])
def test_all_model_modes_forward_and_backward(mode: str) -> None:
    model = build_model(_config(mode))
    inputs = _model_inputs()

    output = model(*inputs)

    assert output.state.spatial_shape == (8, 8, 8)
    assert output.predictions["depth"].shape == (1, 1, 1, 8, 8)
    loss = sum(value.mean() for value in output.predictions.values())
    loss.backward()
    gradients = [parameter.grad for parameter in model.parameters() if parameter.requires_grad]
    assert any(gradient is not None for gradient in gradients)
    assert all(torch.isfinite(gradient).all() for gradient in gradients if gradient is not None)


def test_main_forward_has_no_target_label_argument() -> None:
    model = build_model(_config("fixed"))
    signature = inspect.signature(model.forward)

    assert "target_rgb" not in signature.parameters
    assert "target_depth" not in signature.parameters
    with pytest.raises(TypeError):
        model(*_model_inputs(), target_rgb=torch.rand(1, 1, 3, 8, 8))


def test_model_forward_is_finite_under_cuda_autocast() -> None:
    if not torch.cuda.is_available():
        pytest.skip("CUDA is not available")
    device = torch.device("cuda")
    context_rgb = torch.rand(1, 2, 3, 8, 8)
    intrinsics = make_intrinsics((8, 8), 60.0)
    context_poses = torch.stack(
        (
            look_at(torch.tensor([0.0, 0.0, 0.0]), torch.tensor([0.0, 0.0, 1.0])),
            look_at(torch.tensor([1.0, 0.0, -1.0]), torch.zeros(3)),
        )
    )
    target_pose = look_at(torch.tensor([0.0, 0.0, 0.0]), torch.tensor([0.0, 0.0, 1.0]))
    context_cameras = Cameras(
        intrinsics.expand(1, 2, 3, 3).clone(), context_poses.unsqueeze(0), (8, 8)
    )
    target_cameras = Cameras(intrinsics.view(1, 1, 3, 3), target_pose.view(1, 1, 4, 4), (8, 8))
    bounds = torch.tensor([[[-2.0, -2.0, -2.0], [2.0, 2.0, 2.0]]])
    model = build_model(_config("fixed")).to(device)
    context_rgb = context_rgb.to(device)
    context_cameras = context_cameras.to(device)
    target_cameras = target_cameras.to(device)
    bounds = bounds.to(device)

    with torch.autocast(device_type="cuda", enabled=True):
        output = model(context_rgb, context_cameras, target_cameras, bounds)

    assert all(torch.isfinite(value).all() for value in output.predictions.values())
    assert torch.isfinite(output.state.density_logits).all()


def test_model_builds_anisotropic_scene_state() -> None:
    config = ModelConfig(
        mode="fixed",
        voxel_resolution=(6, 8, 10),
        image_feature_dim=8,
        state_feature_dim=8,
        refinement_blocks=0,
        n_samples=8,
        ray_chunk_size=128,
    )
    model = build_model(config)

    output = model(*_model_inputs())

    assert output.state.spatial_shape == (6, 8, 10)