#!/usr/bin/env python3 """V10 bounds-prior runs of the RGB-only V5 C0 recipe; no writer or test cohort access.""" from __future__ import annotations import argparse import hashlib import json import time from pathlib import Path import numpy as np import torch from mcss.dynamic.types import hash_value from mcss.geometry import transform_cameras from mcss.mechanism_pilot.geometry_carrier_experiment import ( select_checkpoint, verify_data, verify_lock, ) from mcss.mechanism_pilot.rgb_prior_bounds_carrier import ( VARIANTS, build_state, make_carrier, parameter_hash, save_checkpoint, scene_data, train_records, training_loss, ) from mcss.mechanism_pilot.small_training import AccessLog, sha, write_json def train(root, variant, seed, device): root = Path(root).resolve() manifest, config = verify_lock(root) verify_data(root) torch.set_num_threads(config["num_threads"]) if variant not in config["variants"] or seed not in config["seeds"]: raise PermissionError("Unregistered model/seed") destination = root / "checkpoints" / f"{variant}_{seed}" destination.mkdir(exist_ok=False) access = AccessLog(destination / "access.jsonl") records = train_records(manifest, config, variant) scenes = [scene_data(variant)(r, manifest, root, device, access) for r in records] generator = torch.Generator(device="cpu").manual_seed(seed) order = torch.randperm(len(scenes), generator=generator).tolist() prior = json.loads((root / "train_depth_prior.json").read_text()) carrier = make_carrier(variant, seed, device, prior["near_m"], prior["far_m"]) carrier.train() initial_hash = hash_value(carrier.state_dict()) initial_parameters = parameter_hash(carrier) optimizer = torch.optim.Adam(carrier.parameters(), lr=config["learning_rate"], weight_decay=0) stream = hashlib.sha256() candidates = [] if str(device).startswith("cuda"): torch.cuda.reset_peak_memory_stats() start = time.perf_counter() with (destination / "training.jsonl").open("x", buffering=1) as log: for step in range(1, config["steps"] + 1): tick = time.perf_counter() visit = (step - 1) // len(scenes) index = order[(step - 1) % len(scenes)] scene = scenes[index] slot = visit % len(scene.record["roles"]["primary_query"]) optimizer.zero_grad(set_to_none=True) # Independently built states share only the trainable slow carrier parameters. states = { role: build_state( carrier, scene.context(role), scene.bounds[role], f"{seed}:{step}:{role}" ) for role in ("A", "B") } target = scene.target(slot) # offline targets only after state construction ray_count = int(np.prod(manifest["image_size"])) indices = torch.randint(ray_count, (config["batch_rays"],), generator=generator) stream.update(json.dumps([scene.record["scene_id"], slot]).encode()) stream.update(indices.numpy().astype("