src/mcss/mechanism_pilot/geometry_carrier.py
"""Matched static carrier construction and offline surface supervision.
Carrier inputs are RGB observations and context-camera-derived bounds only. Loss
labels are accepted by a separate function after the shared state exists.
"""
from __future__ import annotations
import torch
from torch import nn
from mcss.dynamic.carrier import DynamicSceneCarrier
from mcss.dynamic.types import OnlineObservation
from mcss.measurements import FixedMeasurementRenderer
from mcss.mechanism_pilot.direct_capacity_contracts import ObservationBatch
from mcss.mechanism_pilot.direct_capacity_optimization import cache_rays, render_rays
from mcss.mechanism_pilot.geometric_supervision_optimization import (
geometry_losses,
ray_geometry,
surface_tau,
)
from mcss.training.grounded import build_grounded_state
class _Materializer(nn.Module):
def __init__(self, carrier):
super().__init__()
self.carrier = carrier
def forward(self, cache, fast):
return self.carrier.materialize(cache, fast)
def build_state(carrier, observations, bounds, episode_id):
"""Return (differentiable shared state, raw anchor pose); no labels/query arguments.
The caller supplies only the frozen context-camera bounds rule. Geometry buffers
are scoped to this call using functional_call, and do not enter learned parameters.
No writes or nonzero fast offsets are used.
"""
if not isinstance(carrier, DynamicSceneCarrier):
raise TypeError("Existing DynamicSceneCarrier architecture required")
config = carrier.config
if (
tuple(config.grid_size),
config.token_count,
config.feature_dim,
config.hidden_dim,
config.expansion_dim,
) != ((16, 16, 16), 128, 8, 8, 16):
raise ValueError("Frozen matched 16-grid carrier architecture required")
observations = tuple(observations)
if not observations or any(type(o) is not OnlineObservation for o in observations):
raise TypeError("Only RGB-camera OnlineObservation values may construct state")
bounds = torch.as_tensor(bounds, device=carrier._bounds.device, dtype=carrier._bounds.dtype)
if bounds.shape not in ((2, 3), (1, 2, 3)) or bounds.requires_grad:
raise ValueError("Frozen nonlearned bounds must have shape [2,3] or [1,2,3]")
bounds = bounds.reshape(1, 2, 3).detach().clone()
if not torch.isfinite(bounds).all() or not (bounds[:, 1] > bounds[:, 0]).all():
raise ValueError("Finite ordered bounds required")
fractions = (carrier._candidate_normalized_xyz + 1) * 0.5
points = bounds[:, 0] + fractions * (bounds[:, 1] - bounds[:, 0])
wrapped = _Materializer(carrier)
replacements = {"carrier._bounds": bounds, "carrier._candidate_points": points}
class ContextOnlyAdapter:
encode = carrier.encode
initial_fast = carrier.initial_fast
@staticmethod
def materialize(cache, fast):
return torch.func.functional_call(wrapped, replacements, (cache, fast), strict=False)
return build_grounded_state(ContextOnlyAdapter(), observations, episode_id)
def training_loss(state, local_cameras, rgb, depth, indices, variant):
"""Offline TRAIN labels only; caller enforces scene-role provenance.
C0 retains the old carrier RGB Charbonnier (epsilon .001) plus masked depth
AbsRel. Its explicit regularization was zero. C1 adds only .1 surface loss;
optional C2 also adds .1 free-space. Index sampling is externally frozen.
"""
if variant not in ("C0", "C1", "C2"):
raise ValueError("Only preregistered C0/C1/C2 loss variants allowed")
if tuple(state.spatial_shape) != (16, 16, 16):
raise ValueError("Only 16-grid state allowed")
if len(local_cameras.leading_shape) != 2 or local_cameras.leading_shape[0] != 1:
raise ValueError("Single-scene cameras must have shape [1,V]")
views = local_cameras.leading_shape[1]
height, width = local_cameras.image_size
if rgb.shape != (1, views, 3, height, width) or depth.shape != (1, views, 1, height, width):
raise ValueError("Exact camera-aligned RGB/depth target shapes required")
if not torch.isfinite(rgb).all() or (rgb < 0).any() or (rgb > 1).any():
raise ValueError("Finite RGB targets in [0,1] required")
if indices.ndim != 1 or indices.numel() == 0 or indices.dtype != torch.long:
raise ValueError("Nonempty one-dimensional int64 sampled ray indices required")
if indices.min() < 0 or indices.max() >= views * height * width:
raise ValueError("Sampled ray index outside TRAIN labels")
valid = torch.isfinite(depth) & (depth > 0)
clean_depth = torch.where(valid, depth, torch.zeros_like(depth))
batch = ObservationBatch(
"offline_train",
tuple(range(views)),
rgb,
local_cameras,
clean_depth,
"OFFLINE_TRAIN_LABELS_NOT_CARRIER_INPUT",
)
cache = cache_rays(batch, state.bounds)
renderer = FixedMeasurementRenderer(n_samples=64, ray_chunk_size=2048).to(state.color.device)
pred = render_rays(renderer, state, cache, indices)
rgb_loss = torch.sqrt((pred["rgb"] - cache["rgb"][:, indices]).square() + 1e-6).mean()
target = cache["depth"][:, indices]
valid = target > 0
relative = (pred["depth"] - target).abs() / target.abs().clamp_min(1e-3)
depth_loss = (relative * valid).sum() / valid.sum().clamp_min(1)
base = rgb_loss + depth_loss
with torch.set_grad_enabled(torch.is_grad_enabled() and variant != "C0"):
free, surface, counts = geometry_losses(
*ray_geometry(renderer, state, cache, indices), target, surface_tau(state.bounds)
)
loss = base
if variant in ("C1", "C2"):
loss = loss + 0.1 * surface
if variant == "C2":
loss = loss + 0.1 * free
terms = {
"loss_rgb": rgb_loss,
"loss_depth": depth_loss,
"regularization": base.new_zeros(()),
"base_loss": base,
"loss_free": free,
"loss_surface": surface,
"loss": loss,
"opacity": pred["visibility"].mean(),
**counts,
}
return loss, terms