"""Topology arm only: DecDPO on DistilGPT-2 across four 5-node graphs.""" import json, time, numpy as np, torch from transformers import AutoModelForCausalLM from dpo_real import DEV, MODEL import fed_real as FR RES = json.load(open("fed_real_results.json")) def main(): t0=time.time() clients,names,tok = FR.build_clients() base = AutoModelForCausalLM.from_pretrained(MODEL) ref = AutoModelForCausalLM.from_pretrained(MODEL).to(DEV).eval() for p in ref.parameters(): p.requires_grad_(False) pad=tok.pad_token_id; n=FR.N_CLIENTS tops={} ring=np.zeros((n,n),int) for i in range(n): ring[i,(i+1)%n]=ring[(i+1)%n,i]=1 tops["ring"]=ring star=np.zeros((n,n),int); star[0,1:]=star[1:,0]=1; tops["star"]=star path=np.zeros((n,n),int) for i in range(n-1): path[i,i+1]=path[i+1,i]=1 tops["path"]=path tops["complete"]=np.ones((n,n),int)-np.eye(n,dtype=int) rows=[] for nm,adj in tops.items(): W,rho=FR.metropolis(adj) m,cons=FR.dec_run(base,ref,clients,tok,W) l,a=FR.evaluate(m,ref,clients,pad) rows.append({"topology":nm,"rho":round(rho,4), "one_over_1_minus_rho2":(round(1/(1-rho**2),3) if rho<0.999999 else None), "consensus_error":cons,"loss":l,"acc":a}) print(" %-9s rho=%.4f 1/(1-rho^2)=%s cons=%.4e loss=%.5f (%.0fs)"%( nm,rho,rows[-1]["one_over_1_minus_rho2"],cons,l,time.time()-t0),flush=True) json.dump({**RES,"claim5_topology":{"rows":rows}},open("fed_real_results.json","w"),indent=1) ok=[r for r in rows if r["consensus_error"]>1e-7 and r["one_over_1_minus_rho2"]] out={"rows":rows,"n_in_fit":len(ok)} if len(ok)>=3: x=np.log([r["one_over_1_minus_rho2"] for r in ok]); y=np.log([r["consensus_error"] for r in ok]) sl,ic=np.polyfit(x,y,1) out.update({"loglog_slope":round(float(sl),4), "r2":round(float(1-np.var(y-(sl*x+ic))/np.var(y)),4), "spearman_positive":bool(np.corrcoef(x,y)[0,1]>0)}) RES["claim5_topology"]=out json.dump(RES,open("fed_real_results.json","w"),indent=1) print("DONE %.0fs"%(time.time()-t0),flush=True) if __name__=="__main__": main()