def gradients_XX(self, dL_dK, X, X2=None):
<EOL>invdist = self._inv_dist(X, X2)<EOL>invdist2 = invdist**<NUM_LIT:2><EOL>dL_dr = self.dK_dr_via_X(X, X2) <EOL>tmp1 = dL_dr * invdist<EOL>dL_drdr = self.dK2_drdr_via_X(X, X2) <EOL>tmp2 = dL_drdr*invdist2<EOL>l2 =  np.ones(X.shape[<NUM_LIT:1>])*self.lengthscale**<NUM_LIT:2> <EOL>if X2 is None:<EOL><INDENT>X2 = X<EOL>tmp1 -= np.eye(X.shape[<NUM_LIT:0>])*self.variance<EOL><DEDENT>else:<EOL><INDENT>tmp1[invdist2==<NUM_LIT:0.>] -= self.variance<EOL><DEDENT>dist = X[:,None,:] - X2[None,:,:]<EOL>dist = (dist[:,:,:,None]*dist[:,:,None,:])<EOL>I = np.ones((X.shape[<NUM_LIT:0>], X2.shape[<NUM_LIT:0>], X2.shape[<NUM_LIT:1>], X.shape[<NUM_LIT:1>]))*np.eye((X2.shape[<NUM_LIT:1>]))<EOL>grad = (((dL_dK*(tmp1*invdist2 - tmp2))[:,:,None,None] * dist)/l2[None,None,:,None]<EOL>- (dL_dK*tmp1)[:,:,None,None] * I)/l2[None,None,None,:]<EOL>return grad<EOL>
Given the derivative of the objective K(dL_dK), compute the second derivative of K wrt X and X2:

returns the full covariance matrix [QxQ] of the input dimensionfor each pair or vectors, thus
the returned array is of shape [NxNxQxQ].

..math:
    \frac{\partial^2 K}{\partial X2 ^2} = - \frac{\partial^2 K}{\partial X\partial X2}

..returns:
    dL2_dXdX2:  [NxMxQxQ] in the cov=True case, or [NxMxQ] in the cov=False case,
                for X [NxQ] and X2[MxQ] (X2 is X if, X2 is None)
                Thus, we return the second derivative in X2.