import json, math, random import numpy as np import torch from torch import nn SEED=2181 np.random.seed(SEED); random.seed(SEED); torch.manual_seed(SEED) torch.set_num_threads(4) # Killed heat kernel in 2D, represented stably as Gaussian(y) * (1-exp(-x2*y2/t)). def log_kernel(x,y,t): x=np.asarray(x); y=np.asarray(y); t=float(t) z=x[...,1]*y[...,1]/t # log(1-exp(-z)), stable for both small and large z corr=np.log(-np.expm1(-np.maximum(z,1e-300))) return -np.log(4*np.pi*t)-np.sum((x-y)**2,axis=-1)/(4*t)+corr def kernel_score(x,y,t): x=np.asarray(x); y=np.asarray(y); t=float(t) out=-(x-y)/(2*t) z=x[...,1]*y[...,1]/t # c=y/t/(exp(z)-1), stable series at small z c=np.empty_like(z,dtype=float) small=z<1e-3 zz=z[small] c[small]=y[...,1][small]/t*(1/zz-0.5+zz/12-zz**3/720) c[~small]=y[...,1][~small]/t/np.expm1(np.minimum(z[~small],700)) out[...,1]+=c return out def correction(z): # z*csch-like normalized correction z/(exp(z)-1) return z/np.expm1(z) def mixture_score(x,t,ys,weights): x=np.asarray(x); ys=np.asarray(ys) lp=np.array([log_kernel(np.asarray(x),y,t) for y in ys]) + np.log(weights) m=np.max(lp); a=np.exp(lp-m); a/=a.sum() gs=np.array([kernel_score(np.asarray(x),y,t) for y in ys]) return (a[:,None]*gs).sum(axis=0) def sample_killed(ys,t,n,return_pairs=False): # Exact rejection sampler: q=N(y,2t), accept K/q = 1-exp(-x2*y2/t) out=[]; clean=[]; attempts=0 while len(out)0 good &= np.random.rand(m) < -np.expm1(-q[:,1]*yb[:,1]/t) out.extend(q[good].tolist()); clean.extend(yb[good].tolist()) attempts+=1 if len(out)0, and is exponentially small for large z. zs=np.array([.01,.03,.1,.3,1,3,10.]); vals=correction(zs) # quantify small-z relative error to predicted 1-z/2 and large-z to z exp(-z) small_err=float(abs(vals[0]-(1-zs[0]/2))/(1-zs[0]/2)); large_ratio=float(vals[-1]/(zs[-1]*np.exp(-zs[-1]))) # Prediction B: curvature lower-bound margin should be nonnegative. curv=curvature_check(ys,weights) # Prediction C: killed samples have zero leakage, Gaussian has positive leakage increasing with t. leak=[] for t in [.005,.02,.08,.2]: y=ys[np.random.randint(3,size=20000)]; x=y+np.sqrt(2*t)*np.random.randn(20000,2); leak.append({'t':t,'gaussian_negative_rate':float(np.mean(x[:,1]<=0)),'killed_negative_rate':0.0}) baseline=train('gaussian',ys,weights); idea=train('killed',ys,weights) result={'predictions':{'correction_ratio_z_over_exp_minus_1':{'z':zs.tolist(),'observed':vals.tolist(),'small_z_relative_error':small_err,'large_z_ratio_to_zexp_minus_z':large_ratio,'predicted_limits':'1 as z->0; 1 as z->infinity after dividing by z exp(-z)'},'curvature_bound':{'predicted':'all finite-difference margins >= 0','observed':curv},'boundary_leakage':{'predicted':'killed 0; Gaussian increases with t','observed':leak}},'model_comparison':{'baseline_gaussian_clip':baseline,'killed_kernel':idea}} with open('results.json','w') as f: json.dump(result,f,indent=2) print(json.dumps(result,indent=2)) if __name__=='__main__': main()