# ML Program 7 - Locally Weighted Regression (LWR)
import numpy as np
import matplotlib.pyplot as plt

def local_regression(query_point, X, Y, tau):
    query_point = np.array([1, query_point])
    X_bias = np.array([[1, x] for x in X])
    distances = np.sum((X_bias - query_point) ** 2, axis=1)
    weights = np.exp(-distances / (2 * tau ** 2))
    weighted_X_transpose = (X_bias.T * weights)
    theta = np.linalg.pinv(weighted_X_transpose @ X_bias) @ (weighted_X_transpose @ Y)
    return theta @ query_point

def draw(tau):
    predictions = [local_regression(x0, X, Y, tau) for x0 in domain]

    plt.figure(figsize=(6, 4))
    plt.scatter(X, Y, color='black', label='Data Points')
    plt.plot(domain, predictions, color='red', label=f'LWR Fit ({tau=})')
    plt.xlabel("X Values")
    plt.ylabel("Y Values")
    plt.title(f"Locally Weighted Regression ({tau=})")
    plt.legend()
    plt.show()

X = np.linspace(-3, 3, num=100)
domain = X
Y = np.log(np.abs((X ** 2) - 1) + 0.5)
draw(10)
draw(0.1)
draw(0.01)
draw(0.001)
