Source: src/mcss/mechanism_pilot/rgbd_viewcount_carrier.py · Plain text

src/mcss/mechanism_pilot/rgbd_viewcount_carrier.py

"""V9: training-time view count for the Core A RGB-D recipe (fixed 3 vs variable 3-7 views).

Every variant is the V8 carrier family at one grid (fixed by the pre-V8 plan) with the V7 C1
bounds rule, the frozen V5 C1 loss, TRAIN72 and 6000 steps from the same per-seed draw; only
the number of views in each training context differs. A context is the role's 3 frozen
context frames plus the first m of the scene's 4 evenly spaced free frames (the Core B stream
rule, frame ids only), in arrival order: m = 0 (C0), m ~ U{0..4} from a separate seeded
generator (C1), or m = 4 (C2). Bounds always come from the role's 3 context frames.
"""

from __future__ import annotations

import torch

from mcss.dynamic.config import CarrierConfig
from mcss.dynamic.types import OnlineObservation
from mcss.mechanism_pilot import rgbd_evidence_carrier as v5
from mcss.mechanism_pilot import rgbd_resolution_carrier as v8

EXPERIMENT = "EXP-3D-RGBD-VIEWCOUNT-V9"
GRID = 32
EXTRA_MAX = 4
EXTRA_SEED_OFFSET = 7919
VARIANT_SPECS = {
    "C0": {"label": "FIXED3", "extra": "none", "model": "C1", "train": "TRAIN72"},
    "C1": {"label": "VARIABLE3TO7", "extra": "uniform_0_4", "model": "C1", "train": "TRAIN72"},
    "C2": {"label": "FIXED7", "extra": "all_4", "model": "C1", "train": "TRAIN72"},
}
VARIANTS = tuple(VARIANT_SPECS)
PRIMARY_PAIR = ("C0", "C1")
build_state = v8.build_state
parameter_hash = v8.parameter_hash
save_checkpoint = v8.save_checkpoint
load_checkpoint = v8.load_checkpoint
train_records = v8.train_records

__all__ = [
    "EXPERIMENT",
    "EXTRA_MAX",
    "EXTRA_SEED_OFFSET",
    "GRID",
    "PRIMARY_PAIR",
    "VARIANTS",
    "VARIANT_SPECS",
    "build_state",
    "carrier_config",
    "extra_count",
    "load_checkpoint",
    "make_carrier",
    "parameter_hash",
    "save_checkpoint",
    "scene_data",
    "train_records",
    "training_loss",
    "with_extra_views",
]


def carrier_config(variant=None):
    del variant
    return CarrierConfig(grid_size=(GRID, GRID, GRID), token_count=GRID**3)


def make_carrier(variant, seed, device, near=None, far=None):
    """Identical per-seed draw for every variant (the V8 family at the planned grid)."""
    del near, far
    if variant not in VARIANT_SPECS:
        raise KeyError(variant)
    torch.manual_seed(seed)
    if torch.cuda.is_available():
        torch.cuda.manual_seed_all(seed)
    return v8.ResolutionRGBDCarrier(carrier_config()).to(device)


def extra_count(variant, generator):
    """Views added to one training step's contexts; C1 draws from its own generator."""
    kind = VARIANT_SPECS[variant]["extra"]
    if kind == "none":
        return 0
    if kind == "all_4":
        return EXTRA_MAX
    return int(torch.randint(0, EXTRA_MAX + 1, (1,), generator=generator))


def with_extra_views(context, stream, count):
    """The context followed by the first `count` stream views, relabeled in arrival order.

    count == 0 returns the context unchanged, so C0 runs the exact V7/V8 code path. The carrier
    reads frame ids only for alignment and order, never as values.
    """
    if count == 0:
        return context
    if not 0 < count <= len(stream):
        raise ValueError("Extra view count outside the available stream")
    observations = [*context, *list(stream)[:count]]
    depths = torch.cat((context.depths, stream.depths[:count]))
    relabeled = [
        OnlineObservation(o.scene_id, i, o.rgb, o.camera) for i, o in enumerate(observations)
    ]
    return v5.RGBDContext(relabeled, depths)


def scene_data(variant):
    del variant
    return v8.SCENE_DATA["CONTEXT_DEPTH"]


def training_loss(state, local_cameras, rgb, depth, indices, variant):
    """The frozen V2 C1 loss for the planned grid (surface band at the frozen 16-grid spacing)."""
    if variant not in VARIANT_SPECS:
        raise KeyError(variant)
    return v8.training_loss(state, local_cameras, rgb, depth, indices, "C0")