rpx_benchmark.vqa.sampling

Deterministic, diversity-preferring row selection shared by the acceptance-manifest and benchmark-plan builders.

 1"""Deterministic, diversity-preferring row selection shared by the
 2acceptance-manifest and benchmark-plan builders."""
 3
 4from __future__ import annotations
 5
 6import hashlib
 7from typing import Any, Callable
 8
 9from .canonical import semantic_key
10
11
12def rank(seed: int, identity: str) -> str:
13    return hashlib.sha256(f"{seed}:{identity}".encode()).hexdigest()
14
15
16def row_identity(row: dict[str, Any]) -> str:
17    return "\x1f".join(
18        str(row.get(key, ""))
19        for key in ("scene_id", "kind", "phase", "frame", "type", "question", "sample_id")
20    )
21
22
23def is_centered(row: dict[str, Any]) -> bool:
24    """True if the answer bbox centroid is in the middle 50% of both axes.
25    In-context rows already carry a precomputed answer_is_centered column
26    (built with this exact rule at generation time); normal rows do not, so
27    it is computed here from answer_bbox/img_w/img_h."""
28    if row.get("answer_is_centered") is not None:
29        return bool(row["answer_is_centered"])
30    bbox = row.get("answer_bbox")
31    if bbox is None:
32        return False
33    x0, y0, x1, y1 = bbox
34    cx = (x0 + x1) / (2 * row["img_w"])
35    cy = (y0 + y1) / (2 * row["img_h"])
36    return 0.25 <= cx <= 0.75 and 0.25 <= cy <= 0.75
37
38
39class DeterministicSelector:
40    """Greedy, seeded selection across many (cell -> quota) groups, sharing
41    one running state so "prefer distinct scenes/frames" and "no semantic
42    alias duplicate" hold across the whole output, not just within one cell.
43    """
44
45    def __init__(self, seed: int) -> None:
46        self.seed = seed
47        self.used_scene_frame: set[tuple[str, str]] = set()
48        self.used_semantic_keys: set[tuple] = set()
49        self.used_row_ids: set[str] = set()
50
51    def _rank_key(self, row: dict[str, Any]) -> tuple:
52        scene_frame = (str(row["scene_id"]), str(row["frame"]))
53        # Rows whose (scene, frame) is already used sort after rows that
54        # would introduce a new one -- "prefer distinct scenes/frames" as a
55        # soft, deterministic tiebreak, not a hard requirement.
56        reused = scene_frame in self.used_scene_frame
57        return (reused, rank(self.seed, row_identity(row)))
58
59    def select_cell(self, candidates: list[dict[str, Any]], quota: int) -> list[dict[str, Any]]:
60        ordered = sorted(candidates, key=self._rank_key)
61        picked: list[dict[str, Any]] = []
62        for row in ordered:
63            if len(picked) >= quota:
64                break
65            row_id = row_identity(row)
66            if row_id in self.used_row_ids:
67                continue
68            key = semantic_key(row)
69            if key in self.used_semantic_keys:
70                continue
71            picked.append(row)
72            self.used_row_ids.add(row_id)
73            self.used_semantic_keys.add(key)
74            self.used_scene_frame.add((str(row["scene_id"]), str(row["frame"])))
75        return picked
76
77    def select(
78        self,
79        candidates_by_cell: dict[Any, list[dict[str, Any]]],
80        quota_of: Callable[[Any], int],
81    ) -> dict[Any, list[dict[str, Any]]]:
82        """Process cells in a fixed, seed-independent order (sorted by cell
83        key) so the shared used_* state is applied deterministically."""
84        result: dict[Any, list[dict[str, Any]]] = {}
85        for cell in sorted(candidates_by_cell, key=str):
86            result[cell] = self.select_cell(candidates_by_cell[cell], quota_of(cell))
87        return result
def rank(seed: int, identity: str) -> str:
13def rank(seed: int, identity: str) -> str:
14    return hashlib.sha256(f"{seed}:{identity}".encode()).hexdigest()
def row_identity(row: dict[str, typing.Any]) -> str:
17def row_identity(row: dict[str, Any]) -> str:
18    return "\x1f".join(
19        str(row.get(key, ""))
20        for key in ("scene_id", "kind", "phase", "frame", "type", "question", "sample_id")
21    )
def is_centered(row: dict[str, typing.Any]) -> bool:
24def is_centered(row: dict[str, Any]) -> bool:
25    """True if the answer bbox centroid is in the middle 50% of both axes.
26    In-context rows already carry a precomputed answer_is_centered column
27    (built with this exact rule at generation time); normal rows do not, so
28    it is computed here from answer_bbox/img_w/img_h."""
29    if row.get("answer_is_centered") is not None:
30        return bool(row["answer_is_centered"])
31    bbox = row.get("answer_bbox")
32    if bbox is None:
33        return False
34    x0, y0, x1, y1 = bbox
35    cx = (x0 + x1) / (2 * row["img_w"])
36    cy = (y0 + y1) / (2 * row["img_h"])
37    return 0.25 <= cx <= 0.75 and 0.25 <= cy <= 0.75

True if the answer bbox centroid is in the middle 50% of both axes. In-context rows already carry a precomputed answer_is_centered column (built with this exact rule at generation time); normal rows do not, so it is computed here from answer_bbox/img_w/img_h.

class DeterministicSelector:
40class DeterministicSelector:
41    """Greedy, seeded selection across many (cell -> quota) groups, sharing
42    one running state so "prefer distinct scenes/frames" and "no semantic
43    alias duplicate" hold across the whole output, not just within one cell.
44    """
45
46    def __init__(self, seed: int) -> None:
47        self.seed = seed
48        self.used_scene_frame: set[tuple[str, str]] = set()
49        self.used_semantic_keys: set[tuple] = set()
50        self.used_row_ids: set[str] = set()
51
52    def _rank_key(self, row: dict[str, Any]) -> tuple:
53        scene_frame = (str(row["scene_id"]), str(row["frame"]))
54        # Rows whose (scene, frame) is already used sort after rows that
55        # would introduce a new one -- "prefer distinct scenes/frames" as a
56        # soft, deterministic tiebreak, not a hard requirement.
57        reused = scene_frame in self.used_scene_frame
58        return (reused, rank(self.seed, row_identity(row)))
59
60    def select_cell(self, candidates: list[dict[str, Any]], quota: int) -> list[dict[str, Any]]:
61        ordered = sorted(candidates, key=self._rank_key)
62        picked: list[dict[str, Any]] = []
63        for row in ordered:
64            if len(picked) >= quota:
65                break
66            row_id = row_identity(row)
67            if row_id in self.used_row_ids:
68                continue
69            key = semantic_key(row)
70            if key in self.used_semantic_keys:
71                continue
72            picked.append(row)
73            self.used_row_ids.add(row_id)
74            self.used_semantic_keys.add(key)
75            self.used_scene_frame.add((str(row["scene_id"]), str(row["frame"])))
76        return picked
77
78    def select(
79        self,
80        candidates_by_cell: dict[Any, list[dict[str, Any]]],
81        quota_of: Callable[[Any], int],
82    ) -> dict[Any, list[dict[str, Any]]]:
83        """Process cells in a fixed, seed-independent order (sorted by cell
84        key) so the shared used_* state is applied deterministically."""
85        result: dict[Any, list[dict[str, Any]]] = {}
86        for cell in sorted(candidates_by_cell, key=str):
87            result[cell] = self.select_cell(candidates_by_cell[cell], quota_of(cell))
88        return result

Greedy, seeded selection across many (cell -> quota) groups, sharing one running state so "prefer distinct scenes/frames" and "no semantic alias duplicate" hold across the whole output, not just within one cell.

DeterministicSelector(seed: int)
46    def __init__(self, seed: int) -> None:
47        self.seed = seed
48        self.used_scene_frame: set[tuple[str, str]] = set()
49        self.used_semantic_keys: set[tuple] = set()
50        self.used_row_ids: set[str] = set()
seed
used_scene_frame: set[tuple[str, str]]
used_semantic_keys: set[tuple]
used_row_ids: set[str]
def select_cell( self, candidates: list[dict[str, typing.Any]], quota: int) -> list[dict[str, typing.Any]]:
60    def select_cell(self, candidates: list[dict[str, Any]], quota: int) -> list[dict[str, Any]]:
61        ordered = sorted(candidates, key=self._rank_key)
62        picked: list[dict[str, Any]] = []
63        for row in ordered:
64            if len(picked) >= quota:
65                break
66            row_id = row_identity(row)
67            if row_id in self.used_row_ids:
68                continue
69            key = semantic_key(row)
70            if key in self.used_semantic_keys:
71                continue
72            picked.append(row)
73            self.used_row_ids.add(row_id)
74            self.used_semantic_keys.add(key)
75            self.used_scene_frame.add((str(row["scene_id"]), str(row["frame"])))
76        return picked
def select( self, candidates_by_cell: dict[typing.Any, list[dict[str, typing.Any]]], quota_of: Callable[[Any], int]) -> dict[typing.Any, list[dict[str, typing.Any]]]:
78    def select(
79        self,
80        candidates_by_cell: dict[Any, list[dict[str, Any]]],
81        quota_of: Callable[[Any], int],
82    ) -> dict[Any, list[dict[str, Any]]]:
83        """Process cells in a fixed, seed-independent order (sorted by cell
84        key) so the shared used_* state is applied deterministically."""
85        result: dict[Any, list[dict[str, Any]]] = {}
86        for cell in sorted(candidates_by_cell, key=str):
87            result[cell] = self.select_cell(candidates_by_cell[cell], quota_of(cell))
88        return result

Process cells in a fixed, seed-independent order (sorted by cell key) so the shared used_* state is applied deterministically.