EvolvingNav / code /tests /test_paper_code.py
fengnian1678's picture
Mirror ZJU4EmbodiedAI/EvolvingNav at dfa6872
ad91e86 verified
Raw History Blame Contribute Delete
14 kB
from __future__ import annotations
import numpy as np
from evolvingnav_paper.policy import candidate_utility, pack_public_query, predict_public, rank_candidates
from evolvingnav_paper.evaluate import evaluate_search
from evolvingnav_paper.backend import visible_fraction_from_masks
def test_public_packing_never_exposes_labels_or_world_metadata() -> None:
query = {
"query_id": "q1", "world_variant": "routine",
"input": {
"target": {"instance_uuid": "mug_1", "category": "mug"},
"query": {"query_time_s": 100000.0},
"history_summary": {"elapsed_since_last_positive_s": 10.0},
"target_history": [{
"timestamp_s": 99990.0, "delta_t_from_previous_s": 0.0,
"event_type": "positive_observation", "observed_state_id": 1,
"detector_confidence": 1.0, "instance_match_confidence": 1.0,
"visible_fraction_estimate": 1.0,
}],
"candidate_state_ids": [1, 2],
},
}
schema = {"category_to_id": {"mug": 0}, "state_count_including_unknown": 3}
features = {
"candidate_region_category_id": np.zeros(3, dtype=np.int16),
"candidate_receptacle_category_id": np.zeros(3, dtype=np.int16),
"candidate_center_xyz": np.zeros((3, 3), dtype=np.float32),
"candidate_is_unknown": np.zeros(3, dtype=bool),
}
arrays = pack_public_query(query, schema, features)
assert "y_current_state" not in arrays
assert "meta_world_variant_id" not in arrays
assert arrays["observed_state_id"][0, -1] == 1
def test_search_uses_public_cost_and_evaluator_scores_private_goal() -> None:
belief = {1: 0.75, 2: 0.25}
order = rank_candidates(belief, {1: 3.0, 2: 1.0})
assert order == [1, 2]
truth = {"current_state_id": 2, "oracle_shortest_path_m": 1.0}
public = {
"base_episode_id": "ep",
"episode_budget": {"max_candidate_inspections": 2, "max_path_length_m": 8.0},
"success_spec": {"max_geodesic_distance_m": 1.0},
}
def route(a, b):
return abs(a - b)
def inspect(state, _viewpoint):
return {
"detected": state == 2, "visible_fraction": 1.0,
"distance_to_valid_goal_m": 0.0,
}
result = evaluate_search(
public, truth, belief, start=0.0, goals={1: 3.0, 2: 4.0},
distance=route, inspect=inspect,
)
assert result["success"] is True
assert result["inspections"] == 2
assert result["path_m"] == 4.0
assert result["spl"] == 0.25
def test_candidate_utility_matches_equation_twelve() -> None:
assert candidate_utility(
arrival_probability=0.6, new_detection_probability=0.8,
distance_m=2.0, eta_s=5.0, lambda_time=0.2, lambda_inspect=1.0,
) == 0.12
def test_n1_selects_belief_top1_even_when_other_goal_is_nearer() -> None:
belief = {1: 0.7, 2: 0.3}
costs = {1: 9.0, 2: 1.0}
assert rank_candidates(belief, costs, task="n1") == [1]
def test_model_receives_last_state_derived_from_public_history() -> None:
import torch
class RecordingModel:
def __call__(self, batch):
assert batch["last_state"].tolist() == [1]
assert batch["last_candidate_index"].tolist() == [0]
return {"probabilities": torch.tensor([[0.8, 0.2]])}
arrays = {
"event_type": np.array([[1]], dtype=np.int8),
"history_mask": np.array([[True]]),
"observed_state_id": np.array([[1]], dtype=np.int16),
"candidate_state_ids": np.array([[1, 2]], dtype=np.int16),
"candidate_mask": np.array([[True, True]]),
"instance_uuid": np.array(["mug_1"]),
}
schema = {"instance_uuid_to_id": {"mug_1": 0}}
assert predict_public(RecordingModel(), schema, arrays) == {1: 0.800000011920929, 2: 0.20000000298023224}
def test_transition_model_batch_contains_no_evaluator_fields() -> None:
from evolvingnav_paper.policy import model_input_batch
arrays = {
"event_type": np.array([[1]], dtype=np.int8),
"history_mask": np.array([[True]]),
"observed_state_id": np.array([[1]], dtype=np.int16),
"candidate_state_ids": np.array([[1, 2]], dtype=np.int16),
"candidate_mask": np.array([[True, True]]),
"instance_uuid": np.array(["mug_1"]),
"evaluation_private": np.array([123]),
}
result = model_input_batch(arrays, {"instance_uuid_to_id": {"mug_1": 0}})
assert "evaluation_private" not in result
assert result["last_state"].item() == 1
def test_public_packing_rejects_future_observation() -> None:
import pytest
query = {
"query_id": "future", "input": {
"target": {"instance_uuid": "mug_1", "category": "mug"},
"query": {"query_time_s": 100.0},
"history_summary": {"elapsed_since_last_positive_s": 0.0},
"target_history": [{
"timestamp_s": 101.0, "delta_t_from_previous_s": 0.0,
"event_type": "positive_observation", "observed_state_id": 1,
"detector_confidence": 1.0, "instance_match_confidence": 1.0,
"visible_fraction_estimate": 1.0,
}],
"candidate_state_ids": [1, 2],
},
}
schema = {"category_to_id": {"mug": 0}, "state_count_including_unknown": 3}
features = {
"candidate_region_category_id": np.zeros(3, dtype=np.int16),
"candidate_receptacle_category_id": np.zeros(3, dtype=np.int16),
"candidate_center_xyz": np.zeros((3, 3), dtype=np.float32),
"candidate_is_unknown": np.zeros(3, dtype=bool),
}
with pytest.raises(ValueError, match="future"):
pack_public_query(query, schema, features)
def test_n2_replans_from_current_viewpoint_and_stops_at_verified_goal() -> None:
episode = {
"base_episode_id": "ep", "episode_budget": {
"max_candidate_inspections": 3, "max_path_length_m": 30.0,
},
"success_spec": {"max_geodesic_distance_m": 1.0, "require_stop_action": True},
}
truth = {"current_state_id": 3, "oracle_shortest_path_m": 5.0}
belief = {1: 0.8, 2: 0.12, 3: 0.08}
goals = {1: 4.0, 2: -3.0, 3: 5.0}
def inspect(state, viewpoint):
return {
"detected": state == 3,
"visible_fraction": 0.9 if state == 3 else 0.0,
"distance_to_valid_goal_m": 0.0 if state == 3 else 5.0,
}
result = evaluate_search(
episode, truth, belief, start=0.0, goals=goals,
distance=lambda a, b: abs(a - b), inspect=inspect,
)
assert result["inspection_order"] == [1, 3]
assert result["actions"][-1] == "STOP"
assert result["success"] is True
assert result["inspection_evidence"][-1]["state_id"] == 3
assert result["inspection_evidence"][-1]["visible_fraction"] == 0.9
def test_correct_state_without_valid_view_is_not_success() -> None:
episode = {
"base_episode_id": "ep", "episode_budget": {
"max_candidate_inspections": 1, "max_path_length_m": 10.0,
},
"success_spec": {"max_geodesic_distance_m": 1.0, "require_stop_action": True},
}
truth = {"current_state_id": 1, "oracle_shortest_path_m": 2.0}
def inspect(_state, _viewpoint):
return {"detected": True, "visible_fraction": 0.8, "distance_to_valid_goal_m": 1.5}
result = evaluate_search(
episode, truth, {1: 1.0}, start=0.0, goals={1: 2.0},
distance=lambda a, b: abs(a - b), inspect=inspect, task="n1",
)
assert result["success"] is False
assert result["actions"][-1] == "STOP"
def test_verified_instance_at_neighbor_state_viewpoint_can_succeed() -> None:
episode = {
"base_episode_id": "ep", "episode_budget": {
"max_candidate_inspections": 1, "max_path_length_m": 10.0,
},
"success_spec": {"max_geodesic_distance_m": 1.0},
}
truth = {"current_state_id": 2, "oracle_shortest_path_m": 2.0}
result = evaluate_search(
episode, truth, {1: 1.0}, start=0.0, goals={1: 2.0},
distance=lambda a, b: abs(a - b),
inspect=lambda _state, _viewpoint: {
"detected": True, "visible_fraction": 0.8,
"distance_to_valid_goal_m": 0.4,
},
task="n1",
)
assert result["success"] is True
def test_paper_success_requires_twenty_percent_visibility() -> None:
episode = {
"base_episode_id": "ep", "episode_budget": {
"max_candidate_inspections": 1, "max_path_length_m": 10.0,
},
"success_spec": {"max_geodesic_distance_m": 1.0, "min_visible_fraction": 0.05},
}
truth = {"current_state_id": 1, "oracle_shortest_path_m": 2.0}
def inspect(_state, _viewpoint):
return {"detected": True, "visible_fraction": 0.19, "distance_to_valid_goal_m": 0.0}
result = evaluate_search(
episode, truth, {1: 1.0}, start=0.0, goals={1: 2.0},
distance=lambda a, b: abs(a - b), inspect=inspect, task="n1",
)
assert result["success"] is False
def test_visible_fraction_uses_target_only_projection_as_denominator() -> None:
actual = np.array([[7, 7, 0], [0, 7, 0]])
target_only = np.array([[7, 7, 7], [7, 7, 0]])
assert visible_fraction_from_masks(actual, target_only, semantic_id=7) == 0.6
def test_grounded_sam_policy_detection_uses_rgb_depth_and_category_only() -> None:
from evolvingnav_paper.perception import detect_category
def detector(rgb, categories):
assert rgb.shape == (2, 2, 3)
assert categories == ("bottle",)
return (("bottle", 0.9, (0.0, 0.0, 1.0, 1.0)),)
def segmenter(rgb, box):
assert rgb.shape == (2, 2, 3)
assert box == (0.0, 0.0, 1.0, 1.0)
return np.ones((2, 2), dtype=bool)
assert detect_category(
detector, segmenter, np.zeros((2, 2, 4), dtype=np.uint8), "bottle"
) is True
def test_grounded_sam_returns_mask_for_rgbd_memory_backprojection() -> None:
from evolvingnav_paper.perception import detect_instances
found = detect_instances(
lambda _rgb, _categories: (("bottle", 0.9, (0., 0., 1., 1.)),),
lambda _rgb, _box: np.ones((2, 2), dtype=bool),
np.zeros((2, 2, 4), dtype=np.uint8), "bottle",
)
assert len(found) == 1
assert found[0].confidence == 0.9
assert found[0].mask.sum() == 4
def test_grounded_sam_requires_a_nonempty_target_category_mask() -> None:
from evolvingnav_paper.perception import detect_category
rgb = np.zeros((2, 2, 3), dtype=np.uint8)
def detector(_rgb, _categories):
return (
("book", 0.9, (0.0, 0.0, 1.0, 1.0)),
("bottle", 0.8, (0.0, 0.0, 1.0, 1.0)),
)
def empty_mask(_rgb, _box):
return np.zeros((2, 2), dtype=bool)
assert detect_category(detector, empty_mask, rgb, "bottle") is False
def test_training_cli_exposes_paper_optimizer_parameters(monkeypatch) -> None:
import sys
from train_p4d_belief import arguments
monkeypatch.setattr(sys, "argv", [
"train_p4d_belief.py", "--dataset-root", "/data/p4d", "--output", "/tmp/p4d",
"--weight-decay", "0.01", "--gradient-clip", "1.0",
])
args = arguments()
assert args.weight_decay == 0.01
assert args.gradient_clip == 1.0
def test_training_cli_defaults_match_paper_configuration(monkeypatch) -> None:
import sys
from train_p4d_belief import arguments
monkeypatch.setattr(sys, "argv", [
"train_p4d_belief.py", "--dataset-root", "/data/p4d", "--output", "/tmp/p4d",
])
args = arguments()
assert args.models == ["p4d"]
assert args.seeds == [0, 1, 2, 3, 4]
assert (args.layers, args.hidden_dim, args.heads, args.dropout) == (3, 128, 4, 0.1)
assert (args.batch_size, args.patience, args.weight_decay) == (64, 10, 0.01)
assert args.eval_split == "val"
assert args.use_instance_identity is True
assert args.use_compatibility is True
def test_paper_compatibility_head_receives_state_loss_gradient() -> None:
import torch
from readyagent.p4d_belief.data import Catalog
from readyagent.p4d_belief.models import ModelConfig, P4DBelief, state_nll
catalog = Catalog(
region_category=torch.tensor([0, 0, 0]),
receptacle_category=torch.tensor([0, 0, 0]),
center_xyz=torch.tensor([[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 0.0, 1.0]]),
is_unknown=torch.tensor([False, False, True]),
region_instance_ids=("room", "room", "unknown"),
schema={
"region_category_to_id": {"room": 0},
"receptacle_category_to_id": {"shelf": 0},
"category_to_id": {"mug": 0},
},
)
batch = {
"event_type": torch.tensor([[1, 0]]),
"event_time_days": torch.tensor([[0.0, 0.0]]),
"observed_state_id": torch.tensor([[0, -1]]),
"candidate_state_id": torch.tensor([[-1, -1]]),
"evidence_features": torch.zeros((1, 2, 6)),
"history_mask": torch.tensor([[True, False]]),
"candidate_state_ids": torch.tensor([[0, 1, 2]]),
"candidate_mask": torch.tensor([[True, True, True]]),
"target_category_id": torch.tensor([0]),
"query_time_days": torch.tensor([0.1]),
"query_time_of_day_sin_cos": torch.tensor([[0.0, 1.0]]),
"query_weekday_id": torch.tensor([0]),
"elapsed_since_last_positive_days": torch.tensor([0.1]),
"last_state": torch.tensor([0]),
"last_candidate_index": torch.tensor([0]),
"target_candidate_index": torch.tensor([1]),
}
model = P4DBelief(catalog, ModelConfig(
hidden_dim=32, layers=1, heads=4, dropout=0.0, use_compatibility=True,
))
loss = state_nll(model(batch)["probabilities"], batch["target_candidate_index"])
loss.backward()
gradients = [parameter.grad for parameter in model.compatibility.parameters()]
assert all(gradient is not None for gradient in gradients)
assert sum(float(gradient.abs().sum()) for gradient in gradients) > 0