Coverage for cafl4ds/filters/base.py: 100%
18 statements
« prev ^ index » next coverage.py v7.15.0, created at 2026-07-17 14:39 +0000
« prev ^ index » next coverage.py v7.15.0, created at 2026-07-17 14:39 +0000
1"""The selection-filter interface (the ``A`` factor / independent variable).
3The filter is the knob the whole study turns. The ``A`` factor has **two composable roles**,
4because the cheap Phase-0 knobs are not all the same kind of thing:
6* An **admission** :class:`Filter` *narrows* an incoming batch to a subset — B-floor
7 (:class:`~cafl4ds.filters.accept_all.AcceptAll`, accept everything), SemDeDup
8 (near-duplicate removal), the loss-gate (keep high-loss frames). Admission filters are pure
9 batch reducers, so they **compose freely as a list** (apply one after another).
10* A **replay** :class:`ReplayBuffer` decides what is *stored and replayed* — it may emit past
11 items, so it can *grow* the training batch, and it carries state across steps. Reservoir
12 sampling (:class:`~cafl4ds.filters.reservoir.ReservoirReplay`) is the Phase-0 instance. A
13 buffer is a single-choice role and always runs **last** (after admission).
15:class:`~cafl4ds.filters.composite.CompositeSelector` combines an admission list with an
16optional buffer behind the single :class:`Filter` interface the loop calls, which is what lets
17the plan's baselines be selected by config — B-floor, B1 (reservoir), dedup, loss-gate, and
18B1.5 (dedup ∘ reservoir). Later phases add novelty (F-a), coverage (F-b), and the
19health-steerable filter (F-c) behind these same interfaces.
21The :class:`FilterContext` deliberately carries the live model and step so that
22model-in-the-loop filters (the *fast edge*) and, later, health state (the *slow edge*) have
23what they need without changing the loop's call site.
24"""
26from __future__ import annotations
28from abc import ABC, abstractmethod
29from dataclasses import dataclass
31import torch
33from cafl4ds.data.streams import StreamBatch
34from cafl4ds.ssl.base import SSLMethod
37@dataclass
38class FilterContext:
39 """Everything a filter may consult when selecting from a batch."""
41 method: SSLMethod
42 """The live SSL method — its encoder scores informativeness against the *current* model
43 (the fast edge). B-floor ignores it."""
44 step: int
45 """The current global stream step."""
48class Filter(ABC):
49 """Selects which images from an incoming stream batch enter the SSL update."""
51 @abstractmethod
52 def select(self, batch: StreamBatch, ctx: FilterContext) -> torch.Tensor:
53 """Return the images accepted from ``batch`` for this update.
55 Args:
56 batch: The incoming label-free stream batch.
57 ctx: The live filter context (model, step).
59 Returns:
60 The accepted images ``[K, C, H, W]`` with ``0 <= K <= B`` (possibly the whole
61 batch, possibly empty).
62 """
65class ReplayBuffer(ABC):
66 """Stores incoming images and emits the batch the SSL update trains on (the replay role).
68 Unlike an admission :class:`Filter`, a buffer holds state across steps and may return
69 *more* items than it was given (the incoming batch mixed with replayed past items), so it
70 is a distinct role that runs after admission. Phase 0 has one instance,
71 :class:`~cafl4ds.filters.reservoir.ReservoirReplay`.
72 """
74 @abstractmethod
75 def observe(self, images: torch.Tensor, ctx: FilterContext) -> torch.Tensor:
76 """Ingest the admitted images and return the batch to train on this step.
78 Args:
79 images: The admitted images ``[K, C, H, W]`` (post-admission) for this step.
80 ctx: The live filter context (model, step).
82 Returns:
83 The training batch ``[M, C, H, W]`` — typically the incoming images plus a replay
84 sample drawn from the buffer's stored history (``M >= K``).
85 """