twanghcmut's picture
download
raw
5.3 kB
#!/usr/bin/env python
"""Walk ONE query through the whole t=0 retrieval pipeline, printing what each stage produces.
Reading graph/run/retrieve.py top to bottom tells you what the code does; running this tells you
what the numbers look like while it does it. Every line below corresponds to one stage of the chain:
q_hist --cleanliness--> pooling weights --observe--> per-node scores --retrieve--> target
Usage:
OMP_NUM_THREADS=4 PYTHONPATH=src python scripts/trace_query.py [suite] [perturbation_rad]
The perturbation is how far off the demonstration manifold to place the query, in radians.
It prints the SAME query under both readouts, because the difference is the thing most likely to
confuse a reader of retrieve.py:
entry_band > 0 (the deployed t=0 recipe) -- observe() masks the pool to
task lane AND phase <= entry_band AND reachable, and the aggregation over what
survives is UNIFORM. So node == -1 and q_star is the barycentre of that task's
start-pose cloud. That is by design, not a failure: at t=0 there is no motion
history, so the mask decides the answer and the head's weighting within it does not.
entry_band = 0 -- no band mask; the head's own posterior decides, and the target lands wherever
the query most resembles a demo frame, which mid-episode is NOT the start.
node == -1 in both cases: both registered readout arms (euc_raw, basin) synthesise a config
rather than returning a graph node, so -1 is the normal answer, not an error code. abstain is
likewise computed either way, from the observation model alone; the entry path does not act on it.
"""
from __future__ import annotations
import sys
from pathlib import Path
import numpy as np
REPO_ROOT = Path(__file__).resolve().parents[1]
if str(REPO_ROOT / "src") not in sys.path:
sys.path.insert(0, str(REPO_ROOT / "src"))
from onf.config import GraphConfig, default_paths # noqa: E402
from onf.graph.core.geometry import finite_diff_vel # noqa: E402
from onf.graph.run.retrieve import GraphRetriever # noqa: E402
def trace(suite: str = "long", perturb: float = 0.05, node_idx: int = 100) -> None:
paths = default_paths()
cfg = GraphConfig(hist=8, entry_band=0.05, device="cpu")
r = GraphRetriever.load(paths.graph(suite), cfg=cfg, field_dir=paths.fwm(suite), device="cpu")
n = r.nodes
print(f"GRAPH {suite}: V={len(n)} nodes E={len(r.edges.src)} edges "
f"demos={n.n_demos} tasks={n.n_tasks} dim={n.dim}")
print(f" field: {type(r.field).__name__ if r.field else 'None (uniform weights)'}")
q0 = n.q[node_idx].astype(np.float64) + perturb
q_hist = np.repeat(q0[None, :], cfg.hist, axis=0)
w = r.scorer.weights(q_hist)
print(f"\n1 QUERY q_hist {q_hist.shape} {q_hist.dtype} -- at t=0 the window is ONE pose repeated,")
print(f" so finite_diff_vel is all-zero: {np.allclose(finite_diff_vel(q_hist), 0)}")
print(f" cleanliness w {w.shape} in [{w.min():.4f}, {w.max():.4f}] -- flat here BY "
f"CONSTRUCTION (identical rows)")
obs = r.observe(q_hist)
finite = int(np.isfinite(obs.logits64.numpy()).sum())
print(f"\n2 OBSERVE one score per node: logits64 {tuple(obs.logits64.shape)}")
print(f" {finite}/{len(n)} survive the task/owner/entry-band masks")
print(f" abstain_logit {float(obs.abstain_logit):+.3f} vs best node {float(obs.node_max):+.3f}"
f" -> {'ABSTAIN' if float(obs.abstain_logit) > float(obs.node_max) else 'retrieve'}")
res = r.retrieve(q_hist)
print(f"\n3 RESULT (entry_band={cfg.entry_band}, the DEPLOYED t=0 recipe)")
print(f" node={res.node} <- -1 means 'no single node': q_star is the uniform barycentre")
print(f" of the masked pool. This is the design, not a fallback.")
print(f" WHEN depth (phase in [0,1]) = {res.depth:.4f}")
print(f" WHERE q_star {res.q_star.shape}, q_seg {res.q_seg.shape} -- a SEGMENT, not a point,")
print(f" so the policy gets its velocity back too")
print(f" conf={res.conf:.4f} abstain={res.abstain} (computed, but the entry path ignores it)")
print(f" |q_query - q_star| = {np.linalg.norm(q0 - res.q_star):.4f} rad"
f" <- what controller.py's PD loop closes")
cfg2 = GraphConfig(hist=8, entry_band=0.0, device="cpu")
r2 = GraphRetriever.load(paths.graph(suite), cfg=cfg2, field_dir=paths.fwm(suite), device="cpu")
res2 = r2.retrieve(q_hist)
print(f"\n4 SAME QUERY, entry_band=0 (no band mask -- the head's own posterior decides)")
print(f" node={res2.node} owner(demo)={res2.owner} task_id={res2.task_id}")
print(f" depth={res2.depth:.4f} conf={res2.conf:.4f} abstain={res2.abstain}")
print(f" |q_query - q_star| = {np.linalg.norm(q0 - res2.q_star):.4f} rad")
print(f"\n Same query, two different targets. Which one ships is a CONFIG choice"
f"\n (GR_ENTRY_BAND), not a code path someone picked at random.")
if __name__ == "__main__":
trace(sys.argv[1] if len(sys.argv) > 1 else "long",
float(sys.argv[2]) if len(sys.argv) > 2 else 0.05)

Xet Storage Details

Size:
5.3 kB
·
Xet hash:
747837c8a45f172d4180d2005391ae17b27b22ba73c8948d95aeab56b0149f93

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.