"""CIS 6270 Lecture 6. Small, reproducible flow-map teaching experiments. Run: python numerical_examples.py --output outputs/numerical Dependencies: Python 3.11+, numpy, torch. The toy experiments validate the lecture mathematics. They do not reproduce the large-scale training or benchmark claims of the cited papers. """ import argparse, json, math, pathlib import numpy as np import torch from torch import nn from torch.nn import functional as F def flow(model, x, s, t): """Residual map with an exactly identity diagonal.""" return x + (t-s)*model(torch.cat([x,s,t],dim=-1)) def train_scalar(output, iterations=6000): """Self-distill the flow of dx/dt=x, using diagonal and composition only.""" torch.manual_seed(6270) torch.set_num_threads(2) model=nn.Sequential(nn.Linear(3,64),nn.SiLU(),nn.Linear(64,64),nn.SiLU(),nn.Linear(64,1)) opt=torch.optim.Adam(model.parameters(),lr=1e-3) history=[] for step in range(iterations): # Broad state coverage includes states reached by the split map. x=6*torch.rand(256,1)-3 times=torch.rand(256,2).sort(dim=-1).values s,t=times[:,:1],times[:,1:] u=s+(t-s)*torch.rand_like(s) diagonal=model(torch.cat([x,s,s],dim=-1)) diag_loss=(diagonal-x).square().mean() with torch.no_grad(): target=flow(model,flow(model,x,s,u),u,t) prediction=flow(model,x,s,t) # Normalize the interval residual to prevent tiny intervals dominating # the count of nearly zero-error examples. Keep a finite floor. cons_loss=((prediction-target)/(t-s).clamp_min(0.1)).square().mean() loss=diag_loss + (0 if step<500 else 1)*cons_loss opt.zero_grad();loss.backward();opt.step() if step%500==0:history.append({'step':step,'diagonal':diag_loss.item(),'composition':cons_loss.item()}) with torch.no_grad(): x=torch.linspace(-1.5,1.5,501)[:,None] s=torch.zeros_like(x);t=torch.ones_like(x);u=0.5*t pred=flow(model,x,s,t);truth=x*math.e split=flow(model,flow(model,x,s,u),u,t) rmse=(pred-truth).square().mean().sqrt().item() composition_rmse=(pred-split).square().mean().sqrt().item() np.savez(output/'scalar_predictions.npz',x=x.numpy().ravel(),pred=pred.numpy().ravel(),truth=truth.numpy().ravel()) torch.save(model.state_dict(),output/'scalar_map_weights.pt') return {'endpoint_rmse':rmse,'composition_rmse':composition_rmse,'iterations':iterations,'history':history} def categorical_map(net,x,s,t): psi=net(x,s,t).softmax(dim=-1) h=(t-s)/(1-s) return (1-h)*x+h*psi,psi def check_categorical(): x=torch.tensor([-.2,.6,1.1],dtype=torch.float64) psi=torch.tensor([.1,.7,.2],dtype=torch.float64) y=(1/3)*x+(2/3)*psi assert torch.allclose(y,torch.tensor([0,2/3,.5],dtype=torch.float64)) target=(1/3)*torch.tensor([.8,.2])+(2/3)*torch.tensor([.2,.8]) assert torch.allclose(target,torch.tensor([.4,.6])) logits=torch.tensor([.2,-.1],requires_grad=True) loss=F.kl_div(logits.log_softmax(-1),target,reduction='sum') loss.backward() assert torch.allclose(logits.grad,logits.softmax(-1)-target,atol=1e-7) return {'mapped_state':y.tolist(),'state_sum':y.sum().item(),'target':target.tolist()} def check_meanflow_jvp(): # Exact backward average for dz/dt=z. Stay off diagonal for this check. def exact_average(z,r,t): h=t-r return z*(-torch.expm1(-h))/h z=torch.tensor([[1.7]],dtype=torch.float64) r=torch.tensor([[.2]],dtype=torch.float64) t=torch.tensor([[.8]],dtype=torch.float64) value,derivative=torch.func.jvp(exact_average,(z,r,t),(z,torch.zeros_like(r),torch.ones_like(t))) target=z-(t-r)*derivative assert torch.allclose(value,target,atol=1e-10) return {'average':value.item(),'identity_residual':(value-target).abs().item()} def posterior_value_demo(): # Prior Z~N(0,1), observation x=beta*Z+alpha*eps. # Reward r(Z)=c*Z gives an analytic log moment-generating function. torch.manual_seed(13) alpha,beta,c=.7,.6,.4 x=torch.tensor(.3,dtype=torch.float64,requires_grad=True) gain=beta/(alpha**2+beta**2) variance=alpha**2/(alpha**2+beta**2) eps=torch.randn(100000,dtype=torch.float64) z=gain*x+math.sqrt(variance)*eps logw=c*z estimate=torch.logsumexp(logw,0)-math.log(len(eps)) gradient=torch.autograd.grad(estimate,x)[0] exact=c*gain*x.detach()+.5*c*c*variance exact_gradient=c*gain assert abs(gradient.item()-exact_gradient)<1e-10 assert abs(estimate.item()-exact.item())<.006 return {'estimated_value':estimate.item(),'exact_value':exact.item(),'gradient':gradient.item(),'exact_gradient':exact_gradient} def sample_gap_counts(means,remaining_budget): """Paper-style bounded proposals followed by left-to-right budget capping. These proposals match the per-gap means before joint truncation. They are not asserted to identify the full conditional count law from means alone. """ counts=[];remaining=int(remaining_budget) for mean in means: if remaining_budget==0:count=0 else: prob=float(torch.as_tensor(mean).clamp(0,remaining_budget))/remaining_budget count=int(torch.distributions.Binomial(remaining_budget,probs=prob).sample()) count=min(count,remaining);counts.append(count);remaining-=count return torch.tensor(counts,dtype=torch.long) def insert_by_gap(state,old_birth_times,counts,new_noise,birth_time): """Insert ordered noise rows into the n+1 gaps and preserve clock alignment.""" assert len(counts)==len(state)+1 pieces=[];clocks=[];offset=0 for gap,count in enumerate(counts.tolist()): if count: pieces.append(new_noise[offset:offset+count]);offset+=count clocks.append(torch.full((count,),float(birth_time))) if gap