Skip to content

Commit bbca09a

Browse files
committed
fix(mine): fallback to test pool when assign_splits hashes all tasks to test
1 parent 79124b3 commit bbca09a

2 files changed

Lines changed: 41 additions & 4 deletions

File tree

‎skillopt_sleep/mine.py‎

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -337,13 +337,36 @@ def _promote_one(*, to: str, from_splits: set[str]) -> None:
337337
else:
338338
t.split = "train"
339339

340+
import logging
341+
340342
# Guarantee val (the gate) is non-empty when we have >=2 real tasks.
341-
# Only promote from train so hash-assigned test tasks stay untouched.
343+
# Only promote from train so hash-assigned test tasks stay untouched,
344+
# UNLESS train is also empty, in which case we must borrow from test.
342345
if len(real) >= 2 and not any(t.split == "val" for t in real):
343-
_promote_one(to="val", from_splits={"train"})
344-
# Guarantee a train pool exists when possible; never borrow from test.
346+
if not any(t.split == "train" for t in real):
347+
logging.getLogger("skillopt_sleep").warning(
348+
f"assign_splits: all {len(real)} real tasks hashed into test "
349+
f"(val_fraction={val_fraction}, test_fraction={test_fraction}, seed={seed}). "
350+
"Borrowing from test to satisfy non-empty val."
351+
)
352+
_promote_one(to="val", from_splits={"test"})
353+
else:
354+
_promote_one(to="val", from_splits={"train"})
355+
356+
# Guarantee a train pool exists when possible; never borrow from test
357+
# UNLESS val has only 1 task (so taking from val would re-empty the gate)
358+
# and test tasks are available.
345359
if not any(t.split == "train" for t in tasks) and len(real) >= 2:
346-
_promote_one(to="train", from_splits={"val"})
360+
val_count = sum(1 for t in real if t.split == "val")
361+
if val_count <= 1 and any(t.split == "test" for t in real):
362+
logging.getLogger("skillopt_sleep").warning(
363+
f"assign_splits: pulling from test to satisfy non-empty train "
364+
f"because val has only {val_count} real task "
365+
f"(val_fraction={val_fraction}, test_fraction={test_fraction}, seed={seed})."
366+
)
367+
_promote_one(to="train", from_splits={"test"})
368+
else:
369+
_promote_one(to="train", from_splits={"val"})
347370
return tasks
348371

349372

‎tests/test_split_hardening_2x3.py‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,20 @@ def test_hash_assigned_test_not_demoted_for_val_top_up(self):
117117
if t.id in test_ids:
118118
self.assertEqual(t.split, "test")
119119

120+
def test_val_and_train_pull_from_test_when_empty(self):
121+
"""When all real tasks hash to test, val and train borrow from test to satisfy non-empty guarantees."""
122+
# Seed 42 with this configuration was observed to put all tasks into test
123+
tasks = assign_splits(
124+
[_task(f"t{i}", f"task {i}") for i in range(5)],
125+
val_fraction=0.10,
126+
test_fraction=0.80,
127+
seed=42,
128+
)
129+
splits = {t.split for t in tasks}
130+
self.assertIn("val", splits, "val must not be empty even if it needs to borrow from test")
131+
self.assertIn("train", splits, "train must not be empty even if it needs to borrow from test")
132+
self.assertIn("test", splits, "test must still contain remaining tasks")
133+
120134

121135
class Pass1ApproachCFractionBoundaries(unittest.TestCase):
122136
"""Pass 1 / approach C: reject invalid fraction knobs early."""

0 commit comments

Comments
 (0)