| from __future__ import annotations |
|
|
| from pathlib import Path |
|
|
| from dovla_cil.data.datasets import CILDataset, write_cil_collection |
| from dovla_cil.data.group_sampler import GroupAwareBatchSampler |
| from dovla_cil.generation.pipeline import generate_cil_dataset |
| from dovla_cil.tasks.library import built_in_toy_tasks |
| from dovla_cil.training.collate import collate_cil_records |
|
|
|
|
| def _make_toy_dataset(tmp_path: Path) -> CILDataset: |
| generate_cil_dataset( |
| backend="toy", |
| tasks=built_in_toy_tasks()[:3], |
| out_dir=tmp_path, |
| num_states_per_task=1, |
| k=4, |
| seed=13, |
| shard_size=8, |
| inline_observations=True, |
| ) |
| return CILDataset(tmp_path) |
|
|
|
|
| def test_cil_dataset_loads_generated_toy_cil(tmp_path: Path) -> None: |
| dataset = _make_toy_dataset(tmp_path) |
| assert len(dataset) == 12 |
| first = dataset[0] |
| assert dataset.get_group(first.group_id)[0] == first |
| assert len(list(dataset.iter_groups())) == 3 |
|
|
|
|
| def test_full_group_sampler_keeps_group_ids_together(tmp_path: Path) -> None: |
| dataset = _make_toy_dataset(tmp_path) |
| sampler = GroupAwareBatchSampler(dataset, mode="full_group", batch_groups=1) |
| batch_indices = next(iter(sampler)) |
| records = [dataset[index] for index in batch_indices] |
| assert len({record.group_id for record in records}) == 1 |
| assert len(records) == 4 |
|
|
|
|
| def test_pair_sampler_returns_same_group_reward_ordered_pairs(tmp_path: Path) -> None: |
| dataset = _make_toy_dataset(tmp_path) |
| sampler = GroupAwareBatchSampler( |
| dataset, |
| mode="pairs", |
| batch_groups=3, |
| pair_count_per_group=2, |
| shuffle=False, |
| seed=3, |
| ) |
| batch_indices = next(iter(sampler)) |
| records = [dataset[index] for index in batch_indices] |
| assert batch_indices.pair_indices |
| for better_index, worse_index in batch_indices.pair_indices: |
| better = records[better_index] |
| worse = records[worse_index] |
| assert better.group_id == worse.group_id |
| assert better.reward.score > worse.reward.score |
|
|
|
|
| def test_collate_returns_tensors_and_metadata(tmp_path: Path) -> None: |
| dataset = _make_toy_dataset(tmp_path) |
| sampler = GroupAwareBatchSampler(dataset, mode="full_group", batch_groups=1) |
| records = [dataset[index] for index in next(iter(sampler))] |
| batch = collate_cil_records(records) |
|
|
| assert batch["observations"].shape[0] == len(records) |
| assert batch["action_features"].shape[0] == len(records) |
| assert batch["effect_features"].shape[0] == len(records) |
| assert batch["rewards"].shape == (len(records),) |
| assert len(batch["instructions"]) == len(records) |
| assert len(batch["action_chunks"]) == len(records) |
| assert len(batch["effects"]) == len(records) |
| assert len(batch["group_ids"]) == len(records) |
| assert len(batch["candidate_types"]) == len(records) |
| assert len(batch["failures"]) == len(records) |
| assert "pair_indices" in batch |
|
|
|
|
| def test_zero_copy_collection_loads_disjoint_source_datasets(tmp_path: Path) -> None: |
| tasks = built_in_toy_tasks() |
| sources = [tmp_path / "source-a", tmp_path / "source-b"] |
| for source, task, seed in zip(sources, tasks[:2], (31, 47), strict=False): |
| generate_cil_dataset( |
| backend="toy", |
| tasks=[task], |
| out_dir=source, |
| num_states_per_task=2, |
| k=3, |
| seed=seed, |
| shard_size=8, |
| inline_observations=True, |
| ) |
| collection_dir = tmp_path / "collection" |
| write_cil_collection(collection_dir, sources, dataset_name="two-task-collection") |
|
|
| dataset = CILDataset(collection_dir) |
|
|
| assert len(dataset) == 12 |
| assert len(dataset.group_ids) == 4 |
| assert dataset.index.metadata["dataset_name"] == "two-task-collection" |
| assert dataset.index.metadata["task_count"] == 2 |
| assert {record.metadata["source_dataset"] for record in dataset} == { |
| str(source.resolve()) for source in sources |
| } |
|
|