from __future__ import annotations import numpy as np import pytest from cil.chart_features import ( CONTEXT_HASH_WIDTH, OBSERVATION_EMBED_DIM, OBJECT_LAYOUT_EMBED_DIM, build_chart_feature, chart_feature_dim, ) def test_base_chart_feature_preserves_original_flattened_base_action() -> None: base_action = np.arange(12, dtype=np.float32).reshape(3, 4) feature = build_chart_feature(base_action, mode="base") assert feature.dtype == np.float32 assert np.allclose(feature, base_action.reshape(-1)) assert chart_feature_dim(base_action, mode="base") == 12 def test_base_context_feature_is_deterministic_and_extends_base() -> None: base_action = np.zeros((2, 7), dtype=np.float32) metadata = {"task_id": "PickCube-v1", "instruction": "pick up the cube"} first = build_chart_feature(base_action, metadata, mode="base_context") second = build_chart_feature(base_action, metadata, mode="base_context") assert np.allclose(first, second) assert first.shape == (base_action.size + 2 * CONTEXT_HASH_WIDTH + 2,) assert chart_feature_dim(base_action, mode="base_context") == first.shape[0] def test_base_context_feature_does_not_read_outcome_or_hidden_fields() -> None: base_action = np.ones((1, 4), dtype=np.float32) shared = {"task_id": "StackCube-v1", "instruction": "stack the cube"} metadata_a = { **shared, "scalar_utility": 1.0, "delta_U": 0.8, "label": "positive", "outcome_vector": {"success": True}, "hidden_chart_oracle": 1.0, } metadata_b = { **shared, "scalar_utility": -1.0, "delta_U": -0.4, "label": "negative", "outcome_vector": {"success": False}, "hidden_chart_oracle": 0.0, } assert np.allclose( build_chart_feature(base_action, metadata_a, mode="base_context"), build_chart_feature(base_action, metadata_b, mode="base_context"), ) def test_base_context_obs_appends_precomputed_observation_embedding(tmp_path) -> None: base_action = np.zeros((2, 7), dtype=np.float32) embeddings = np.arange(OBSERVATION_EMBED_DIM * 2, dtype=np.float32).reshape( 2, OBSERVATION_EMBED_DIM ) path = tmp_path / "obs_embeddings.npz" np.savez_compressed(path, embeddings=embeddings) metadata = { "task_id": "PullCube-v1", "instruction": "pull the cube", "observation_embedding_path": f"{path.name}#embeddings/1", "_chart_root": str(tmp_path), "scalar_utility": -999.0, "label": "negative", } feature = build_chart_feature(base_action, metadata, mode="base_context_obs") assert feature.shape == ( base_action.size + 2 * CONTEXT_HASH_WIDTH + 2 + OBSERVATION_EMBED_DIM, ) assert np.allclose(feature[-OBSERVATION_EMBED_DIM:], embeddings[1]) assert chart_feature_dim(base_action, mode="base_context_obs") == feature.shape[0] def test_base_context_obj_appends_precomputed_object_embedding(tmp_path) -> None: base_action = np.zeros((2, 7), dtype=np.float32) embeddings = np.arange(OBJECT_LAYOUT_EMBED_DIM * 2, dtype=np.float32).reshape( 2, OBJECT_LAYOUT_EMBED_DIM ) path = tmp_path / "object_embeddings.npz" np.savez_compressed(path, embeddings=embeddings) metadata = { "task_id": "PickCube-v1", "instruction": "pick the cube", "object_embedding_path": f"{path.name}#embeddings/1", "_chart_root": str(tmp_path), "outcome_vector": {"success": False}, "label": "negative", } feature = build_chart_feature(base_action, metadata, mode="base_context_obj") expected = base_action.size + 2 * CONTEXT_HASH_WIDTH + 2 + OBJECT_LAYOUT_EMBED_DIM assert feature.shape == (expected,) assert np.allclose(feature[-OBJECT_LAYOUT_EMBED_DIM:], embeddings[1]) assert chart_feature_dim(base_action, mode="base_context_obj") == expected def test_base_context_obs_obj_appends_both_embeddings(tmp_path) -> None: base_action = np.zeros((1, 4), dtype=np.float32) obs = np.ones((1, OBSERVATION_EMBED_DIM), dtype=np.float32) obj = np.full((1, OBJECT_LAYOUT_EMBED_DIM), 2.0, dtype=np.float32) obs_path = tmp_path / "obs.npz" obj_path = tmp_path / "obj.npz" np.savez_compressed(obs_path, embeddings=obs) np.savez_compressed(obj_path, embeddings=obj) metadata = { "task_id": "StackCube-v1", "instruction": "stack the cube", "observation_embedding_path": f"{obs_path.name}#embeddings/0", "object_embedding_path": f"{obj_path.name}#embeddings/0", "_chart_root": str(tmp_path), } feature = build_chart_feature(base_action, metadata, mode="base_context_obs_obj") expected = ( base_action.size + 2 * CONTEXT_HASH_WIDTH + 2 + OBSERVATION_EMBED_DIM + OBJECT_LAYOUT_EMBED_DIM ) assert feature.shape == (expected,) assert np.allclose(feature[-(OBSERVATION_EMBED_DIM + OBJECT_LAYOUT_EMBED_DIM):-OBJECT_LAYOUT_EMBED_DIM], obs[0]) assert np.allclose(feature[-OBJECT_LAYOUT_EMBED_DIM:], obj[0]) def test_unknown_chart_feature_mode_raises() -> None: with pytest.raises(ValueError, match="unknown chart feature mode"): build_chart_feature(np.zeros((1, 2), dtype=np.float32), mode="outcome")