"""Deterministic low-data subset selection. The SAME function is used by (a) the segmentation trainer's real-data loader and (b) the pixel-diffusion generator's data loader, so that a low-data experiment at fraction f trains the generator on EXACTLY the real images the segmenter sees — no leakage, no mismatch, fully reproducible. Selection is driven only by (fraction, fraction_seed) and the SORTED list of items, so it is independent of the training seed: the 3 segmentation seeds all use the identical real subset, only their weight init differs. """ from __future__ import annotations import math import random from typing import List, Sequence, TypeVar T = TypeVar("T") def select_fraction(items: Sequence[T], fraction: float, seed: int = 0) -> List[T]: """Return a deterministic subset of `items` of size ceil(fraction*N). items: any sequence (e.g. list of (image_path, mask_path) tuples). It is sorted by repr() first so order of discovery never affects the subset. fraction: in (0, 1]. >=1 returns all items (sorted copy). seed: subset seed (NOT the training seed). Fixed across training seeds. """ ordered = sorted(items, key=lambda x: repr(x)) n = len(ordered) if fraction >= 1.0 or n == 0: return ordered if fraction <= 0.0: raise ValueError(f"fraction must be in (0,1], got {fraction}") k = max(1, math.ceil(fraction * n)) rng = random.Random(seed) idx = list(range(n)) rng.shuffle(idx) keep = sorted(idx[:k]) return [ordered[i] for i in keep]