"""Cairn's compute layer for the browser build (Pyodide, no server, no gradio).
Why this exists instead of Gradio-lite: gradio-lite imports ``gradio`` *before*
it installs the page's requirements, and gradio 5.x currently cannot be resolved
against ``huggingface-hub`` 1.x inside Pyodide -- the capped builds fail to
install, the uncapped ones import and then die on a missing ``httpcore``. Since
the boot order is not ours to change there, we skip the framework and drive
Pyodide directly. The page ends up lighter too: no pandas, pydantic or orjson,
just numpy, scipy, pillow and the ``cairn`` wheel.
Every function here returns plain JSON-able data (HTML fragments and base64 PNG
data URIs) which ``index.html`` drops into the DOM. All of the science is
imported unchanged from the ``cairn`` package; nothing is reimplemented for the
browser.
Claim: R/E -- lets anyone check the two headline claims themselves, for free,
with no GPU, no install and no account.
"""
from __future__ import annotations
import base64
import io
from typing import Any, Dict, List, Tuple
import numpy as np
from PIL import Image
from cairn.runner import CONDITION_NAMES, RunConfig, run_condition
from cairn.world import (
make_departure_return_trajectory,
make_scene,
render,
schedule_edit,
usable_targets,
)
GAP = 6
BASELINES = {"A": "Vanilla", "B": "Context-window", "C": "Compressed-memory"}
# --------------------------------------------------------------------------
# rendering helpers
# --------------------------------------------------------------------------
def _png(arr: np.ndarray) -> str:
"""``(H, W, 3)`` float image -> base64 PNG data URI for an ```` tag."""
a = (np.clip(np.asarray(arr), 0.0, 1.0) * 255).astype(np.uint8)
buf = io.BytesIO()
Image.fromarray(a).save(buf, format="PNG", optimize=True)
return "data:image/png;base64," + base64.b64encode(buf.getvalue()).decode("ascii")
def _filmstrip(frames: np.ndarray, idx: List[int]) -> str:
"""Selected frames side by side, separated by pale gutters.
Pyodide has no ffmpeg, so there is no video. A strip is arguably the better
medium for this claim anyway: "before leaving" and "after returning" sit in
one glance instead of several seconds apart in a loop.
Claim: R -- the experiment, in one picture.
"""
idx = [i for i in idx if 0 <= i < len(frames)]
if not idx:
return _png(np.zeros((8, 8, 3)))
h = frames[0].shape[0]
gutter = np.full((h, GAP, 3), 0.93, dtype=np.float32)
panels: List[np.ndarray] = []
for k, i in enumerate(idx):
if k:
panels.append(gutter)
panels.append(frames[i])
return _png(np.concatenate(panels, axis=1))
def _windows(traj) -> List[int]:
return [
0,
max(0, traj.observe_frames[1] - 1),
(traj.departure_frame + traj.return_frame) // 2,
traj.return_frame + 1,
min(len(traj) - 1, traj.return_frame + 7),
]
PANEL_CAPTION = (
"opening shot · last frame before leaving · "
"looking away · just back · settled after return"
)
def _ledger_html(res) -> str:
rows = []
for e in res.ledger.entries(include_absent=True):
p = e.pose.position
state = "present" if e.present else 'REMOVED'
rows.append(
f"
| id | x | z | yaw | rgb | " "seen | conf | state |
|---|
Object #{int(requested)} is permanently hidden behind another object " f"in this room, so it cannot host a leave-and-return episode. Showing object " f"#{chosen} instead.
" ) # -------------------------------------------------------------------------- # public entry points (called from JavaScript) # -------------------------------------------------------------------------- def scene_preview(seed: int, n_objects: int) -> str: """Four views of the room, so you can see what you are about to test.""" from cairn.types import CameraPose scene = make_scene(int(n_objects), seed=int(seed)) cams = [ CameraPose(np.array([5.0, 1.55, 5.0]), a) for a in np.linspace(-np.pi, np.pi, 4, endpoint=False) ] frames = np.stack([render(scene.states(), c, scene.settings).rgb for c in cams]) return _filmstrip(frames, list(range(4))) def _verdict_html(res, label: str) -> str: m = res.metrics drawn = m["return_observed"] > 0.5 ok = m["return_success"] > 0.5 err = "not drawn at all" if not drawn else f"{m['return_self_trans']:.2f} m" badge = ( "consistent" if ok else "inconsistent" ) return ( f"Camera left object #{target} at frame {traj.departure_frame} and came back " f"at frame {traj.return_frame} — {traj.absence_frames} frames away. " f"The return viewpoint is deliberately not the departure viewpoint, so neither " f"method can win by replaying its last frame.
" f"Panels: {PANEL_CAPTION}
{note}" ) return { "header": header, "off_img": _filmstrip(off.frames, idx), "on_img": _filmstrip(on.frames, idx), "off_label": f"Cairn OFF — ({baseline}) {CONDITION_NAMES[baseline]}", "on_label": "Cairn ON — (D) explicit world ledger", "off_verdict": _verdict_html(off, f"Cairn OFF — ({baseline}) {BASELINES[baseline]}"), "on_verdict": _verdict_html(on, "Cairn ON — (D) explicit world ledger"), "ledger": _ledger_html(on), } def edit(seed: int, n_objects: int, absence: int, target: int, kind: str) -> Dict[str, Any]: """Issue an edit while the object is off screen, then score what came back. Claim: E -- the operation conditions A–C cannot express at all. """ seed, n_objects, absence = int(seed), int(n_objects), int(absence) scene = make_scene(n_objects, seed=seed) target, note = _resolve_target(scene, int(np.clip(target, 0, n_objects - 1)), seed) traj = make_departure_return_trajectory(scene, target, absence, seed=seed) ev = schedule_edit(traj, scene, kind, seed=seed) traj.edits = [ev] on = run_condition(scene, traj, _cfg("D", seed)) off = run_condition(scene, traj, _cfg("A", seed)) idx = _windows(traj) if kind == "move": d = float(np.linalg.norm(np.asarray(ev.payload["position"]) - scene.get(target).pose.position)) what = f"move object #{target} {d:.1f} m and turn it" elif kind == "remove": what = f"delete object #{target} from the world" else: v = np.asarray(ev.payload["value"], dtype=float) what = ( f"recolour object #{target} to " f"" f"RGB {np.round(v, 2).tolist()}" ) rows = [] for res, cond in ((off, "A"), (on, "D")): for s in res.edit_scores: expressible = ( "yes" if cond == "D" else "no — no addressable world state" ) if s.complied: v = "yes" elif s.ledger_correct: v = "written, not confirmable" else: v = "no" rows.append( f"{s.detail}Command: {what}
" f"Issued at frame {ev.frame}, while the object is off screen " f"(frames {traj.departure_frame}–{traj.return_frame}).
" f"Panels: {PANEL_CAPTION}
{note}" ) table = ( "| condition | can express it? | obeyed? | " "evidence |
|---|
Conditions A–C hold the world implicitly, in activations. There is no "
f'row named "object #{target}" to write to, so move/remove/'
f"set_attr are not merely hard for them — they are undefined.
{t.op}| # | frame | op | object | " "source | note |
|---|
Rewound to frame {int(rewind_to)}: undid {undone} of {v0} transactions. " f"The ledger is now exactly as it stood at that instant, object for object. No learned " f"memory offers this operation — its state is entangled across every object and every " f"timestep at once.
" ) return {"before": before, "log": log, "after": _ledger_html(res), "note": note}