"""M4 — LAT: Linear Artificial Tomography — SVM decision boundary direction.""" import numpy as np from sklearn.svm import LinearSVC from sklearn.preprocessing import StandardScaler from src.methods.base import SteeringMethod class LAT(SteeringMethod): """LAT — SVM decision boundary as steering direction.""" @property def name(self) -> str: return "LAT" @property def method_id(self) -> str: return "M4" def extract_vector( self, h_pos: np.ndarray, h_neg: np.ndarray, **kwargs, ) -> np.ndarray: """Compute SVM decision boundary direction. Args: h_pos: (N_pos, d) positive activations h_neg: (N_neg, d) negative activations Returns: (d,) normal vector to SVM decision boundary """ X = np.concatenate([h_pos, h_neg], axis=0) y = np.concatenate([ np.ones(len(h_pos)), np.zeros(len(h_neg)), ]) # Standardise for numeric stability scaler = StandardScaler() X_scaled = scaler.fit_transform(X) C = kwargs.get("C", 1.0) max_iter = kwargs.get("max_iter", 5000) svm = LinearSVC(C=C, max_iter=max_iter, dual="auto") svm.fit(X_scaled, y) # Get weight vector in original (unscaled) space w = svm.coef_[0] / scaler.scale_ # Normalise to unit vector w = w / (np.linalg.norm(w) + 1e-8) return w