"""Standalone synthetic teaching example. Run locally with Python, not in the HTML."""
import json
import numpy as np
import pandas as pd
from scipy.special import expit

def clean(value):
    if isinstance(value, dict): return {str(k):clean(v) for k,v in value.items()}
    if isinstance(value,(list,tuple,np.ndarray)): return [clean(v) for v in value]
    if isinstance(value,(np.integer,)): return int(value)
    if isinstance(value,(float,np.floating)): return float(value) if np.isfinite(value) else None
    if isinstance(value,(np.bool_,)): return bool(value)
    return value

def print_result(result):
    print(json.dumps(clean(result),ensure_ascii=False,allow_nan=False))


def mlp_loss_grad(X, y, theta):
    """2 inputs -> 2 tanh hidden units -> 1 output; flattened 9 parameters."""
    W1,b1,W2,b2 = theta[:4].reshape(2,2),theta[4:6],theta[6:8].reshape(2,1),theta[8]
    hidden = np.tanh(X@W1+b1)
    residual = hidden@W2+b2-y
    loss = float(np.sum(residual**2)/(2*len(X)))
    output_grad = residual/len(X)
    hidden_grad = (output_grad@W2.T)*(1-hidden**2)
    gradient = np.r_[(X.T@hidden_grad).ravel(),hidden_grad.sum(0),
                     (hidden.T@output_grad).ravel(),output_grad.sum()]
    return loss,gradient

if __name__ == "__main__":
    rng=np.random.default_rng(42)
    X=rng.normal(size=(32,2)); y=(np.sin(X[:,0])+X[:,1]*.3)[:,None]
    theta=rng.normal(0,.3,9); eps=1e-6
    loss,gradient=mlp_loss_grad(X,y,theta)
    numeric=np.array([(mlp_loss_grad(X,y,theta+eps*e)[0]-mlp_loss_grad(X,y,theta-eps*e)[0])/(2*eps)
        for e in np.eye(9)])
    np.testing.assert_allclose(gradient,numeric,atol=1e-8)
    max_error=abs(gradient-numeric).max(); initial=loss
    for _ in range(600):
        loss,gradient=mlp_loss_grad(X,y,theta)
        theta-=.05*gradient
    final,_=mlp_loss_grad(X,y,theta)
    assert final<initial
    print_result({"gradient_max_error":max_error,"initial_training_loss":initial,
        "final_training_loss":final,"scope":"training demonstration, not financial OOS evidence"})
