def _grad_shapelets(X, y, n_classes, weights, shapelets, lengths, alpha,
                    penalty, C, fit_intercept, intercept_scaling,
                    sample_weight):
    """Compute the gradient of the loss with regards to the shapelets."""
    n_samples, n_timestamps = X.shape

    # Derive distances between shapelets and time series
    distances = _derive_all_squared_distances(
        X, n_samples, n_timestamps, shapelets, lengths, alpha)
    distances = np.asarray(distances).T

    # Add intercept
    if fit_intercept:
        distances = np.c_[np.ones(n_samples) * intercept_scaling, distances]
        weight_idx = 1
    else:
        weight_idx = 0

    # Derive probabilities and cross-entropy loss
    if weights.ndim == 1:
        proba = _expit(distances @ weights)
        proba = np.clip(proba, 1e-8, 1 - 1e-8)
    else:
        proba = _softmax(distances @ weights, n_samples, n_classes)
        proba = np.clip(proba, 1e-8, 1 - 1e-8)

    # Reshape some arrays
    if weights.ndim == 1:
        proba_minus_y = (proba - y)[:, None]
    else:
        proba_minus_y = proba - y

    # Compute the gradients
    gradients = _compute_shapelet_grad(
        X, n_samples, n_timestamps, weights, shapelets, lengths,
        alpha, proba_minus_y, weight_idx, sample_weight
    )
    gradients = np.concatenate(gradients)

    return gradients
Compute the gradient of the loss with regards to the shapelets.
pyts/classification/learning_shapelets.py