Implement Locally Linear Embedding (LLE), a nonlinear dimensionality reduction technique. Given a dataset of high-dimensional points, a number of neighbors k, and a target dimension d, reduce the data to d dimensions while preserving local neighborhood structure.
import numpy as np
def lle(X: np.ndarray, k: int, d: int) -> np.ndarray:
n = X.shape[0]
# Step 1: Find k nearest neighbors for each point
from scipy.spatial.distance import cdist
dists = cdist(X, X)
neighbors = np.zeros((n, k), dtype=int)
for i in range(n):
sorted_idx = np.argsort(dists[i])
neighbors[i] = sorted_idx[1:k+1]
# Step 2: Compute reconstruction weights
W = np.zeros((n, n))
for i in range(n):
Z = X[neighbors[i]] - X[i]
C = Z @ Z.T
C += np.eye(k) * 1e-3 * np.trace(C)
w = np.linalg.solve(C, np.ones(k))
w /= w.sum()
W[i, neighbors[i]] = w
# Step 3: Compute embedding via eigenvectors of (I - W)^T (I - W)
M = (np.eye(n) - W)
M = M.T @ M
eigenvalues, eigenvectors = np.linalg.eigh(M)
# Skip the smallest eigenvalue (near zero), take next d
Y = eigenvectors[:, 1:d+1]
return Y(I - W)^T (I - W).