vla / workspace /tests /test_cil_schema.py
anhtld's picture
auto-sync 2026-07-02T17:27:17Z workspace (part 4)
b3bbe96 verified
Raw
History Blame Contribute Delete
5.39 kB
from __future__ import annotations
import pytest
from dovla_cil.data.schema import (
CIL_VERSION,
ActionChunk,
CILBenchBranch,
CILBenchGroup,
CILGroup,
CILRecord,
FailureInfo,
OutcomeVector,
RewardInfo,
StructuredEffect,
compute_regret_and_ranks,
compute_state_hash,
make_record_id,
validate_group,
)
def make_record(
group_id: str, action_id: str, progress: float, *, state_hash: str = "s"
) -> CILRecord:
return CILRecord(
version=CIL_VERSION,
record_id=make_record_id(group_id, action_id, seed=7),
group_id=group_id,
state_hash=state_hash,
task_id="task",
scene_id=None,
instruction="move the mug",
instruction_family={"family": "place"},
observation_ref=None,
observation_inline={"symbolic": True},
action_chunk=ActionChunk(
action_id=action_id,
representation="delta_xy",
horizon=1,
values=[[progress, 0.0]],
skill_type="push",
),
next_observation_ref=None,
next_observation_inline={"symbolic": True, "next": True},
structured_effect=StructuredEffect(
object_pose_delta={"mug": [progress, 0.0, 0.0]},
relation_before={"inside(mug,bowl)": False},
relation_after={"inside(mug,bowl)": progress > 0.5},
moved_objects=["mug"] if progress else [],
symbolic_before={"objects": {"mug": {"position": [0, 0, 0]}}},
symbolic_after={"objects": {"mug": {"position": [progress, 0, 0]}}},
),
reward=RewardInfo(
progress=progress,
success=progress > 0.5,
terminal_success=progress > 0.5,
dense_components={"progress": progress},
),
regret=None,
rank_within_group=None,
candidate_type="sampled",
failure=None
if progress > 0
else FailureInfo(type="no_motion", symbolic_reason="object did not move"),
)
def test_schema_roundtrip() -> None:
record = make_record("g", "a", 1.0)
record.validate()
assert CILRecord.from_dict(record.to_dict()) == record
group = CILGroup.from_records([record])
assert group.group_id == "g"
def test_group_validation() -> None:
records = [make_record("g", "a", 0.0), make_record("g", "b", 1.0)]
validate_group(records)
with pytest.raises(ValueError):
validate_group([make_record("g", "a", 0.0), make_record("other", "b", 1.0)])
with pytest.raises(ValueError):
validate_group([make_record("g", "a", 0.0), make_record("g", "b", 1.0, state_hash="x")])
def test_regret_and_rank_computation() -> None:
ranked = compute_regret_and_ranks(
[make_record("g", "low", 0.0), make_record("g", "high", 1.0)]
)
by_action = {record.action_chunk.action_id: record for record in ranked}
assert by_action["high"].rank_within_group == 0
assert by_action["high"].regret == 0.0
assert by_action["low"].rank_within_group == 1
assert by_action["low"].regret == 2.0
def test_deterministic_record_ids_and_state_hashes() -> None:
assert make_record_id("g", "a", 1) == make_record_id("g", "a", 1)
assert make_record_id("g", "a", 1) != make_record_id("g", "a", 2)
assert compute_state_hash(b"state") == compute_state_hash(b"state")
assert compute_state_hash(b"state") != compute_state_hash(b"other")
def test_outcome_vector_from_reward_and_utility() -> None:
reward = RewardInfo(
progress=0.6,
success=True,
terminal_success=True,
dense_components={
"contact_quality": 0.5,
"task_stage_quality": 0.25,
"smoothness": 0.8,
"recovery": 1.0,
},
)
outcome = OutcomeVector.from_reward(reward)
assert outcome.success == 1.0
assert outcome.progress == 0.6
assert outcome.contact_quality == 0.5
assert outcome.safety_violation == 0.0
assert outcome.lexicographic_utility() > reward.score
def test_cilbench_group_roundtrip_and_same_state_contrast() -> None:
base_action = ActionChunk(action_id="base", horizon=1, values=[[0.0, 0.0]])
repair_action = ActionChunk(action_id="repair", horizon=1, values=[[0.1, 0.0]])
group = CILBenchGroup(
group_id="chart-0",
task_id="PickCube-v1",
split_id="train",
simulator_state_hash="hash",
instruction="pick the cube",
observation_ref=None,
observation_inline={"rgb": "obs/000.png"},
scene_metadata={"target_object": "cube"},
anchor_policy="h16_bc",
branches=[
CILBenchBranch(
branch_id="base",
action=base_action,
branch_family="anchor",
outcome=OutcomeVector(success=0.0, progress=0.2),
),
CILBenchBranch(
branch_id="repair",
action=repair_action,
branch_family="recovery_tangent",
outcome=OutcomeVector(success=1.0, progress=0.8, recovery=1.0),
),
],
)
restored = CILBenchGroup.from_dict(group.to_dict())
assert restored == group
assert restored.same_state_causal_contrast("repair", "base") > 0.0
with pytest.raises(KeyError):
restored.same_state_causal_contrast("repair", "missing")