"""One actual discriminator update and one non-saturating generator update."""
import math,json
Z=[-1.,1.];REAL=1.5
def sigmoid(x):return 1/(1+math.exp(-x))
def sp(x):return max(x,0)+math.log1p(math.exp(-abs(x)))
def generated(g):return [g['a']*z+g['c'] for z in Z]
def dloss(d,g):
    return sp(-(d['w']*REAL+d['b']))+sum(sp(d['w']*x+d['b']) for x in generated(g))/len(Z)
def dgrad(d,g):
    sr=sigmoid(d['w']*REAL+d['b']);xs=generated(g);sf=[sigmoid(d['w']*x+d['b']) for x in xs]
    return {'w':(sr-1)*REAL+sum(s*x for s,x in zip(sf,xs))/len(xs),'b':sr-1+sum(sf)/len(sf)}
def gloss(g,d):return sum(sp(-(d['w']*x+d['b'])) for x in generated(g))/len(Z)
def ggrad(g,d):
    terms=[(sigmoid(d['w']*x+d['b'])-1)*d['w'] for x in generated(g)]
    return {'a':sum(t*z for t,z in zip(terms,Z))/len(Z),'c':sum(terms)/len(Z)}
def check(fn,p,g):
    errors={}
    for k in p:
        plus=p.copy();minus=p.copy();plus[k]+=1e-6;minus[k]-=1e-6
        errors[k]=abs((fn(plus)-fn(minus))/2e-6-g[k])
    assert max(errors.values())<1e-7
    return errors
def run():
    d={'w':.4,'b':-.1};g={'a':.5,'c':0.};initial={'D':d.copy(),'G':g.copy()}
    gd=dgrad(d,g);de=check(lambda p:dloss(p,g),d,gd)
    d2={k:d[k]-.1*gd[k] for k in d};assert g==initial['G']
    gg=ggrad(g,d2);ge=check(lambda p:gloss(p,d2),g,gg)
    g2={k:g[k]-.1*gg[k] for k in g};assert d2=={k:d[k]-.1*gd[k] for k in d}
    assert dloss(d2,g)<dloss(d,g) and gloss(g2,d2)<gloss(g,d2)
    return {'real_training_value':REAL,'fixed_noise':Z,'initial':initial,'D_step':{'G_fixed':g,'gradients':gd,'finite_difference_errors':de,'D_after':d2,'loss_before':dloss(d,g),'loss_after':dloss(d2,g)},'G_step':{'D_fixed':d2,'gradients':gg,'finite_difference_errors':ge,'G_after':g2,'loss_before':gloss(g,d2),'loss_after':gloss(g2,d2)},'generated_values_before':generated(g),'generated_values_after':generated(g2),'G_objective':'non-saturating -mean(log D(G(z)))','D_objective':'-log D(real) - mean(log(1-D(fake)))','image_generated':False,'TensorFlow_or_PyTorch_run':False}
if __name__=='__main__':print(json.dumps(run(),indent=2))
