human
Complete this python program:
def _logistic_grad_hess(w, X, y, alpha, sample_weight=None):
assistant
#Here is the Python program you requested.

    """Computes the gradient and the Hessian, in the case of a logistic loss.
    Parameters
    w : ndarray, shape (n_features,) or (n_features + 1,)
    Coefficient vector.
    X : {array-like, sparse matrix}, shape (n_samples, n_features)
    Training data.
    y : ndarray, shape (n_samples,)
    Array of labels.
    alpha : float
    Regularization parameter. alpha is equal to 1 / C.
    sample_weight : array-like, shape (n_samples,) optional
    Array of weights that are assigned to individual samples.
    If not provided, then each sample is given unit weight.
    Returns
    grad : ndarray, shape (n_features,) or (n_features + 1,)
    Logistic gradient.
    Hs : callable
    Function that takes the gradient as a parameter and returns the
    matrix product of the Hessian and gradient.
    """
    (n_samples, n_features) = X.shape
    grad = np.empty_like(w)
    fit_intercept = (grad.shape[0] > n_features)
    (w, c, yz) = _intercept_dot(w, X, y)
    if (sample_weight is None):
        sample_weight = np.ones(y.shape[0])
    z = expit(yz)
    z0 = ((sample_weight * (z - 1)) * y)
    grad[:n_features] = (safe_sparse_dot(X.T, z0) + (alpha * w))
    if fit_intercept:
        grad[(-1)] = z0.sum()
    d = ((sample_weight * z) * (1 - z))
    if sparse.issparse(X):
        dX = safe_sparse_dot(sparse.dia_matrix((d, 0), shape=(n_samples, n_samples)), X)
    else:
        dX = (d[:, np.newaxis] * X)
    if fit_intercept:
        dd_intercept = np.squeeze(np.array(dX.sum(axis=0)))
    def Hs(s):
        ret = np.empty_like(s)
        ret[:n_features] = X.T.dot(dX.dot(s[:n_features]))
        ret[:n_features] += (alpha * s[:n_features])
        if fit_intercept:
            ret[:n_features] += (s[(-1)] * dd_intercept)
            ret[(-1)] = dd_intercept.dot(s[:n_features])
            ret[(-1)] += (d.sum() * s[(-1)])
        return ret
    return (grad, Hs)