File size: 10,271 Bytes
9d6c005 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | """Ray-matched causal persistence benchmark; no ground-truth policy access."""
from pathlib import Path
import argparse,csv,json,time,platform,sys
ROOT=Path(__file__).resolve().parents[1]
sys.path.insert(0,str(ROOT))
import numpy as np
from aureole import WorldMemory,freeze,draw,correct,exact_mse
from aureole.core import proposal_from_bound
from aureole.renderer import Scene,VisibilityPrior,receiver_grid,light_grid,unoccluded,physical_table
def cluster_interval(values,seed=992):
"""95% percentile interval over independent scenes, not individual pixels."""
x=np.asarray(values,float)
rng=np.random.default_rng(seed)
means=x[rng.integers(0,len(x),(10000,len(x)))].mean(1)
return [float(v) for v in np.percentile(means,[2.5,97.5])]
def run(followup=False,output="reproduced_results"):
config=json.loads((ROOT/("experiments_followup.json" if followup else "experiments.json")).read_text())
prefix="followup" if followup else "rendering"
points=receiver_grid(*config["receiver_grid"]);lights=light_grid(config["emitter_grid_side"])
height,width=config["receiver_grid"]; vh,vw=config["viewport"]
methods=config["methods"];prior=VisibilityPrior(ROOT/"models/visibility_prior.npz")
phase_frames=[(phase,i) for phase,n in config["phases"].items() for i in range(n)]
rows=[];prep=[];snapshots={};start=time.perf_counter();n=config["rays_per_receiver_per_frame"]
for scene_id in config["scene_ids"]:
scene=Scene.create(scene_id);changed=scene.changed()
tic=time.perf_counter();features=scene.features(points[:,None,:],lights[None,:,:]);p_all=prior(features)
prior_seconds=time.perf_counter()-tic
bound0=unoccluded(points,lights);bound1=unoccluded(points,lights,True,0.7)
# Privileged reference is used ONLY after the physical online estimate.
truth0=physical_table(scene,points,lights,bound0)
truth1=physical_table(scene,points,lights,bound1)
truth2=physical_table(changed,points,lights,bound1)
prep.append({"scene":scene_id,"prior_all_receivers_seconds":prior_seconds,"prior_receiver_emitter_pairs":len(points)*len(lights)})
for seed in config["replicate_seeds"]:
memory={method:WorldMemory(len(points),len(lights),f"scene-{scene_id}") for method in methods}
rngs={method:np.random.default_rng(seed+scene_id*1000) for method in methods}
for frame,(phase,phase_frame) in enumerate(phase_frames):
if phase=="revisit" and phase_frame==0:
for m in memory.values():m.advance(config["unseen_ticks_before_revisit"])
memory["screen_cv"].retain_only(np.array([],dtype=int))
x0=2+(phase_frame%4);y0=8
ids=(np.arange(y0,y0+vh)[:,None]*width+np.arange(x0,x0+vw)[None,:]).ravel()
b=(bound1 if phase in ("relight","hidden_change") else bound0)[ids]
current_scene=changed if phase=="hidden_change" else scene
offline_truth=(truth2 if phase=="hidden_change" else truth1 if phase=="relight" else truth0)[ids]
target=offline_truth.sum(1)
for method in methods:
tic=time.perf_counter();m=memory[method]
if method=="screen_cv":m.retain_only(ids)
base_prior=np.full((len(ids),len(lights)),0.5) if method=="constant_world_cv" else p_all[ids]
if method=="raw_importance":v=np.zeros_like(base_prior)
elif method=="neural_cv":v=base_prior
else:v=m.predict(ids,base_prior)
q=proposal_from_bound(b,v,m.trusted(ids),active=(method in ("world_active_cv","world_guarded_cv")))
snapshot=freeze(b*v[...,None],q)
j=draw(snapshot,n,rngs[method])
observed_visibility=current_scene.visibility(points[ids,None,:],lights[j])
physical=b[np.arange(len(ids))[:,None],j]*observed_visibility[...,None]
result=correct(snapshot,j,physical)
if method=="world_plugin":result=snapshot.integral.copy()
conflicts=0
if method not in ("raw_importance","neural_cv"):
conflicts=m.commit(ids,j,observed_visibility,revise_on_conflict=(method=="world_guarded_cv"))
m.advance()
runtime=time.perf_counter()-tic
# Oracle enumeration is outside the online policy and timing.
conditional=(np.mean((snapshot.integral-target)**2,axis=1) if method=="world_plugin"
else exact_mse(offline_truth,snapshot,n))
rows.append({"scene":scene_id,"seed":seed,"phase":phase,"phase_frame":phase_frame,
"frame":frame,"method":method,"mse":float(np.mean((result-target)**2)),
"conditional_expected_mse":float(conditional.mean()),
"mean_error":float(np.mean(result-target)),"negative_channel_fraction":float(np.mean(result<0)),
"rays":len(ids)*n,"runtime_seconds":runtime,"memory_bytes":0 if method in ("raw_importance","neural_cv") else m.nbytes,
"observed_conflicting_ray_entries":conflicts,"epoch_after":m.epoch,
"known_fraction_after":float(np.isfinite(m.values[ids]).mean())})
if scene_id==config["scene_ids"][0] and seed==config["replicate_seeds"][0] and phase_frame==0 and phase in ("revisit","hidden_change"):
snapshots[f"{phase}_{method}"]=result.reshape(vh,vw,3)
snapshots[f"{phase}_reference"]=target.reshape(vh,vw,3)
print(f"completed scene {scene_id}; {len(rows)} frame-method observations",flush=True)
out=ROOT/output;out.mkdir(parents=True,exist_ok=True)
with (out/f"{prefix}_raw.csv").open("w",newline="") as handle:
writer=csv.DictWriter(handle,fieldnames=list(rows[0]));writer.writeheader();writer.writerows(rows)
summary=[]
for phase in config["phases"]:
for method in methods:
subset=[r for r in rows if r["phase"]==phase and r["method"]==method]
scene_values=[np.mean([r["conditional_expected_mse"] for r in subset if r["scene"]==scene_id]) for scene_id in config["scene_ids"]]
summary.append({"phase":phase,"method":method,"expected_mse":float(np.mean(scene_values)),
"expected_mse_scene_bootstrap_95":cluster_interval(scene_values),
"observed_mse":float(np.mean([r["mse"] for r in subset])),
"negative_channel_fraction":float(np.mean([r["negative_channel_fraction"] for r in subset])),
"cpu_median_ms":float(np.median([r["runtime_seconds"] for r in subset])*1000),
"cpu_p99_ms":float(np.quantile([r["runtime_seconds"] for r in subset],.99)*1000)})
comparisons=[]
for phase in config["phases"]:
pairs=([("world_guarded_cv","world_active_cv"),("world_guarded_cv","world_cv"),("world_guarded_cv","raw_importance"),("world_guarded_cv","screen_cv")]
if followup else [("world_cv","screen_cv"),("world_cv","raw_importance"),("world_active_cv","world_cv"),("world_cv","constant_world_cv"),("world_cv","world_plugin")])
for contender,baseline in pairs:
a=[];b=[]
for sid in config["scene_ids"]:
a.append(np.mean([r["conditional_expected_mse"] for r in rows if r["scene"]==sid and r["phase"]==phase and r["method"]==contender]))
b.append(np.mean([r["conditional_expected_mse"] for r in rows if r["scene"]==sid and r["phase"]==phase and r["method"]==baseline]))
a=np.array(a);b=np.array(b)
rng=np.random.default_rng(299);draws=rng.integers(0,len(a),(10000,len(a)))
ratios=1-a[draws].mean(1)/np.maximum(b[draws].mean(1),1e-30)
comparisons.append({"phase":phase,"contender":contender,"baseline":baseline,
"relative_expected_mse_reduction":float(1-a.mean()/max(b.mean(),1e-30)),
"scene_bootstrap_95":[float(v) for v in np.percentile(ratios,[2.5,97.5])]})
report={"protocol":config,"elapsed_seconds":time.perf_counter()-start,"environment":{"python":platform.python_version(),"numpy":np.__version__,"device":"cpu"},
"record_count":len(rows),"total_physical_ray_calls":int(sum(r["rays"] for r in rows)),
"per_persistent_method_memory_bytes":len(points)*len(lights)*8,
"prior_preparation":prep,"summary":summary,"comparisons":comparisons,
"limitations":["Only a direct-light floor/three-sphere scene family; no game engine or full path tracing.",
"Ray counts match. Compute, bandwidth, and VRAM do not match; report is not an equal-frame-time comparison.",
"Clock gap has no observations; it is not 500 fully rendered frames.",
"CPU timings exclude shared feature/prior preparation and privileged reference enumeration; not full-renderer frame times.",
"Hidden changes deliberately omit invalidation and use the stale prior; controls remain fallible.",
"Negative linear-radiance estimates are retained for statistical metrics. Display clipping introduces bias.",
"Small scene-cluster intervals characterize this generator only; no cross-game generalization claim."]}
(out/f"{prefix}_report.json").write_text(json.dumps(report,indent=2)+"\n")
np.savez_compressed(out/f"{prefix}_example_frames.npz",**snapshots)
print(json.dumps({"records":len(rows),"elapsed_seconds":report["elapsed_seconds"],"comparisons":comparisons},indent=2))
if __name__=="__main__":
parser=argparse.ArgumentParser();parser.add_argument("--followup",action="store_true")
parser.add_argument("--output",default="reproduced_results",help="Keep new runs separate from the recorded release evidence")
args=parser.parse_args();run(args.followup,args.output)
|