def dS2deta(nparams, nlevels, L, XtX, XtZ, ZtZ, ZtX, D, sigma2): # Number of voxels nv = D.shape[0] # Calculate X'V^{-1}X=X'(I+ZDZ')^{-1}X=X'X-X'Z(I+DZ'Z)^{-1}DZ'X XtiVX = XtX - XtZ @ np.linalg.inv(np.eye(D.shape[1]) + D @ ZtZ) @ D @ ZtX # New empty array for differentiating S^2 wrt gamma. dS2deta = np.zeros((nv, 1+np.int32(np.sum(nparams*(nparams+1)/2)),1)) # Work out indices for each start of each component of vector # i.e. [dS2/dsigm2, dS2/vechD1,...dS2/vechDr] DerivInds = np.int32(np.cumsum(nparams*(nparams+1)/2) + 1) DerivInds = np.insert(DerivInds,0,1) # Work of derivative wrt to sigma^2 dS2dsigma2 = L @ np.linalg.inv(XtiVX) @ L.transpose() # Add to dS2deta dS2deta[:,0:1] = dS2dsigma2.reshape(dS2deta[:,0:1].shape) # Now we need to work out ds2dVech(Dk) for k in np.arange(len(nparams)): # Initialize an empty zeros matrix dS2dvechDk = np.zeros((np.int32(nparams[k]*(nparams[k]+1)/2),1))#... for j in np.arange(nlevels[k]): # Get the indices for this level and factor. Ikj = faclev_indices2D(k, j, nlevels, nparams) # Work out Z_(k,j)'Z ZkjtZ = ZtZ[:,Ikj,:] # Work out Z_(k,j)'X ZkjtX = ZtX[:,Ikj,:] # Work out Z_(k,j)'V^{-1}X ZkjtiVX = ZkjtX - ZkjtZ @ np.linalg.inv(np.eye(D.shape[1]) + D @ ZtZ) @ D @ ZtX # Work out the term to put into the kronecker product # K = Z_(k,j)'V^{-1}X(X'V^{-1})^{-1}L' K = ZkjtiVX @ np.linalg.inv(XtiVX) @ L.transpose() # Sum terms dS2dvechDk = dS2dvechDk + mat2vech3D(kron3D(K,K.transpose(0,2,1))) # Multiply by sigma^2 dS2dvechDk = np.einsum('i,ijk->ijk',sigma2,dS2dvechDk) # Add to dS2deta dS2deta[:,DerivInds[k]:DerivInds[k+1]] = dS2dvechDk.reshape(dS2deta[:,DerivInds[k]:DerivInds[k+1]].shape) return(dS2deta)
def get_covdldDk1Dk23D(k1, k2, nlevels, nparams, ZtZ, DinvIplusZtZD, invDupMatdict, vec=False): # Sum of R_(k1, k2, i, j) kron R_(k1, k2, i, j) over i and j for i in np.arange(nlevels[k1]): for j in np.arange(nlevels[k2]): # Get the indices for the k1th factor jth level Ik1i = faclev_indices2D(k1, i, nlevels, nparams) Ik2j = faclev_indices2D(k2, j, nlevels, nparams) # Work out R_(k1, k2, i, j) Rk1k2ij = ZtZ[np.ix_(np.arange(ZtZ.shape[0]), Ik1i, Ik2j)] - ( ZtZ[:, Ik1i, :] @ DinvIplusZtZD @ ZtZ[:, :, Ik2j]) # Work out Rk1k2ij kron Rk1k2ij RkRt = kron3D(Rk1k2ij, Rk1k2ij) # Add together if (i == 0) and (j == 0): RkRtSum = RkRt else: RkRtSum = RkRtSum + RkRt # Multiply by duplication matrices and save if not vec: covdldDk1dldk2 = 1 / 2 * invDupMatdict[k1] @ RkRtSum @ invDupMatdict[ k2].transpose() else: covdldDk1dldk2 = 1 / 2 * RkRtSum # Return the result return (covdldDk1dldk2)
def get_covdldDkdsigma23D(k, sigma2, nlevels, nparams, ZtZ, DinvIplusZtZD, invDupMatdict, vec=False): # Number of voxels nv = DinvIplusZtZD.shape[0] # Sum of R_(k, j) over j RkSum = np.zeros((nv, nparams[k], nparams[k])) for j in np.arange(nlevels[k]): # Get the indices for the kth factor jth level Ikj = faclev_indices2D(k, j, nlevels, nparams) # Work out R_(k, j) Rkj = ZtZ[np.ix_(np.arange(ZtZ.shape[0]), Ikj, Ikj)] - forceSym3D( ZtZ[:, Ikj, :] @ DinvIplusZtZD @ ZtZ[:, :, Ikj]) # Add together RkSum = RkSum + Rkj # Multiply by duplication matrices and if not vec: covdldDdldsigma2 = np.einsum('i,ijk->ijk', 1 / (2 * sigma2), invDupMatdict[k] @ mat2vec3D(RkSum)) else: covdldDdldsigma2 = np.einsum('i,ijk->ijk', 1 / (2 * sigma2), mat2vec3D(RkSum)) return (covdldDdldsigma2)
def get_dldDk3D(k, nlevels, nparams, ZtZ, Zte, sigma2, DinvIplusZtZD, reml=False, ZtX=0, XtX=0): # Number of voxels nv = Zte.shape[0] # Initalize the derivative to zeros dldDk = np.zeros((nv, nparams[k], nparams[k])) # For each level j we need to add a term for j in np.arange(nlevels[k]): # Get the indices for the kth factor jth level Ikj = faclev_indices2D(k, j, nlevels, nparams) # Get (the kj^th columns of Z)^T multiplied by Z Z_kjtZ = ZtZ[:, Ikj, :] Z_kjte = Zte[:, Ikj, :] # Get the first term of the derivative Z_kjtVinve = Z_kjte - (Z_kjtZ @ DinvIplusZtZD @ Zte) firstterm = np.einsum( 'i,ijk->ijk', 1 / sigma2, forceSym3D(Z_kjtVinve @ Z_kjtVinve.transpose((0, 2, 1)))) # Get (the kj^th columns of Z)^T multiplied by (the kj^th columns of Z) Z_kjtZ_kj = ZtZ[np.ix_(np.arange(ZtZ.shape[0]), Ikj, Ikj)] secondterm = forceSym3D(Z_kjtZ_kj) - forceSym3D( Z_kjtZ @ DinvIplusZtZD @ Z_kjtZ.transpose((0, 2, 1))) if j == 0: # Start a running sum over j dldDk = firstterm - secondterm else: # Add these to the running sum dldDk = dldDk + firstterm - secondterm if reml == True: invXtinvVX = np.linalg.inv(XtX - ZtX.transpose( (0, 2, 1)) @ DinvIplusZtZD @ ZtX) # For each level j we need to add a term for j in np.arange(nlevels[k]): # Get the indices for the kth factor jth level Ikj = faclev_indices2D(k, j, nlevels, nparams) Z_kjtZ = ZtZ[:, Ikj, :] Z_kjtX = ZtX[:, Ikj, :] Z_kjtinvVX = Z_kjtX - Z_kjtZ @ DinvIplusZtZD @ ZtX dldDk = dldDk + 0.5 * Z_kjtinvVX @ invXtinvVX @ Z_kjtinvVX.transpose( (0, 2, 1)) # Halve the sum (the coefficient of a half was not included in the above) dldDk = forceSym3D(dldDk / 2) # Store it in the dictionary return (dldDk)
def initDk3D(k, ZtZ, Zte, sigma2, nlevels, nparams, invDupMatdict): # Small check on sigma2 if len(sigma2.shape) > 1: sigma2 = sigma2.reshape(sigma2.shape[0]) # Initalize D to zeros invSig2ZteetZminusZtZ = np.zeros((Zte.shape[0], nparams[k], nparams[k])) # First we work out the derivative we require. for j in np.arange(nlevels[k]): Ikj = faclev_indices2D(k, j, nlevels, nparams) # Work out Z_(k, j)'Z_(k, j) ZkjtZkj = ZtZ[np.ix_(np.arange(ZtZ.shape[0]), Ikj, Ikj)] # Work out Z_(k,j)'e Zkjte = Zte[:, Ikj, :] if j == 0: # Add first \sigma^{-2}Z'ee'Z - Z_(k,j)'Z_(k,j) invSig2ZteetZminusZtZ = np.einsum( 'i,ijk->ijk', 1 / sigma2, (Zkjte @ Zkjte.transpose(0, 2, 1))) - ZkjtZkj else: # Add next \sigma^{-2}Z'ee'Z - Z_(k,j)'Z_(k,j) invSig2ZteetZminusZtZ = invSig2ZteetZminusZtZ + np.einsum( 'i,ijk->ijk', 1 / sigma2, (Zkjte @ Zkjte.transpose(0, 2, 1))) - ZkjtZkj # Second we need to work out the double sum of Z_(k,j)'Z_(k,j) for j in np.arange(nlevels[k]): for i in np.arange(nlevels[k]): Iki = faclev_indices2D(k, i, nlevels, nparams) Ikj = faclev_indices2D(k, j, nlevels, nparams) # Work out Z_(k, j)'Z_(k, j) ZkitZkj = ZtZ[np.ix_(np.arange(ZtZ.shape[0]), Iki, Ikj)] if j == 0 and i == 0: # Add first Z_(k,j)'Z_(k,j) kron Z_(k,j)'Z_(k,j) ZtZkronZtZ = kron3D(ZkitZkj, ZkitZkj.transpose(0, 2, 1)) else: # Add next Z_(k,j)'Z_(k,j) kron Z_(k,j)'Z_(k,j) ZtZkronZtZ = ZtZkronZtZ + kron3D(ZkitZkj, ZkitZkj.transpose(0, 2, 1)) # Work out information matrix infoMat = invDupMatdict[k] @ ZtZkronZtZ @ invDupMatdict[k].transpose() # Work out the final term. Dkest = vech2mat3D( np.linalg.inv(infoMat) @ mat2vech3D(invSig2ZteetZminusZtZ)) return (Dkest)