Builds a penalty matrix for P-Splines with continuous features.
    Penalizes the squared differences between basis coefficients.

    Parameters
    ----------
    n : int
        number of splines

    coef : unused
        for compatibility with constraints

    derivative: int, default: 2
        which derivative do we penalize.
        derivative is 1, we penalize 1st order derivatives,
        derivative is 2, we penalize 2nd order derivatives, etc

    Returns
    -------
    penalty matrix : sparse csc matrix of shape (n,n)

def derivative(n, coef, derivative=2, periodic=False):
    """
    Builds a penalty matrix for P-Splines with continuous features.
    Penalizes the squared differences between basis coefficients.

    Parameters
    ----------
    n : int
        number of splines

    coef : unused
        for compatibility with constraints

    derivative: int, default: 2
        which derivative do we penalize.
        derivative is 1, we penalize 1st order derivatives,
        derivative is 2, we penalize 2nd order derivatives, etc

    Returns
    -------
    penalty matrix : sparse csc matrix of shape (n,n)
    """
    if n == 1:
        # no derivative for constant functions
        return sp.sparse.csc_matrix(0.)
    D = sparse_diff(sp.sparse.identity(n + 2*derivative*periodic).tocsc(), n=derivative).tolil()

    if periodic:
        # wrap penalty
        cols = D[:, :derivative]
        D[:, -2 * derivative:-derivative] += cols * (-1) ** derivative

        # do symmetric operation on lower half of matrix
        n_rows = int((n + 2 * derivative)/2)
        D[-n_rows:] = D[:n_rows][::-1, ::-1]

        # keep only the center of the augmented matrix
        D = D[derivative:-derivative, derivative:-derivative]
    return D.dot(D.T).tocsc()