"""Authored mean-squared linear gradient: unequal microbatches, fixed-parameter accumulation, one exact SGD update."""
from fractions import Fraction as F
import json
DATA=[(F(1),F(0)),(F(2),F(0)),(F(3),F(0)),(F(4),F(0)),(F(5),F(10))]
def gradient(rows,w):return sum(((w*x-y)*x for x,y in rows),F(0))/len(rows)
def loss(rows,w):return sum(((w*x-y)**2 for x,y in rows),F(0))/(2*len(rows))
def demo():
    w=F(1);rate=F(1,10);batches=[DATA[:4],DATA[4:]]
    full=gradient(DATA,w);micro=[gradient(b,w) for b in batches]
    weighted=sum((F(len(b),len(DATA))*g for b,g in zip(batches,micro)),F(0));wrong=sum(micro,F(0))/len(micro)
    assert full==weighted==1 and micro==[F(15,2),F(-25)] and wrong==-F(35,4)
    new_full=w-rate*full;new_accum=w-rate*weighted;new_wrong=w-rate*wrong
    assert new_full==new_accum==F(9,10) and new_wrong==F(15,8)
    sequential=w;trace=[]
    for b in batches:
        old=sequential;g=gradient(b,old);sequential=old-rate*g;trace.append({'n':len(b),'w_before':str(old),'gradient_at_current_w':str(g),'w_after':str(sequential)})
    assert sequential==F(37,8)
    return {'authored_xy':[[str(x),str(y)] for x,y in DATA],'loss_definition':'mean((w*x-y)^2)/2','initial_w':str(w),'learning_rate':str(rate),'microbatch_sizes':[len(b) for b in batches],'per_row_gradients_at_same_w':[str((w*x-y)*x) for x,y in DATA],'microbatch_mean_gradients':[str(v) for v in micro],'sample_weighted_accumulated_gradient':str(weighted),'full_mean_gradient':str(full),'incorrect_unweighted_micro_mean':str(wrong),'one_update_full_or_accumulated_w':str(new_full),'incorrect_micro_mean_update_w':str(new_wrong),'separate_update_after_each_microbatch_w':str(sequential),'separate_updates_trace':trace,'loss_before':str(loss(DATA,w)),'loss_after_correct_one_update':str(loss(DATA,new_full)),'batch_coupled_layers_dropout_AMP_or_optimizer_state_not_simulated':True,'memory_speed_GPU_or_pytorch_run':False}
if __name__=='__main__':print(json.dumps(demo(),ensure_ascii=False,indent=2))
