"""Authored shared scalar and two learned heads; one simultaneous update per weighting."""
import json
def objective(params,weight):
    w,a,b=params;p1=a*w;p2=b*w;l1=.5*(p1-2)**2;l2=.5*p2*p2
    return weight*l1+(1-weight)*l2,(l1,l2),(p1,p2)
def gradient(params,weight):
    w,a,b=params;e1=a*w-2;e2=b*w
    return [weight*e1*a+(1-weight)*e2*b,weight*e1*w,(1-weight)*e2*w]
def demo():
    initial=[1.,1.,1.];out=[];checked=0
    for weight in [.5,.75]:
        before=objective(initial,weight);g=gradient(initial,weight)
        for i in range(3):
            lo=initial[:];hi=initial[:];lo[i]-=1e-6;hi[i]+=1e-6
            fd=(objective(hi,weight)[0]-objective(lo,weight)[0])/(2e-6)
            assert abs(fd-g[i])<1e-7;checked+=1
        after_params=[x-.1*dx for x,dx in zip(initial,g)];after=objective(after_params,weight)
        assert after[0]<before[0]
        out.append({'task1_loss_weight':weight,'task2_loss_weight':1-weight,'initial':initial[:],'gradient_w_a_b':g,'new_w_a_b':after_params,'before_weighted_loss':before[0],'after_weighted_loss':after[0],'before_task_losses':before[1],'after_task_losses':after[1],'new_predictions':after[2]})
    assert out[0]['gradient_w_a_b'][0]==0 and out[0]['after_task_losses'][0]<.5 and out[0]['after_task_losses'][1]<.5
    assert out[1]['after_task_losses'][0]<.5<out[1]['after_task_losses'][1]
    assert initial==[1.,1.,1.]
    return {'authored_input_x':1,'task1_target':2,'task2_target':0,'shared_representation':'h=w*x','heads':['prediction1=a*h','prediction2=b*h'],'learning_rate':.1,'task1_task2_unweighted_shared_gradients_at_start':[-1,1],'separate_experiments':out,'finite_difference_parameter_checks':checked,'real_tasks_or_holdout_evaluated':False,'Keras_executed':False}
if __name__=='__main__':print(json.dumps(demo(),ensure_ascii=False,indent=2))
