import numpy as np

# ---- binary cross-entropy on a toy prediction ----
y = np.array([1, 0, 1, 1])
Q = np.array([0.9, 0.2, 0.6, 0.4])   # model's predicted P(class=1)

bce = -np.mean(y * np.log(Q) + (1 - y) * np.log(1 - Q))
print("Binary cross-entropy loss:", round(bce, 4))

# ---- manual KL divergence on a small discrete distribution ----
# P = true distribution, Q = model distribution, over 4 outcomes
P = np.array([0.10, 0.40, 0.35, 0.15])
Qd = np.array([0.20, 0.30, 0.25, 0.25])
assert np.isclose(P.sum(), 1) and np.isclose(Qd.sum(), 1)

def entropy(p):
    return -np.sum(p * np.log(p))

def cross_entropy(p, q):
    return -np.sum(p * np.log(q))

def kl_divergence(p, q):
    return np.sum(p * np.log(p / q))

H_P   = entropy(P)
H_PQ  = cross_entropy(P, Qd)
D_KL  = kl_divergence(P, Qd)

print(f"\nH(P)          = {H_P:.4f}")
print(f"H(P,Q)        = {H_PQ:.4f}")
print(f"D_KL(P||Q)    = {D_KL:.4f}")
print(f"H(P) + D_KL   = {H_P + D_KL:.4f}  (should equal H(P,Q) above)")
