# Source: content/notes/ml/svm-and-kernels.md
# Independent CPU example; use the curriculum environment.
# See /notes/ml/#example-environment or /notes/deep-learning/#example-environment.

import numpy as np
from sklearn.svm import SVC, SVR

X = np.array([[-2.0], [-1.0], [1.0], [2.0]])
y = np.array([-1, -1, 1, 1])
C = 10.0
model = SVC(kernel="linear", C=C, tol=1e-10).fit(X, y)
signed = model.dual_coef_[0]
alpha = np.zeros(len(y))
alpha[model.support_] = np.abs(signed)
w = signed @ model.support_vectors_
margin = y * model.decision_function(X)
slack = np.maximum(0.0, 1.0 - margin)
assert np.allclose(w, [1.0], atol=1e-7)
assert np.allclose(model.intercept_, [0.0], atol=1e-7)
assert np.allclose(alpha, [0.0, 0.5, 0.5, 0.0], atol=1e-7)
assert abs(alpha @ y) < 1e-7
assert np.allclose(alpha * (margin - 1.0 + slack), 0.0, atol=1e-7)
assert np.allclose((C - alpha) * slack, 0.0, atol=1e-7)
primal = 0.5 * (w @ w) + C * slack.sum()
dual = alpha.sum() - 0.5 * (w @ w)
assert abs(primal - dual) < 1e-7

points = np.array([[1.0, 2.0], [-1.0, 0.5], [2.0, -1.0]])
phi = np.column_stack((points[:, 0] ** 2, points[:, 1] ** 2,
                       np.sqrt(2.0) * points[:, 0] * points[:, 1]))
gram = (points @ points.T) ** 2
assert np.allclose(phi @ phi.T, gram)
assert np.linalg.eigvalsh(gram).min() > -1e-10

reg = SVR(kernel="linear", C=10.0, epsilon=0.1).fit(X, X[:, 0])
residual = np.abs(X[:, 0] - reg.predict(X))
assert residual.max() <= 0.1001
print("weights", w, "multipliers", alpha, "duality gap", primal - dual)
