if __name__ == "__main__":
    rng = np.random.default_rng(0)
    n, d = 300, 5
    X = rng.normal(size=(n, d))
    # only features 0 and 2 actually drive the label
    true_weights = np.array([3.0, 0.0, -2.0, 0.0, 0.0])
    y = (X @ true_weights + rng.normal(0, 0.1, n) > 0).astype(int)

    def model_predict(X_input):
        return (X_input @ true_weights > 0).astype(float)

    def accuracy_metric(y_true, y_pred):
        return np.mean(y_true == y_pred)

    importances = permutation_importance(
        model_predict, X, y, accuracy_metric
    )
    print("Permutation importances (features 0-4):")
    print(np.round(importances, 3))

    x_query = X[0]
    background = X[:50]
    shap_values = kernel_shap_approx(model_predict, x_query, background)
    print("\nApproximate SHAP values for one prediction:")
    print(np.round(shap_values, 3))

    lime_coeffs = lime_explain(model_predict, x_query)
    print("\nLIME local linear coefficients:")
    print(np.round(lime_coeffs, 3))
