R/EnvelopeModels.R

Defines functions .contr .expan .ridgeinv .GaussElim henvlp_MU poisson.envlp_MU binom.envlp_MU xyenvlp_MU sxenvlp_MU envlp_MU GLENV.poisson GLENV.binom GLENV HENV ENV.mvxy ENV.mv ENV.uni ENV

Documented in ENV GLENV HENV

#' Envelope Regression for Sufficient Dimension Reduction
#'
#' @description This is an adaptation with some improvements on functions in the Renvlp package. This can fit
#' envelope models for dimension reduction to either univariate or multivariate numeric responses with numeric
#' predictors. In the case of a univaraite outcome, a predictor envelope model is fit. For multivariate outcomes,
#' an envelope is fit to both the response matrix and model matrix.
#'
#' @param formula model formula
#' @param data data frame
#' @param rank number of dimensions
#' @param yrank number of dimensions for a multivariate response.
#' @param se should standard errors for the regression basis be calculated? defaults to TRUE.
#' @param init an optional initial covariate dimension reduction subspace. defaults to NULL.
#'
#'
#' @return an sdr object
#' @export
#'
ENV <- function(formula, data, rank = "all", yrank = "all", se = TRUE, init = NULL){

  X = model.matrix(formula, data)[,-1]
  Y = model.frame(formula, data)
  Y = model.response(Y)
  Y <- as.matrix(Y)
  if (rank == "all" || is.null(rank)){
    rank = ncol(X)
  }
  if (ncol(Y)==1){
    ENV.uni(formula, data, rank, se, init)
  }
  else{
    if (yrank == "all" || yrank == ncol(Y)){ENV.mv(formula, data, rank, se, init)}
    else{ENV.mvxy(formula, data, rank, yrank, se, Pinit = init, Ginit = NULL)}
  }
}

ENV.uni <- function(formula, data, rank, se = TRUE, init = NULL) {

  X = model.matrix(formula, data)[,-1]
  Y = model.frame(formula, data)
  Y = model.response(Y)
  Y <- as.matrix(Y)
  X <- as.matrix(X)
  a <- dim(Y)
  n <- a[1]
  r <- a[2]
  p <- ncol(X)
  if (a[1] != nrow(X)) stop("X and Y should have the same number of observations.")
  if (rank > p || rank < 0) stop("rank must be an integer between 0 and p.")
  if(sum(duplicated(cbind(X, Y), MARGIN = 2)) > 0) stop("Some responses also appear in the predictors, or there maybe duplicated columns in X or Y.")

  SigmaY  <- cov(Y) * ((n-1)/n)
  SigmaYX <- cov(Y,X) * ((n-1)/n)
  SigmaX  <- solve(.ridgeinv(cov(X))) * ((n-1)/n)
  TauY <- .ridgeinv(SigmaY)
  eig.SigmaY <- eigen(SigmaY)
  U <- crossprod(SigmaYX, TauY) %*% SigmaYX
  M <- SigmaX - U
  tmp <- envlp_MU(M, U, rank)

  if (!is.null(init)) {
    if (nrow(init) != p || ncol(init) != rank) stop("The initial value should have p rows and rank columns.")
    tmp0 <- qr.Q(qr(init), complete = TRUE)
    tmp$Gammahat <- as.matrix(tmp0[, 1:rank])
    tmp$Gamma0hat <- as.matrix(tmp0[, (rank+1):p])
  }

  Gammahat <- tmp$Gammahat;Gamma0hat <- tmp$Gamma0hat;objfun <- tmp$objfun
  covMatrix <- NULL;std.err <- NULL;ratio <- NULL

  if (rank == p) {
    TauX <- .ridgeinv(SigmaX)
    olscoef <- tcrossprod(TauX, SigmaYX)
    etahat <- olscoef
    Omegahat <- SigmaX
    Omega0hat <- NULL
    muhat <- colMeans(Y) - crossprod(olscoef, colMeans(X))
    betahat <- olscoef
    SigmaXhat <- M + U
    SigmaYcXhat <- SigmaY - SigmaYX %*% olscoef
    log_lik <- - n * (r + p) / 2 * (log(2 * pi) + 1) - n / 2 * (objfun + sum(log(eig.SigmaY$values)))
    if (se == T) {
      covMatrix <- kronecker(SigmaYcXhat, TauX)
      std.err <- matrix(sqrt(diag(covMatrix)), nrow = p)
      ratio <- matrix(1, p, r)
    }
  } else {
    TauX <- .ridgeinv(SigmaX)
    etahat <- crossprod(Gammahat, t(SigmaYX))
    Omegahat <- crossprod(Gammahat, SigmaX) %*% Gammahat
    Omega0hat <- crossprod(Gamma0hat, SigmaX) %*% Gamma0hat
    invOmegahat <- .ridgeinv(Omegahat)
    betahat <- Gammahat %*% invOmegahat %*% etahat
    muhat <- colMeans(Y) - crossprod(betahat, colMeans(X))
    SigmaXhat <- Gammahat %*% tcrossprod(Omegahat, Gammahat) + Gamma0hat %*% tcrossprod(Omega0hat, Gamma0hat)
    olscoef <- tcrossprod(TauX, SigmaYX)
    PGamma <- tcrossprod(Gammahat)
    SigmaYcXhat <- SigmaY - SigmaYX %*% PGamma %*% .ridgeinv(SigmaXhat) %*% tcrossprod(PGamma , SigmaYX)
    log_lik <- - n * (r + p) / 2 * (log(2 * pi) + 1) - n / 2 * (objfun + sum(log(eig.SigmaY$values)))

    if (se == T) {
      covMatrix <- kronecker(SigmaYcXhat, TauX)
      seFm <- matrix(sqrt(diag(covMatrix)), nrow = p)
      TaumaYcXhat <- .ridgeinv(SigmaYcXhat)
      invOmega0hat <- .ridgeinv(Omega0hat)
      temp <- kronecker(etahat %*% tcrossprod(TaumaYcXhat, etahat), Omega0hat) + kronecker(invOmegahat, Omega0hat) + kronecker(Omegahat, invOmega0hat) - 2 * kronecker(diag(rank), diag(p - rank))
      temp2 <- kronecker(t(etahat), Gamma0hat)
      covMatrix <- kronecker(SigmaYcXhat, Gammahat %*% tcrossprod(invOmegahat, Gammahat)) + temp2 %*% tcrossprod(.ridgeinv(temp), temp2)
      std.err <- matrix(sqrt(diag(covMatrix)), nrow = p)
      ratio <- seFm / std.err
    }
  }

  betahat <- as.vector(betahat)
  names(betahat) <- colnames(X)
  predictors <- X %*% Gammahat
  fitted <- as.vector(X %*% betahat)
  colnames(predictors) <- paste0("SP", 1:ncol(predictors))
  colnames(Gammahat) <- paste0("SP", 1:ncol(predictors))
  rownames(Gammahat) <- colnames(X)
  if (se){
    std.err <- std.err * (1/sqrt(n))
    p.values <- 2*pnorm(-abs((betahat/std.err)))
  } else {
    std.err <- NULL
    p.values <- NULL
  }
  return(structure(list(basis = Gammahat, coef = betahat, fitted = fitted, predictors = predictors, eta = etahat, muhat = muhat, Omega = Omegahat, SigmaYcX = SigmaYcXhat, SigmaX = SigmaXhat, log_lik = log_lik,  n = n, covMatrix = covMatrix, std.err = std.err, p.values = p.values, ratio = ratio,  y.obs = Y, formula = formula, mf = model.frame(formula, data)), class = "sdr", model = "ENV"))
}


ENV.mv <- function(formula, data, rank, se = TRUE, init = NULL) {

  X = model.matrix(formula, data)[,-1];Y = model.frame(formula, data)
  Y = model.response(Y);Y <- as.matrix(Y);X <- as.matrix(X)
  a <- dim(Y);n <- a[1];r <- a[2];p <- ncol(X);R <- rep(1, p)
  if (a[1] != nrow(X)) stop("X and Y should have the same number of observations.")
  if (rank > p || rank < 0) stop("rank must be an integer between 0 and p.")
  if(sum(duplicated(cbind(X, Y), MARGIN = 2)) > 0) stop("Some responses also appear in the predictors, or there maybe duplicated columns in X or Y.")
  SigmaY  <- cov(Y) * ((n-1)/n)
  SigmaYX <- cov(Y,X) * ((n-1)/n)
  SigmaX  <- solve(.ridgeinv(cov(X))) * ((n-1)/n)
  TauY <- .ridgeinv(SigmaY)
  eig.SigmaY <- eigen(SigmaY)
  eig.SigmaX <- eigen(SigmaX)
  U <- crossprod(SigmaYX, TauY) %*% SigmaYX
  M <- SigmaX - U
  q <- length(R)
  tmp <- envlp_MU(M, U, rank)

  if (!is.null(init)) {
    if (nrow(init) != p || ncol(init) != rank) stop("The initial value should have p rows and rank columns.")
    tmp0 <- qr.Q(qr(init), complete = TRUE)
    tmp$Gammahat <- as.matrix(tmp0[, 1:rank])
    tmp$Gamma0hat <- as.matrix(tmp0[, (rank+1):p])
  }

  Gammahat <- tmp$Gammahat
  Gamma0hat <- tmp$Gamma0hat
  objfun <- tmp$objfun
  covMatrix <- NULL
  std.err <- NULL
  ratio <- NULL

  if (rank >= (p - (q - 1) / r)) {
    Gammahat <- diag(p)
    Gamma0hat <- NULL
    TauX <- .ridgeinv(SigmaX)
    olscoef <- tcrossprod(TauX, SigmaYX)
    etahat <- olscoef
    Omegahat <- SigmaX
    Omega0hat <- NULL
    muYhat <- colMeans(Y)
    muXhat <- colMeans(X)
    betahat <- olscoef
    SigmaXhat <- SigmaX
    SigmaYcXhat <- SigmaY - SigmaYX %*% olscoef
    eig.SigmaXhat <- eigen(SigmaXhat)
    eig.SigmaYcXhat <- eigen(SigmaYcXhat)
    objfun <- (sum(log(eig.SigmaXhat$values)) + sum(log(eig.SigmaYcXhat$values)) + p)
    log_lik <- -n * (r + p)/2 * log(2 * pi) - n * r/2 - n/2 * objfun
    if (se == T) {
      covMatrix <- kronecker(SigmaYcXhat, TauX)
      std.err <- matrix(sqrt(diag(covMatrix)), nrow = p)
      ratio <- matrix(1, p, r)
    }
  } else {
    tmp1 <- sxenvlp_MU(X, Y, rank, R)

    if (!is.null(init)) {
      if (nrow(init) != p || ncol(init) != rank) stop("The initial value should have p rows and rank columns.")
      tmp0 <- qr.Q(qr(init), complete = TRUE)
      tmp1$Gammahat <- as.matrix(tmp0[, 1:rank])
      tmp1$Gamma0hat <- as.matrix(tmp0[, (rank+1):p])
      if (length(R) == p) {
        make_objfun <- function(SigmaY, SigmaYX, SigmaX){
          function(d, Gamma, X, Y){
            X <- as.matrix(X);Y <- as.matrix(Y);a <- dim(Y);n <- a[1];r <- a[2];p <- ncol(X)
            TauX <- .ridgeinv(SigmaX);TauY <- .ridgeinv(SigmaY)
            t1 <- crossprod(SigmaYX, TauY);U <- t1 %*% SigmaYX; M <- SigmaX - U
            d1 <- c(1, d);Lambda <- diag(d1);invLambda <- diag(1 / d1)
            m1 <- crossprod(Gamma, Lambda);eigtem1 <- eigen(m1 %*% tcrossprod(TauX, m1))
            m2 <- crossprod(Gamma, invLambda);eigtem2 <- eigen(m2 %*% tcrossprod(M, m2))
            temp1 <- sum(log(eigtem1$values));temp2 <- sum(log(eigtem2$values))
            objfun <- temp1 + temp2
            return(objfun)
          }
        }
        objfun <- make_objfun(SigmaY, SigmaYX, SigmaX)
        d.init <- rep(1, (p - 1))
        k2 <- rep(0, (p - 1))
        tmp.init <- Rsolnp::solnp(pars = d.init, fun = objfun, LB = k2,
                                  .control = list(delta = 1e-6, tol = 0.0005, trace = 0),
                                  Gamma = tmp1$Gammahat, X = X, Y = Y)
        d <- tmp.init$pars
        d1 <- c(1, d)
        tmp1$Lambdahat <- diag(d1)
      } else {
        make_objfun1 <- function(SigmaY, SigmaYX, SigmaX){
          function(d, Gamma, X, Y, R){
            X <- as.matrix(X);Y <- as.matrix(Y)
            a <- dim(Y);n <- a[1];r <- a[2];p <- ncol(X)
            TauX <- .ridgeinv(SigmaX);TauY<-.ridgeinv(SigmaY)
            t1 <- crossprod(SigmaYX, TauY)
            U <- t1 %*% SigmaYX;M <- SigmaX - U;Lambda <- diag(d);invLambda <- diag(1 / d)
            m1 <- crossprod(Gamma, Lambda);eigtem1 <- eigen(m1 %*% tcrossprod(TauX, m1))
            m2 <- crossprod(Gamma, invLambda);eigtem2 <- eigen(m2 %*% tcrossprod(M, m2))
            temp1 <- sum(log(eigtem1$values));temp2 <- sum(log(eigtem2$values));objfun <- temp1 + temp2
            return(objfun)
          }
        }
        objfun1 <- make_objfun1(SigmaY, SigmaYX, SigmaX)
        heq <- function (d, Gamma, X, Y, R){
          iter = sum(R)
          i <- 1
          cont <- NULL
          while (i < iter) {
            C <- matrix(0, (R[i] - 1), sum(R))
            for (j in 2 : (sum(R) - 1)){
              if (R[i] == j) {
                for (k in 1 : (j - 1)){
                  s <- sum(R[0 : (i - 1)]) + 1
                  C[k , s] <- 1
                  C[k , (s + k)] <- -1
                }
              }
            }
            cont <- rbind(cont, C)
            if (i == length(R)) {
              c1 <- c(1, rep(0, (sum(R) - 1)))
              cont <- rbind(c1, cont)
              break
            } else {
              i <- i + 1
            }
          }

          g <- rep(NA, nrow(cont))
          g[1] <- d[1]
          for (m in 2 : nrow(cont)){
            g[m] <- d[which(cont[m, ] == 1)] - d[which(cont[m, ] == -1)]
          }
          g
        }
        d.init <- rep(1, p)
        g1 <- heq(d.init, tmp1$Gammahat, X, Y, R)
        k1 <- c(1, rep(0, length(g1) - 1))
        k2 <- rep(0, p)

        tmp.init <- Rsolnp::solnp(pars = d.init, fun = objfun1, eqfun = heq, eqB = k1, LB = k2,
                                  .control = list(delta = 1e-6, tol = 0.0005, trace = 0),
                                  R = R, Gamma = tmp1$Gammahat, X = X, Y = Y)
        d <- tmp.init$pars
        tmp1$Lambdahat <- diag(d)
      }
    }
    TauX <- .ridgeinv(SigmaX)
    Gammahat <- tmp1$Gammahat
    Gamma0hat <- tmp1$Gamma0hat
    Lambdahat <- tmp1$Lambdahat
    d1 <- diag(Lambdahat)
    invLambdahat <- diag(1 / d1)
    E1 <- crossprod(Gammahat, invLambdahat)
    Omegahat <- E1 %*% tcrossprod(SigmaX, E1)
    E2 <- crossprod(Gamma0hat, invLambdahat)
    Omega0hat <- E2 %*% tcrossprod(SigmaX, E2)
    invOmegahat <- .ridgeinv(Omegahat)
    etahat <- invOmegahat %*% tcrossprod(E1, SigmaYX)
    betahat <- invLambdahat %*% Gammahat %*% etahat
    muYhat <- colMeans(Y)
    muXhat <- colMeans(X)
    E3 <- Lambdahat %*% Gammahat
    sig1 <- E3 %*% tcrossprod(Omegahat, E3)
    E4 <- Lambdahat %*% Gamma0hat
    sig2 <- E4 %*% tcrossprod(Omega0hat, E4)
    SigmaXhat <- sig1 + sig2
    Yc <- Y - tcrossprod(rep(1, nrow(Y)), colMeans(Y))
    Xc <- X - tcrossprod(rep(1, nrow(X)), colMeans(X))
    E5 <- Yc - Xc %*% betahat
    SigmaYcXhat <- crossprod(E5) / n
    TauYcX <- .ridgeinv(mpd(SigmaYcXhat))
    eig.SigmaXhat <- eigen(mpd(SigmaXhat))
    eig.SigmaYcXhat <- eigen(mpd(SigmaYcXhat))
    TauXhat <-.ridgeinv(mpd(SigmaXhat))
    s1 <- TauXhat %*% SigmaX
    eig.invX <- eigen(s1)
    objfun <- (sum(log(eig.SigmaXhat$values)) + sum(log(eig.SigmaYcXhat$values))  + sum(eig.invX$values))
    log_lik <- -n * (r + p)/2 * log(2 * pi) - n * r/2 - n/2 * objfun
    if (se == T) {
      U1 <- SigmaYX %*% TauX
      U2 <- tcrossprod(U1, SigmaYX)
      M2 <- SigmaY - U2
      covMatrix <- kronecker(M2, TauX)
      seFm <- matrix(sqrt(diag(covMatrix)), nrow = p)
      Ep <- .expan(p)
      Eu <- .expan(rank)
      Epu <- .expan((p - rank))
      Cp <- .contr(p)
      j11 <- kronecker(TauYcX, SigmaXhat)
      j222 <- kronecker(TauXhat, TauXhat)
      j22 <- crossprod(Ep, j222) %*% Ep / 2
      J <- cbind(rbind(j11, matrix(0, nrow = nrow(j22), ncol = ncol(j11))), rbind(matrix(0, nrow = nrow(j11), ncol = ncol(j22)), j22))
      diagvec <- function(R){
        E <- NULL
        p <- sum(R)
        for(i in 1 : (length(R) - 1)){
          e <- rep(0, p)
          s <- sum(R[1 : i])
          e[s + 1] <- 1
          t <- kronecker(e, e)
          E <- cbind(E, t)
        }
        E <- matrix(E, nrow = p^2)
      }
      L <- diagvec(R)
      SigmaL <- Gammahat %*% tcrossprod(Omegahat, Gammahat) + Gamma0hat %*% tcrossprod(Omega0hat, Gamma0hat)
      h1 <- crossprod(Gammahat, invLambdahat)
      h2 <- crossprod(etahat, h1)
      h11 <- - kronecker(h2, invLambdahat) %*% L
      h3 <- kronecker(Lambdahat %*% SigmaL, diag(p))
      h21 <- 2 * Cp %*% h3 %*% L
      h12 <- kronecker(diag(r), invLambdahat %*% Gammahat)
      h13 <- kronecker(t(etahat), invLambdahat)
      h4 <- Lambdahat %*% Gammahat %*% Omegahat
      h5 <- kronecker(h4, Lambdahat)
      h6 <- Lambdahat %*% Gammahat
      h7 <- Lambdahat %*% Gamma0hat
      h78 <- h7 %*% tcrossprod(Omega0hat, Gamma0hat)
      h8  <- kronecker(h6, h78)
      h23 <- 2 * Cp %*% (h5 - h8)
      h9 <- Lambdahat %*% Gammahat
      h24 <- Cp %*% kronecker(h9, h9) %*% Eu
      h10 <- Lambdahat %*% Gamma0hat
      h25 <- Cp %*% kronecker(h10, h10) %*% Epu
      H <- rbind(cbind(h11, h12, h13, matrix(0, nrow = nrow(h11), ncol = ncol(h24)), matrix(0, nrow = nrow(h11), ncol = ncol(h25))), cbind(h21, matrix(0, nrow = nrow(h21), ncol = ncol(h12)), h23, h24, h25))
      M1 <- crossprod(H, J) %*% H
      invM1 <- .ridgeinv(M1)
      V <- H %*% tcrossprod(invM1, H)
      covMatrix <- V[1:(p * r), 1:(p * r)]
      std.err <- matrix(sqrt(diag(covMatrix)), nrow = p)
      ratio <- seFm/std.err
    }
  }
  rownames(betahat) <- colnames(X)
  predictors <- X %*% Gammahat
  fitted <- X %*% betahat
  colnames(predictors) <- paste0("SP", 1:ncol(predictors))
  colnames(Gammahat) <- paste0("SP", 1:ncol(predictors))
  rownames(Gammahat) <- colnames(X)
  if (se){
    std.err <- std.err * (1/sqrt(n))
    p.values <- 2*pnorm(-abs((betahat / std.err)))
  } else {
    std.err <- NULL
    p.values <- NULL
  }
  return(structure(list(basis = Gammahat, coef = betahat, fitted = fitted, predictors = predictors, eta = etahat, muhat = muYhat, Gamma0 = Gamma0hat, Omega = Omegahat, SigmaYcX = SigmaYcXhat, SigmaX = SigmaXhat, log_lik = log_lik,  n = n, covMatrix = covMatrix, std.err = std.err, p.values = p.values, ratio = ratio, formula = formula, mf = model.frame(formula, data), y.obs = Y), class = "sdr", model = "ENV"))
}


ENV.mvxy <- function(formula, data, xrank, yrank, se = TRUE, Pinit = NULL, Ginit = NULL) {

  X = model.matrix(formula, data)[,-1];Y = model.frame(formula, data)
  Y = model.response(Y);Y <- as.matrix(Y);X <- as.matrix(X)
  a <- dim(Y);n <- a[1];r <- a[2];p <- ncol(X)
  if (a[1] != nrow(X))
    stop("X and Y should have the same number of observations.")
  if (yrank > r || yrank < 0)
    stop("yrank must be an integer between 0 and r.")
  if (xrank > p || xrank < 0)
    stop("xrank must be an integer between 0 and p.")
  if (sum(duplicated(cbind(X, Y), MARGIN = 2)) > 0)
    stop("Some responses also appear in the predictors, or there maybe duplicated columns in X or Y.")

  SigmaY  <- cov(Y) * ((n-1)/n)
  SigmaYX <- cov(Y,X) * ((n-1)/n)
  SigmaX  <- solve(.ridgeinv(cov(X))) * ((n-1)/n)
  TauX <- .ridgeinv(SigmaX)
  betaOLS <- SigmaYX %*% TauX
  covMatrix <- NULL
  std.err <- NULL
  if (yrank == 0 || xrank ==0) {
    Gammahat <- NULL
    Gamma0hat <- diag(r)
    Phihat <- NULL
    Phi0hat <- diag(p)
    etahat <- NULL
    Omegahat <- NULL
    Omega0hat <- SigmaY
    Deltahat <- NULL
    Delta0hat <- SigmaX
    muhat <- colMeans(Y)
    betahat <- matrix(0, p, r)
    SigmaYcXhat <- SigmaY
    SigmaXhat <- SigmaX
    tmp1 <- eigen(SigmaX)
    tmp2 <- eigen(SigmaY)
    objfun <- sum(log(tmp1$values)) + sum(log(tmp2$values))
    log_lik <- - n/2 * objfun
  }
  else if (yrank == r && xrank == p) {
    M <- SigmaY - tcrossprod(betaOLS, SigmaYX)
    Gammahat <- diag(r)
    Gamma0hat <- NULL
    Phihat <- diag(p)
    Phi0hat <- NULL
    etahat <- betaOLS
    Omegahat <- M
    Omega0hat <- NULL
    Deltahat <- SigmaX
    Delta0hat <- NULL
    muhat <- colMeans(Y) - betaOLS %*% colMeans(X)
    betahat <- t(betaOLS)
    SigmaYcXhat <- M
    SigmaXhat <- SigmaX
    tmp.e1 <- eigen(Omegahat)
    tmp.e3 <- eigen(Deltahat)
    objfun <- sum(log(tmp.e1$values)) + sum(log(tmp.e3$values))
    log_lik <- -n * (p + r) / 2 - n/2 * objfun
    if (se == T) {
      covMatrix <- kronecker(M, TauX)
      std.err <- matrix(sqrt(diag(covMatrix)), nrow = p)
    }
  }
  else if (xrank == p) {

    tmp.envfun <- function(SigmaX, SigmaY, SigmaYX, TauX, yrank){
      betaOLS <- SigmaYX %*% TauX
      U <- tcrossprod(betaOLS, SigmaYX)
      M <- SigmaY - U
      tmp <- envlp_MU(M, U, yrank)
      Gammahat <- tmp$Gammahat
      Gamma0hat <- tmp$Gamma0hat
      etahat <- crossprod(Gammahat, betaOLS)
      betahat <- Gammahat %*% etahat
      muhat <- colMeans(Y) - betahat %*% colMeans(X)
      Omegahat <- crossprod(Gammahat, M) %*% Gammahat
      Omega0hat <- crossprod(Gamma0hat, sigY) %*% Gamma0hat
      Sigma1 <- Gammahat %*% tcrossprod(Omegahat, Gammahat)
      Sigmahat <- Sigma1 + Gamma0hat %*% tcrossprod(Omega0hat, Gamma0hat)
      return(list(Gamma = Gammahat, Gamma0 = Gamma0hat, Omega = Omegahat, Omega0 = Omega0hat, beta = betahat, Sigma = Sigmahat))
    }

    tmp.env <- tmp.envfun(SigmaX, SigmaY, SigmaYX, TauX, yrank)
    Gammahat <- tmp.env$Gamma
    Gamma0hat <- tmp.env$Gamma0
    Phihat <- diag(p)
    Phi0hat <- NULL
    etahat <- TauX %*% crossprod(SigmaYX, Gammahat)
    Omegahat <- tmp.env$Omega
    Omega0hat <- tmp.env$Omega0
    Deltahat <- SigmaX
    Delta0hat <- NULL
    betahat <- t(tmp.env$beta)
    muhat <- colMeans(Y) - crossprod(betahat, colMeans(X))
    SigmaYcXhat <- tmp.env$Sigma
    SigmaXhat <- SigmaX
    tmp.e1 <- eigen(Omegahat)
    tmp.e2 <- eigen(Omega0hat)
    tmp.e3 <- eigen(Deltahat)
    invome <- .ridgeinv(Omegahat)
    invome0 <- .ridgeinv(Omega0hat)
    invdel <- .ridgeinv(Deltahat)
    t3 <- crossprod(Gammahat, SigmaY) %*% Gammahat %*% invome
    t5 <- crossprod(etahat, SigmaX) %*% etahat %*% invome
    t6 <- t(etahat) %*% crossprod(SigmaYX, Gammahat) %*% invome
    obj1 <- sum(log(tmp.e1$values)) + sum(log(tmp.e2$values))
    obj2 <- sum(log(tmp.e3$values)) + p
    obj3 <- sum(diag(t3)) + (r - yrank)
    obj4 <- sum(diag(t5)) - 2 * sum(diag(t6))
    objfun <- obj1 + obj2 + obj3 + obj4
    log_lik <- - (n/2) * objfun
    if (se == T) {
      M <- SigmaY - tcrossprod(betaOLS, SigmaYX)
      covMatrix <- kronecker(M, TauX)
      seFm <- matrix(sqrt(diag(covMatrix)), nrow = p)
      m1 <- Gammahat %*% tcrossprod(Omegahat, Gammahat)
      temp1 <- kronecker(m1, TauX)
      m2 <- crossprod(etahat, SigmaX) %*% etahat
      m3 <- kronecker(invome0, m2) + kronecker(invome0, Omegahat)
      m4 <- kronecker(Omega0hat, invome) - 2 * kronecker(diag(r - yrank), diag(yrank))
      m5 <- m3 + m4
      invm5 <- .ridgeinv(m5)
      m6 <- kronecker(Gamma0hat, etahat)
      temp2 <- m6 %*% tcrossprod(invm5, m6)
      covMatrix <- temp1 + temp2
      std.err <- matrix(sqrt(diag(covMatrix)), nrow = p)
    }
  }
  else{
    tmp.stenv <- xyenvlp_MU(X, Y, xrank, yrank)
    if (!is.null(Ginit)) {
      if (nrow(Ginit) != r || ncol(Ginit) != yrank) stop("The initial value should have r rows and yrank columns.")
      tmp0 <- qr.Q(qr(Ginit), complete = TRUE)
      tmp.stenv$Gammahat <- as.matrix(tmp0[, 1:yrank])
      tmp.stenv$Gamma0hat <- as.matrix(tmp0[, (yrank+1):r])
    }

    if (!is.null(Pinit)) {
      if (nrow(Pinit) != p || ncol(Pinit) != xrank) stop("The initial value should have p rows and xrank columns.")
      tmp0 <- qr.Q(qr(Pinit), complete = TRUE)
      tmp.stenv$Phihat <- as.matrix(tmp0[, 1:xrank])
      tmp.stenv$Phi0hat <- as.matrix(tmp0[, (xrank+1):p])
    }

    Gammahat <- tmp.stenv$Gammahat
    Gamma0hat <- tmp.stenv$Gamma0hat
    Phihat <- tmp.stenv$Phihat
    Phi0hat <- tmp.stenv$Phi0hat
    Deltahat <- crossprod(Phihat, SigmaX) %*% Phihat
    Delta0hat <- crossprod(Phi0hat, SigmaX) %*% Phi0hat
    invdel <- .ridgeinv(Deltahat)
    et1 <- t(Phihat) %*% crossprod(SigmaYX, Gammahat)
    etahat <- invdel %*% et1
    om1 <- SigmaYX %*% Phihat
    om2 <- om1 %*% tcrossprod(invdel, om1)
    om3 <- SigmaY - om2
    Omegahat <- crossprod(Gammahat, om3) %*% Gammahat
    Omega0hat <- crossprod(Gamma0hat, SigmaY) %*% Gamma0hat
    betahat <- Phihat %*% tcrossprod(etahat, Gammahat)
    muhat <- colMeans(Y) - crossprod(betahat, colMeans(X))
    s1 <- Gammahat %*% tcrossprod(Omegahat, Gammahat)
    SigmaYcXhat <- s1 + Gamma0hat %*% tcrossprod(Omega0hat, Gamma0hat)
    s2 <- Phihat %*% tcrossprod(Deltahat, Phihat)
    SigmaXhat <- s2 + Phi0hat %*% tcrossprod(Delta0hat, Phi0hat)
    tmp.e1 <- eigen(Omegahat)
    tmp.e2 <- eigen(Omega0hat)
    tmp.e3 <- eigen(Deltahat)
    tmp.e4 <- eigen(Delta0hat)
    invome <- .ridgeinv(Omegahat)
    invome0 <- .ridgeinv(Omega0hat)
    invdel0 <- .ridgeinv(Delta0hat)
    t3 <- crossprod(Gammahat, SigmaY) %*% Gammahat %*% invome
    peta <- Phihat %*% etahat
    t5 <- crossprod(peta, SigmaX) %*% peta %*% invome
    t6 <- t(peta) %*% crossprod(SigmaYX, Gammahat) %*% invome
    obj1 <- sum(log(tmp.e1$values)) + sum(log(tmp.e2$values))
    obj2 <- sum(log(tmp.e3$values)) + sum(log(tmp.e4$values)) + p
    obj3 <- sum(diag(t3)) + (r - yrank)
    obj4 <- sum(diag(t5)) - 2 * sum(diag(t6))
    objfun <- obj1 + obj2 + obj3 + obj4
    log_lik <- - (n/2) * objfun
    if (se == T) {
      M <- SigmaY - tcrossprod(betaOLS, SigmaYX)
      covMatrix <- kronecker(M, TauX)
      seFm <- matrix(sqrt(diag(covMatrix)), nrow = p)
      m1 <- Gammahat %*% tcrossprod(Omegahat, Gammahat)
      m2 <- Phihat %*% tcrossprod(invdel, Phihat)
      temp1 <- kronecker(m1, m2)
      d1 <- etahat %*% tcrossprod(invome, etahat)
      m3 <- kronecker(d1, Delta0hat)
      m4 <- kronecker(Deltahat, invdel0) + kronecker(invdel,Delta0hat) - 2 * kronecker(diag(xrank), diag(p - xrank))
      m5 <- m3 + m4
      invm5 <- .ridgeinv(m5)
      m6 <- kronecker(tcrossprod(Gammahat, etahat), Phi0hat)
      temp2 <- m6 %*% tcrossprod(invm5, m6)
      d2 <- crossprod(etahat, Deltahat) %*% etahat
      m8 <- kronecker(invome0, d2) + kronecker(invome0, Omegahat)
      m9 <- kronecker(Omega0hat, invome) - 2 * kronecker(diag(r - yrank), diag(yrank))
      m10 <- m8 + m9
      invm10 <- .ridgeinv(m10)
      m11 <- kronecker(Gamma0hat, Phihat %*% etahat)
      temp3 <- m11 %*% tcrossprod(invm10, m11)
      covMatrix <- temp1 + temp2 + temp3
      std.err <- matrix(sqrt(diag(covMatrix)), nrow = p)
    }
  }

  colnames(betahat) <- colnames(Y)
  rownames(betahat) <- colnames(X)
  predictors <- X %*% Phihat
  y.reduced <- Y %*% Gammahat
  fitted <- X %*% betahat
  colnames(predictors) <- paste0("SP", 1:ncol(predictors))
  colnames(Phihat) <- paste0("SP", 1:ncol(predictors))
  rownames(Phihat) <- colnames(X)
  if (se){
    std.err <- std.err * (1/sqrt(n))
    p.values <- 2*pnorm(-abs((betahat / std.err)))
  } else {
    std.err <- NULL
    p.values <- NULL
  }

  return(structure(list(coef = betahat, SigmaYcX = SigmaYcXhat, SigmaX = SigmaXhat, Gammahat = Gammahat, Gamma0 = Gamma0hat,
              eta = etahat, Omega = Omegahat, Omega0 = Omega0hat, muhat = muhat, basis = Phihat, Phi0 = Phi0hat,
              Delta = Deltahat, Delta0 = Delta0hat, log_lik = log_lik,  covMatrix = covMatrix, std.err = std.err,
              p.values = p.values, n = n, y.obs = Y, fitted = fitted, y.reduced = y.reduced, predictors = predictors), class = "sdr", model = "ENV"))
}



#' Envelope Estimation of Multivariate Means with Between-Level Heteroscedastic Error
#'
#' @description This model is for fitting a multivariate mean when there is a single categorical predictor. This is
#' similar to MANOVA.
#'
#'
#' @param formula model formula. the response must be multivariate numeric data, and the predictor a single factor.
#' @param data data frame
#' @param rank number of dimensions
#' @param se should standard errors for the regression basis be calculated? defaults to TRUE.
#' @param init an optional initial dimension reduction subspace. defaults to NULL.
#'
#' @return an sdr object
#' @export
#'
#' @references
#' Su, Z. and Cook, R. D. (2013)  Estimation of Multivariate Means with Heteroscedastic Error Using Envelope Models. Statistica Sinica, 23, 213-230.
#'
HENV <- function(formula, data, rank = "all", se = TRUE, init = NULL) {
  fit = TRUE
  levelnames = as.character(levels(model.frame(formula, data)[,2]))
  data = as.data.frame(lapply(data, as.numeric))
  X = model.matrix(formula, data)[,-1]
  if (rank == "all" || is.null(rank)){
    rank = ncol(X)
  }
  Y = model.frame(formula, data)
  Y = model.response(Y)
  Y <- as.matrix(Y)
  groupind <- unique(X)
  XX <- as.factor(X)
  X <- match(XX, levels(XX))
  XX <- as.factor(X)
  Y <- as.matrix(Y)
  a <- dim(Y)
  n <- a[1]
  r <- a[2]
  p <- nlevels(XX)
  u <- r-1
  if(rank < 0 | rank > r){
    stop("rank should be an interger between 0 and r")
  }

   ncumx <- c()
  for (i in 1 : p) {
    ncumx[i] <- length(which(XX == as.numeric(levels(XX)[i])))
  }
  ncum <- cumsum(ncumx)
  ng <- diff(c(0, ncum))
  fracN <- ng / n
  sortx <- sort(X, index.return = T)
  Xs <- sortx$x
  ind <- sortx$ix
  Ys <- Y[ind, ]
  mY <- colMeans(Y)
  sigres = invres <- list(length = p)
  mYg <- matrix(rep(0, r*p), ncol = p)
  for (i in 1 : p) {
    if(i > 1) {
      yy <- Ys[(ncum[i-1] + 1):ncum[i], ]
      sigres[[i]] <- cov(yy) * (ng[i] - 1)/ng[i]
      mYg[ , i] <- colMeans(yy)
      invres[[i]] <- .ridgeinv(sigres[[i]])
    } else {
      yy <- Ys[1:ncum[i], ]
      sigres[[i]]<- cov(yy) * (ng[i] - 1)/ng[i]
      mYg[ , i] <- colMeans(yy)
      invres[[i]] <- .ridgeinv(sigres[[i]])
    }
  }
  SigmaY <- solve(.ridgeinv(cov(Y)))  * (n - 1)/n
  eigtemY <- eigen(SigmaY)$values
  logDetSigmaY <- log(prod(eigtemY[eigtemY > 0]))
  invSigmaY <- .ridgeinv(SigmaY)
  U = M <- list(length = p)
  for (i in 1 : p){
    M[[i]] <- sigres[[i]] * (ng[i] - 1)/ng[i]
    U[[i]] <- SigmaY - M[[i]]
  }
  MU <- SigmaY
  tmp <- henvlp_MU(M, U, MU, rank, n, ng, p)
  if (!is.null(init)) {
    if (nrow(init) != r || ncol(init) != rank) stop("The initial value should have r rows and rank columns.")
    tmp0 <- qr.Q(qr(init), complete = TRUE)
    tmp$Gammahat <- as.matrix(tmp0[, 1:rank])
    tmp$Gamma0hat <- as.matrix(tmp0[, (rank+1):r])
  }
  Gammahat <- tmp$Gammahat
  Gamma0hat <- tmp$Gamma0hat
  covMatrix <- NULL
  std.err <- NULL
  ratio <- NULL
  Yfit <- NULL
  if (rank == r) {
    Sigmahat <- sigres
    muhat <- as.matrix(mY)
    mughat <- mYg
    betahat <- mughat - mY %*% matrix(1, 1, p)
    etahat <- betahat
    Omegahat <- sigres
    Omega0hat <- NULL
    log_lik <- -n * r * (1 + log(2 * pi)) / 2
    for (i in 1 : p) {
      eig <- eigen(sigres[[i]])
      log_lik <- log_lik - ng[i] * sum(log(eig$values)) / 2
    }
    if (se == T) {
      J <- matrix(0, p * r + p * r * (r + 1) / 2, p * r + p * r * (r + 1) / 2)
      for (i in 1 : (p - 1)) {
        for (j in 1 : (p - 1)) {
          aa <- (i - 1) * r + 1
          bb <- i * r
          cc <- (j - 1) * r + 1
          dd <- j * r
          J[aa : bb, cc : dd] <- (fracN[j] * fracN[i] / fracN[p] * diag(r) %*% invres[[p]])
        }
        ee <- (i - 1) * r + 1
        ff <- i * r
        J[ee : ff, ee : ff] <- (fracN[i] * diag(r) %*% invres[[i]] + fracN[i] ^ 2 / fracN[p] * diag(r) %*% invres[[p]])
      }
      for (i in 1 : p) {
        aaa <- r * (p - 1) + 1 + (i - 1) * r * (r + 1) / 2
        bbb <- r * (p - 1) + i * r * (r + 1) / 2
        J[aaa : bbb, aaa : bbb] <- (0.5 * fracN[i] * crossprod(.expan(r), kronecker(invres[[i]], invres[[i]])) %*% .expan(r))
      }
      ggg <- r + 1
      hhh <- p * r + p * r * (r + 1) / 2
      iii <- (p - 1) * r + p * r * (r + 1) / 2
      J[ggg : hhh, ggg : hhh] <- J[1 : iii, 1 : iii]
      J[1 : r, ] <- 0
      J[ggg : hhh, 1 : r] <- 0
      for (i in 1 : p) {
        J[1 : r, 1 : r] <- J[1 : r, 1 : r] + fracN[i] * diag(r) %*% invres[[i]]
      }
      for (i in 1 : (p - 1)) {
        kkk <- i * r + 1
        lll <- (i + 1) * r
        J[1 : r, kkk : lll] <- fracN[i] * (invres[[p]] - invres[[i]])
        J[kkk : lll, 1 : r] <- J[1 : r, kkk : lll]
      }
      temp <- .ridgeinv(J)
      temp1 <- kronecker(matrix(1, 1, (p - 1)), diag(r))
      ccc <- r + 1
      ddd <- r * p
      vargroup <- temp1 %*% tcrossprod(temp[ccc : ddd, ccc : ddd], temp1)
      covMatrix <- matrix(0, r * (p + 1), r * (p + 1))
      covMatrix[1 : ddd, 1 : ddd] <- temp[1 : ddd, 1 : ddd]
      eee <- r * p + 1
      fff <- r * (p + 1)
      covMatrix[eee : fff, eee : fff] <- vargroup
      for (i in 1 : (p - 1)) {
        mmm <- i * r + 1
        nnn <- (i + 1) * r
        covMatrix[eee : fff, mmm : nnn] <- (- temp1 %*% temp[ccc : ddd, mmm : nnn])
      }
      covMatrix[ccc : ddd, eee : fff] <- t(covMatrix[eee : fff, ccc : ddd])
      covMatrix[eee : fff, 1 : r] <- (- temp1 %*% temp[ccc : ddd, 1 : r])
      covMatrix[1 : r, eee : fff] <- t(covMatrix[eee : fff, 1 : r])
      seFm <- matrix(sqrt(diag(covMatrix[ccc : fff, ccc : fff])), r, p)
      std.err <- seFm
      ratio <- matrix(1, r, p)
    }
    if (fit == T) {
      Yfit <- matrix(0, n, r)
      for (i in 1:p) {
        if (i > 1) {
          Yfit[ind[(ncum[i - 1] + 1):ncum[i]], ] <- tcrossprod(matrix(1, ng[i], 1) , mughat[ , i])
        }
        else {
          Yfit[ind[1:ncum[1]], ] <- tcrossprod(matrix(1, ng[1], 1) , mughat[ , 1])
        }
      }
    }
  }
  else {
    muhat <- as.matrix(mY)
    Omega0hat <- crossprod(Gamma0hat, SigmaY) %*% Gamma0hat
    etahat = betahat = etm <- list(length = p)
    for (i in 1:p) {
      etm[[i]] <- mYg[ , i] - mY
      etahat[[i]] <- crossprod(Gammahat, etm[[i]])
      betahat[[i]] <- Gammahat %*% etahat[[i]]
    }
    Sigmahat = Omegahat = mughat = invsig <- list(length = p)
    for (i in 1:p) {
      Omegahat[[i]] <- crossprod(Gammahat, sigres[[i]]) %*% Gammahat
      Sigmahat[[i]] <- Gammahat %*% tcrossprod(Omegahat[[i]], Gammahat) + Gamma0hat %*% tcrossprod(Omega0hat, Gamma0hat)
      mughat[[i]] <- muhat + betahat[[i]]
      invsig[[i]] <- .ridgeinv(Sigmahat[[i]])
    }
    beta <- c()
    for (i in 1 : p) {
      beta <- cbind(beta, betahat[[i]])
    }
    betahat <- beta
    mug <- c()
    for (i in 1 : p) {
      mug <- cbind(mug, mughat[[i]])
    }
    mughat <- mug
    e11 <- crossprod(Gammahat, invSigmaY) %*% Gammahat
    eig2 <- eigen(e11)
    r1 <- sum(log(eig2$values))
    log_lik <- - n * r * (1 + log(2 * pi)) /2 - n * logDetSigmaY / 2 - n * r1 / 2
    for (i in 1:p) {
      e22 <- crossprod(Gammahat, sigres[[i]]) %*% Gammahat
      eig <- eigen(e22)
      log_lik <- log_lik - ng[i] * sum(log(eig$values))/2
    }
    if (se == T) {
      J <- matrix(0, p * r + p * r * (r + 1) / 2, p * r + p * r * (r + 1) / 2)
      for (i in 1 : (p - 1)) {
        for (j in 1 : (p - 1)) {
          aa <- (i - 1) * r + 1
          bb <- i * r
          cc <- (j - 1) * r + 1
          dd <- j * r
          J[aa : bb, cc : dd] <- (fracN[j] * fracN[i] / fracN[p] * diag(r) %*% invsig[[p]])
        }
        ee <- (i - 1) * r + 1
        ff <- i * r
        J[ee : ff, ee : ff] <- (fracN[i] * diag(r) %*% invsig[[i]] + fracN[i] ^ 2 / fracN[p] * diag(r) %*% invsig[[p]])
      }
      for (i in 1 : p) {
        aaa <- r * (p - 1) + 1 + (i - 1) * r * (r + 1) / 2
        bbb <- r * (p - 1) + i * r * (r + 1) / 2
        J[aaa : bbb, aaa : bbb] <- (0.5 * fracN[i] * crossprod(.expan(r), kronecker(invsig[[i]], invsig[[i]])) %*% .expan(r))
      }
      ggg <- r + 1
      hhh <- p * r + p * r * (r + 1) / 2
      iii <- (p - 1) * r + p * r * (r + 1) / 2
      J[ggg : hhh, ggg : hhh] <- J[1 : iii, 1 : iii]
      J[1 : r, ] <- 0
      J[ggg : hhh, 1 : r] <- 0
      for (i in 1 : p) {
        J[1 : r, 1 : r] <- J[1 : r, 1 : r] + fracN[i] * diag(r) %*% invsig[[i]]
      }
      for (i in 1 : (p - 1)) {
        kkk <- i * r + 1
        lll <- (i + 1) * r
        J[1 : r, kkk : lll] <- fracN[i] * (invsig[[p]] - invsig[[i]])
        J[kkk : lll, 1 : r] <- J[1 : r, kkk : lll]
      }

      J1 <- matrix(0, p * r + p * r * (r + 1) / 2, p * r + p * r * (r + 1) / 2)
      for (i in 1 : (p - 1)) {
        for (j in 1 : (p - 1)) {
          aa <- (i - 1) * r + 1
          bb <- i * r
          cc <- (j - 1) * r + 1
          dd <- j * r
          J1[aa : bb, cc : dd] <- (fracN[j] * fracN[i] / fracN[p] * diag(r) %*% invres[[p]])
        }
        ee <- (i - 1) * r + 1
        ff <- i * r
        J1[ee : ff, ee : ff] <- (fracN[i] * diag(r) %*% invres[[i]] + fracN[i] ^ 2 / fracN[p] * diag(r) %*% invres[[p]])
      }
      for (i in 1 : p) {
        aaa <- r * (p - 1) + 1 + (i - 1) * r * (r + 1) / 2
        bbb <- r * (p - 1) + i * r * (r + 1) / 2
        J1[aaa : bbb, aaa : bbb] <- (0.5 * fracN[i] * crossprod(.expan(r), kronecker(invres[[i]], invres[[i]])) %*% .expan(r))
      }
      J1[ggg : hhh, ggg : hhh] <- J[1 : iii, 1 : iii]
      J1[1 : r, ] <- 0
      J1[ggg : hhh, 1 : r] <- 0
      for (i in 1 : p) {
        J1[1 : r, 1 : r] <- J[1 : r, 1 : r] + fracN[i] * diag(r) %*% invres[[i]]
      }
      for (i in 1 : (p - 1)) {
        kkk <- i * r + 1
        lll <- (i + 1) * r
        J1[1 : r, kkk : lll] <- fracN[i] * (invres[[p]] - invres[[i]])
        J1[kkk : lll, 1 : r] <- J1[1 : r, kkk : lll]
      }
      temp1 <- .ridgeinv(J1)
      seFm <- matrix(sqrt(diag(temp1[1 : r * p, 1 : r * p])), r, p)
      aaaa <- p * r + p * r * (r + 1) / 2
      bbbb <- r + rank * (r + p - 1 - rank) + p * rank * (rank + 1) / 2 + (r - rank) * (r - rank + 1) / 2
      H <- matrix(0, aaaa, bbbb)
      for (i in 1 : (p - 1)) {
        cccc <- (i - 1) * r + 1
        dddd <- i * r
        eeee <- (i - 1) * rank + 1
        ffff <- i * rank
        gggg <- (p - 1) * rank + 1
        hhhh <- rank * (r + p - 1 - rank)
        H[cccc : dddd, eeee : ffff] <- Gammahat
        H[cccc : dddd, gggg : hhhh] <- kronecker(t(etahat[[i]]), Gamma0hat)
      }
      for (i in 1 : p) {
        iiii <- r * (p - 1) + (i - 1) * r * (r + 1) / 2 + 1
        jjjj <- r * (p - 1) + i * r * (r + 1) / 2
        kkkk <- (p - 1) * rank + 1
        llll <- rank * (r + p - 1 - rank)
        mmmm <- rank * (r + p - 1 - rank) + (i - 1) * rank * (rank + 1) / 2 + 1
        nnnn <- rank * (r + p - 1 - rank) + i * rank * (rank + 1) / 2
        oooo <- rank * (r + p - 1 - rank) + p * rank * (rank + 1) / 2 + 1
        pppp <- rank * (r + p - 1 - rank) + p * rank * (rank + 1) / 2 + (r - rank) * (r - rank + 1) / 2
        H[iiii : jjjj, kkkk : llll] <- 2 * .contr(r) %*% (kronecker(Gammahat %*% Omegahat[[i]], Gamma0hat) -kronecker(Gammahat, Gamma0hat %*% Omega0hat))
        H[iiii : jjjj, mmmm : nnnn] <- .contr(r) %*% kronecker(Gammahat, Gammahat) %*% .expan(rank)
        H[iiii : jjjj, oooo : pppp] <- .contr(r) %*% kronecker(Gamma0hat, Gamma0hat) %*% .expan(r - rank)
      }
      qqqq <- r + 1
      rrrr <- p * r + p * r * (r + 1) / 2
      ssss <- r + rank * (r + p - 1 - rank) + p * rank * (rank + 1) / 2  + (r - rank) * (r - rank + 1) / 2
      tttt <- (p - 1) * r + p * r * (r + 1) / 2
      uuuu <- rank * (r + p - 1 - rank) + p * rank * (rank + 1) / 2 + (r - rank) * (r - rank + 1) / 2
      wwww <- r * p
      xxxx <- r * p + 1
      yyyy <- r * (p + 1)
      H[qqqq : rrrr, qqqq : ssss] <- H[1 : tttt, 1 : uuuu]
      H[1 : r, qqqq : ssss] <- 0
      H[qqqq : rrrr, 1 : r] <- 0
      H[1 : r, 1 : r] <- diag(r)
      temp3 <- pseudoinverse(.ridgeinv(crossprod(H, J) %*% H),tol=1e-100)
      invtemp3 <- .ridgeinv(temp3)
      temp <- H %*% tcrossprod(invtemp3, H)
      temp2 <- kronecker(matrix(1, 1, (p - 1)), diag(r))
      vargroup <- temp2 %*% tcrossprod(temp[qqqq : wwww, qqqq : wwww], temp2)
      covMatrix <- matrix(0, yyyy, yyyy)
      covMatrix[1 : wwww, 1 : wwww] <- temp[1 : wwww, 1 : wwww]
      covMatrix[xxxx : yyyy, xxxx : yyyy] <- vargroup
      for (i in 1 : (p - 1)) {
        zzzz <- i * r + 1
        zzzzz <- (i + 1) * r
        covMatrix[xxxx : yyyy, zzzz : zzzzz] <- (- temp2 %*% temp[qqqq : wwww, zzzz : zzzzz])
      }
      covMatrix[qqqq : wwww, xxxx : yyyy] <- t(covMatrix[xxxx : yyyy, qqqq : wwww])
      covMatrix[xxxx : yyyy, 1 : r] <- (- temp2 %*% temp[qqqq : wwww, 1 : r])
      covMatrix[1 : r, xxxx : yyyy] <- t(covMatrix[xxxx : yyyy, 1 : r])
      std.err <- matrix(sqrt(diag(covMatrix[qqqq : yyyy, qqqq : yyyy])), r, p)
      ratio <- seFm / std.err
    }
    if (fit == T) {
      Yfit <- matrix(rep(0, n * r), ncol = r)
      for (i in 1:p) {
        if (i > 1) {
          Yfit[ind[(ncum[i - 1] + 1):ncum[i]], ] <- tcrossprod(matrix(1, ng[i], 1), mughat[ , i])
        }
        else {
          Yfit[ind[1:ncum[1]], ] <- tcrossprod(matrix(1, ng[1], 1), mughat[ ,1])
        }
      }
    }
  }
  colnames(Gammahat) <- paste0("SP", 1:ncol(Gammahat))
  predictors <- Y  %*% Gammahat
  colnames(predictors) <- paste0("SP", 1:ncol(predictors))
  means <- as.data.frame(mughat)
  colnames(means) <- levelnames
  rownames(Gammahat) <- rownames(means)
  colnames(betahat) <- colnames(means)
  rownames(betahat) <- rownames(means)
  if (se){
    std.err <- std.err * (1/sqrt(n))
    p.values <- 2*pnorm(-abs((betahat / std.err)))
  } else {
    std.err <- NULL
    p.values <- NULL
  }
  return(structure(list(basis = Gammahat, coef = betahat, Gamma0 = Gamma0hat,
                        Sigma = Sigmahat, eta = etahat, Omega = Omegahat,
                        Omega0 = Omega0hat, mu = muhat, means = means, log_lik = log_lik,
                        n = n, ng = ng, fitted = Yfit, predictors = predictors, covMatrix = covMatrix,
                        std.err = std.err, p.values = p.values, ratio = ratio, groupInd = groupind), class = "sdr", model = "HENV"))
}




#' Generalized Linear Envelopes for Sufficient Dimension Reduction
#'
#' @description This is an adaptation with several improvements on functions in the Renvlp package. Incorporated
#' here are methods for fitting predictor envelopes for binomial and poisson regression with Firth's biased reduced
#' estimation process, which removes the second-order bias in the maximum likelihood estimate. This also improves
#' the resilience of the estimator when there is class separation, in which case the MLE is degenerate.
#' \cr
#' @param formula model formula
#' @param data data frame
#' @param rank number of dimensions
#' @param se should standard errors for the regression basis be calculated? defaults to TRUE.
#' @param init an optional initial covariate dimension reduction subspace. defaults to NULL.
#'
#' @return an sdr object
#' @export
#'
GLENV <- function(formula, data, rank = "all",  family = c("binomial", "poisson"), se = TRUE, init = NULL){

  family <- match.arg(family)
  X = model.matrix(formula, data)[,-1]
  Y = model.frame(formula, data)
  Y = model.response(Y)
  Y <- as.matrix(Y)
  if (rank == "all" || is.null(rank)){
    rank = ncol(X)
  }
  if (family == "binomial"){
    GLENV.binom(formula, data, rank, se, init)
  }
  else if (family == "poisson"){
    GLENV.poisson(formula, data, rank,  se, init)
  }
}


GLENV.binom <- function(formula, data, rank,se = TRUE,  init = NULL) {

  X = model.matrix(formula, data)[,-1]
  Y = model.frame(formula, data)
  Y = as.matrix(as.numeric(as.factor(model.response(Y)))-1)
  a <- dim(Y)
  n <- a[1]
  r <- a[2]
  p <- ncol(X)
  if (a[1] != nrow(X))
    stop("X and Y should have the same number of observations.")
  if (rank > p || rank < 0)
    stop("rank must be an integer between 0 and p.")
  if (sum(duplicated(cbind(X, Y), MARGIN = 2)) > 0)
    stop("Some responses also appear in the predictors, or there maybe duplicated columns in X or Y.")


  SigmaY <- var(Y)*(n-1)/n
  SigmaYX <- cov(Y,X)*(n-1)/n
  SigmaX <- solve(.ridgeinv(cov(X))*((n-1)/n))
  TauY <- .ridgeinv(SigmaY)
  tmp <- binom.envlp_MU(X, Y, rank)

  if (!is.null(init)) {
    if (nrow(init) != p || ncol(init) != rank) stop("The initial value should have p rows and rank columns.")
    tmp0 <- qr.Q(qr(init), complete = TRUE)
    tmp$Gammahat <- tmp0[, 1:rank]
    tmp$Gamma0hat <- tmp0[, (rank+1):p]
    GX <- X %*% tmp$Gammahat
    fit <- cvreg:::.biasreducedglm(cbind(1,GX), as.vector(Y), family = binomial())
    tmp$muhat <- as.vector(fit$coef[1])
    tmp$etahat <- matrix(fit$coef[2 : (rank + 1)])
    beta <- tmp$Gammahat %*% tmp$etahat
    theta <- matrix(1, n, 1) %*% tmp$muhat + X %*% beta
    c.theta <- - exp(theta) / (1 + exp(theta))^2
    c.theta.mean <- mean(c.theta)
    tmp$weighthat <- c.theta / c.theta.mean
    mu1 <- exp(theta) / (1 + exp(theta))
    tmp$Vhat <- theta + ((Y - mu1) / tmp$weighthat)
    W <- diag(as.vector(tmp$weighthat))
    wx <- W %*% X
    mean.wx <- apply(wx, 2, mean)
    wxx <- X - matrix(1, nrow = n) %*% mean.wx
    Sigmawx <- crossprod(wxx, W) %*% wxx / n
    wv <- W %*% tmp$Vhat
    mean.wv <- mean(wv)
    wvv <- tmp$Vhat - matrix(1, nrow = n) %*% mean.wv
    Sigmawxv <- crossprod(wxx, W) %*% wvv / n
    Tauwx <- .ridgeinv(Sigmawx)
    tmp$avar <- Tauwx / (- c.theta.mean)
    M <- SigmaX
    MU <- SigmaX
    tmp.MU <- eigen(MU)
    invMU <- tcrossprod(sweep(tmp.MU$vectors, MARGIN = 2, 1/tmp.MU$values, "*") ,tmp.MU$vectors)
    e1 <- eigen(crossprod(tmp$Gammahat, M) %*% tmp$Gammahat)
    e2 <- eigen(crossprod(tmp$Gammahat, invMU) %*% tmp$Gammahat)
    e3 <- eigen(mpd(M))
    e4 <- matrix(1, n, 1) %*% tmp$muhat + X %*% tmp$Gammahat %*% tmp$etahat
    e5 <- crossprod(Y, e4) - colSums(log(1 + exp(e4)))
    tmp$objfun <- - n/2 * (sum(log(e1$values)) + sum(log(e2$values)) + sum(log(e3$values))) + e5
  }
  Gammahat <- tmp$Gammahat
  Gamma0hat <- tmp$Gamma0hat
  objfun <- tmp$objfun
  muhat <- tmp$muhat
  etahat <- tmp$etahat
  weighthat <- tmp$weighthat
  Vhat <- tmp$Vhat
  avarhat <- tmp$avar

  covMatrix <- NULL
  std.err <- NULL
  ratio <- NULL
  if (rank == 0) {
    Omegahat <- NULL
    Omega0hat <- SigmaX
    betahat <- matrix(0, p, 1)
    SigmaXhat <- SigmaX
    log_lik <- objfun
    if (se == T)
      ratio <- matrix(1, p, r)
  }
  else if (rank == p) {
    TauX <- .ridgeinv(SigmaX)
    Omegahat <- SigmaX
    Omega0hat <- NULL
    betahat <- etahat
    SigmaXhat <- SigmaX
    log_lik <- objfun
    if (se == T) {
      covMatrix <- avarhat
      std.err <- matrix(sqrt(diag(covMatrix)), nrow = p)
      ratio <- matrix(1, p, r)
    }
  }
  else {
    TauX <- .ridgeinv(SigmaX)
    Omegahat <- crossprod(Gammahat, SigmaX) %*% Gammahat
    Omega0hat <- crossprod(Gamma0hat, SigmaX) %*% Gamma0hat
    betahat <- Gammahat %*% etahat
    SigmaXhat <- Gammahat %*% tcrossprod(Omegahat, Gammahat) + Gamma0hat %*% tcrossprod(Omega0hat, Gamma0hat)
    betaOLS <- tcrossprod(TauX, SigmaYX)
    PGamma <- tcrossprod(Gammahat)
    log_lik <- objfun
    if (se == T) {
      options(warn = -1)
      fit <- cvreg:::.biasreducedglm(cbind(1,X), as.vector(Y), family = binomial())
      mu <- as.vector(fit$coef[1])
      beta <- as.vector(fit$coef[2 : (p + 1)])
      theta <- matrix(1, n, 1) %*% mu + X %*% beta
      c.theta <- - exp(theta) / (1 + exp(theta))^2
      c.theta.mean <- mean(c.theta)
      weight <- c.theta / c.theta.mean
      mu1 <- exp(theta) / (1 + exp(theta))
      V <- theta + ((Y - mu1) / weight)
      W <- diag(as.vector(weight))
      wx <- W %*% X
      mean.wx <- apply(wx, 2, mean)
      wxx <- X - matrix(1, nrow = n) %*% mean.wx
      Sigmawx <- crossprod(wxx, W) %*% wxx / n
      wv <- W %*% V
      mean.wv <- mean(wv)
      wvv <- V - matrix(1, nrow = n) %*% mean.wv
      Sigmawxv <- crossprod(wxx, W) %*% wvv / n
      Tauwx <- .ridgeinv(Sigmawx)
      avar <- Tauwx / (- c.theta.mean)
      covMatrix <- avar
      seFm <- matrix(sqrt(diag(covMatrix)), nrow = p)
      PGamma <- tcrossprod(Gammahat)
      t1 <- avarhat
      T1 <- PGamma %*% t1 %*% PGamma
      invt1 <- .ridgeinv(t1)
      t2 <- kronecker(etahat, t(Gamma0hat))
      invOme <- .ridgeinv(Omegahat)
      invOme0 <- .ridgeinv(Omega0hat)
      t3 <- kronecker(Omegahat, invOme0) + kronecker(invOme, Omega0hat) - 2 * kronecker(diag(rank), diag(p - rank))
      M <- t2 %*% tcrossprod(invt1, t2) + t3
      invM <- .ridgeinv(M)
      T2 <- crossprod(t2, invM) %*% t2
      covMatrix <- T1 + T2
      std.err <- matrix(sqrt(diag(covMatrix)), nrow = p)
      ratio <- seFm/std.err
    }
  }
  rownames(betahat) <- colnames(X)
  predictors <- X %*% Gammahat
  fitted <- X %*% betahat
  colnames(predictors) <- paste0("SP", 1:ncol(predictors))
  colnames(Gammahat) <- paste0("SP", 1:ncol(predictors))
  rownames(Gammahat) <- colnames(X)
  if (se){
    std.err <- std.err * (1/sqrt(n))
    p.values <- 2*pnorm(-abs((betahat / std.err)))
  } else {
    std.err <- NULL
    p.values <- NULL
  }
  return(structure(list(basis = Gammahat, coef = betahat, fitted = fitted, predictors = predictors, eta = etahat, Omega = Omegahat, SigmaX = SigmaXhat, log_lik = log_lik,  n = n, covMatrix = covMatrix, std.err = std.err, p.values = p.values, ratio = ratio, formula = formula, mf = model.frame(formula, data), y.obs = Y), class = "sdr", model = "ENV"))

}



GLENV.poisson <- function(X, Y, rank, se = TRUE, init = NULL){

  X <- as.matrix(X)
  Y <- as.matrix(Y)
  a <- dim(Y)
  n <- a[1]
  r <- a[2]
  p <- ncol(X)
  if (a[1] != nrow(X))
    stop("X and Y should have the same number of observations.")
  if (rank > p || rank < 0)
    stop("rank must be an integer between 0 and p.")
  if (sum(duplicated(cbind(X, Y), MARGIN = 2)) > 0)
    stop("Some responses also appear in the predictors, or there maybe duplicated columns in X or Y.")

  SigmaY <- var(Y)  * ((n - 1) / n)
  SigmaYX <- cov(Y, X)  * ((n - 1) / n)
  SigmaX <- solve(.ridgeinv(cov(X)))*((n-1)/n)
  TauY <- .ridgeinv(SigmaY)
  tmp <- poisson.envlp_MU(X, Y, rank)

  if (!is.null(init)) {
    if (nrow(init) != p || ncol(init) != rank) stop("The initial value should have p rows and rank columns.")
    tmp0 <- qr.Q(qr(init), complete = TRUE)
    tmp$Gammahat <- tmp0[, 1:rank]
    tmp$Gamma0hat <- tmp0[, (rank+1):p]
    GX <- X %*% tmp$Gammahat
    fit <- cvreg:::.biasreducedglm(cbind(1,GX), as.vector(Y), family = poisson())
    tmp$muhat <- as.vector(fit$coef[1])
    tmp$etahat <- matrix(fit$coef[2 : (rank + 1)])
    beta <- tmp$Gammahat %*% tmp$etahat
    theta <- matrix(1, n, 1) %*% tmp$muhat + X %*% beta
    c.theta <- - exp(theta)
    c.theta.mean <- mean(c.theta)
    tmp$weighthat <- c.theta / c.theta.mean
    tmp$Vhat <- theta + ((Y - c.theta) / tmp$weighthat)
    W <- diag(as.vector(tmp$weighthat))
    wx <- W %*% X
    mean.wx <- apply(wx, 2, mean)
    wxx <- X - matrix(1, nrow = n) %*% mean.wx
    Sigmawx <- crossprod(wxx, W) %*% wxx / n
    wv <- W %*% tmp$Vhat
    mean.wv <- mean(wv)
    wvv <- tmp$Vhat - matrix(1, nrow = n) %*% mean.wv
    Sigmawxv <- crossprod(wxx, W) %*% wvv / n
    Tauwx <- .ridgeinv(Sigmawx)
    tmp$avar <- Tauwx / (- c.theta.mean)
    M <- SigmaX
    MU <- SigmaX
    tmp.MU <- eigen(MU)
    invMU <- tcrossprod(sweep(tmp.MU$vectors, MARGIN = 2, 1/tmp.MU$values, "*") ,tmp.MU$vectors)
    e1 <- eigen(crossprod(tmp$Gammahat, M) %*% tmp$Gammahat)
    e2 <- eigen(crossprod(tmp$Gammahat, invMU) %*% tmp$Gammahat)
    e3 <- eigen(mpd(M))
    e4 <- matrix(1, n, 1) %*% tmp$muhat + X %*% tmp$Gammahat %*% tmp$etahat
    e5 <- crossprod(Y, e4) - colSums(exp(e4))
    tmp$objfun <- - n/2 * (sum(log(e1$values)) + sum(log(e2$values)) + sum(log(e3$values))) + e5
  }

  Gammahat <- tmp$Gammahat
  Gamma0hat <- tmp$Gamma0hat
  objfun <- tmp$objfun
  muhat <- tmp$muhat
  etahat <- tmp$etahat
  weighthat <- tmp$weighthat
  Vhat <- tmp$Vhat
  avarhat <- tmp$avar

  covMatrix <- NULL
  std.err <- NULL
  ratio <- NULL
  if (rank == 0) {
    Omegahat <- NULL
    Omega0hat <- SigmaX
    betahat <- matrix(0, p, 1)
    SigmaXhat <- SigmaX
    log_lik <- objfun
    if (se == T)
      ratio <- matrix(1, p, r)
  }
  else if (rank == p) {
    Omegahat <- SigmaX
    Omega0hat <- NULL
    betahat <- etahat
    SigmaXhat <- SigmaX
    log_lik <- objfun
    if (se == T) {
      covMatrix <- avarhat
      std.err <- matrix(sqrt(diag(covMatrix)), nrow = p)
      ratio <- matrix(1, p, r)
    }
  }
  else {
    TauX<- .ridgeinv(SigmaX)
    Omegahat <- crossprod(Gammahat, SigmaX) %*% Gammahat
    Omega0hat <- crossprod(Gamma0hat, SigmaX) %*% Gamma0hat
    betahat <- Gammahat %*% etahat
    SigmaXhat <- Gammahat %*% tcrossprod(Omegahat, Gammahat) + Gamma0hat %*% tcrossprod(Omega0hat, Gamma0hat)
    PGamma <- tcrossprod(Gammahat)
    log_lik <- objfun
    if (se == T) {
      options(warn = -1)
      fit <- cvreg:::.biasreducedglm(cbind(1,X), as.vector(Y), family = poisson())
      mu <- as.vector(fit$coef[1])
      beta <- as.vector(fit$coef[2 : (p + 1)])
      theta <- matrix(1, n, 1) %*% mu + X %*% beta
      c.theta <- - exp(theta)
      c.theta.mean <- mean(c.theta)
      weight <- c.theta / c.theta.mean
      V <- theta + ((Y - c.theta) / weight)
      W <- diag(as.vector(weight))
      wx <- W %*% X
      mean.wx <- apply(wx, 2, mean)
      wxx <- X - matrix(1, nrow = n) %*% mean.wx
      Sigmawx <- crossprod(wxx, W) %*% wxx / n
      wv <- W %*% V
      mean.wv <- mean(wv)
      wvv <- V - matrix(1, nrow = n) %*% mean.wv
      Sigmawxv <- crossprod(wxx, W) %*% wvv / n
      Tauwx <- .ridgeinv(Sigmawx)
      avar <- Tauwx / (- c.theta.mean)
      covMatrix <- avar
      asyFm <- matrix(sqrt(diag(covMatrix)), nrow = p)
      PGamma <- tcrossprod(Gammahat)
      t1 <- avarhat
      T1 <- PGamma %*% t1 %*% PGamma
      invt1 <- .ridgeinv(t1)
      t2 <- kronecker(etahat, t(Gamma0hat))
      invOme <- .ridgeinv(Omegahat)
      invOme0 <- .ridgeinv(Omega0hat)
      t3 <- kronecker(Omegahat, invOme0) + kronecker(invOme, Omega0hat) - 2 * kronecker(diag(rank), diag(p - rank))
      M <- t2 %*% tcrossprod(invt1, t2) + t3
      invM <- .ridgeinv(M)
      T2 <- crossprod(t2, invM) %*% t2
      covMatrix <- T1 + T2
      std.err <- matrix(sqrt(diag(covMatrix)), nrow = p)
      ratio <- asyFm/std.err
    }
  }
  rownames(betahat) <- colnames(X)
  predictors <- X %*% Gammahat
  fitted <- X %*% betahat
  colnames(predictors) <- paste0("SP", 1:ncol(predictors))
  colnames(Gammahat) <- paste0("SP", 1:ncol(predictors))
  rownames(Gammahat) <- colnames(X)
  if (se){
    std.err <- std.err * (1/sqrt(n))
    p.values <- 2*pnorm(-abs((betahat / std.err)))
  } else {
    std.err <- NULL
    p.values <- NULL
  }
  return(structure(list(basis = Gammahat, coef = betahat, fitted = fitted, predictors = predictors, eta = etahat, Omega = Omegahat, SigmaX = SigmaXhat, log_lik = log_lik, n = n, covMatrix = covMatrix, std.err = std.err, p.values = p.values, ratio = ratio, formula = formula, mf = model.frame(formula, data), y.obs = Y), class = "sdr", model = "ENV"))
}


## MU functions

envlp_MU <- function(M, U, rank) {

  dimM <- dim(M)
  dimU <- dim(U)
  r <- dimM[1]
  if (dimM[1] != dimM[2] & dimU[1] != dimU[2]) stop("M and U should be square matrices.")
  if (dimM[1] != dimU[1]) stop("M and U should have the same dimension.")
  if (rank > r & rank < 0) stop("rank should be between 0 and r.")
  if (rank == r) {
    Gammahat <- diag(r)
    Gamma0hat <- NULL
    tmp.M <- eigen(mpd(M))
    objfun <- sum(log(tmp.M$values))
  } else if (rank == 1) {
    maxiter = 250
    ftol = 0.0005
    MU <- M + U
    tmp.MU <- eigen(MU)
    invMU <- tcrossprod(sweep(tmp.MU$vectors, MARGIN = 2, 1 / tmp.MU$values, '*') , tmp.MU$vectors)
    invMU2 <- tcrossprod(sweep(tmp.MU$vectors, MARGIN = 2, 1 / sqrt(tmp.MU$values), '*') , tmp.MU$vectors)

    midmatrix <- U
    startv <- function(a) crossprod(a, midmatrix) %*% a
    tmp2.MU <- apply(tmp.MU$vectors, 2, startv)
    tmp3.MU <- sort(tmp2.MU, decreasing = TRUE, index.return = TRUE)
    init <- as.matrix(tmp.MU$vectors[, tmp3.MU$ix[1]])

    eig1 <- eigen(crossprod(init,  M) %*% init)
    eig2 <- eigen(crossprod(init, invMU) %*% init)
    obj1 <- sum(log(eig1$values)) + sum(log(eig2$values))

    midmatrix <- invMU2 %*% tcrossprod(U, invMU2)
    tmp2.MU <- apply(tmp.MU$vectors, 2, startv)
    tmp3.MU <- sort(tmp2.MU, decreasing = TRUE, index.return = TRUE)
    init.MU <- as.matrix(tmp.MU$vectors[, tmp3.MU$ix[1]])
    e1 <- eigen(crossprod(init.MU, M) %*% init.MU)
    e2 <- eigen(crossprod(init.MU, invMU) %*% init.MU)
    obj2 <- sum(log(e1$values)) + sum(log(e2$values))
    if (obj2 < obj1) {
      init <- init.MU
      obj1 <- obj2
    }

    tmp.M <- eigen(mpd(M))
    midmatrix <- U
    tmp2.M <- apply(tmp.M$vectors, 2, startv)
    tmp3.M <- sort(tmp2.M, decreasing = TRUE, index.return = TRUE)
    init.M <- as.matrix(tmp.M$vectors[, tmp3.M$ix[1]])
    e1 <- eigen(crossprod(init.M, M) %*% init.M)
    e2 <- eigen(crossprod(init.M, invMU) %*% init.M)
    obj3 <- sum(log(e1$values)) + sum(log(e2$values))
    if (obj3 < obj1) {
      init <- init.M
      obj1 <- obj3
    }

    invM2 <- tcrossprod(sweep(tmp.M$vectors, MARGIN = 2, 1 / sqrt(tmp.M$values), '*') , tmp.M$vectors)
    midmatrix <- invM2 %*% tcrossprod(U, invM2)
    tmp2.M <- apply(tmp.M$vectors, 2, startv)
    tmp3.M <- sort(tmp2.M, decreasing = TRUE, index.return = TRUE)
    init.M <- as.matrix(tmp.M$vectors[, tmp3.M$ix[1]])

    e1 <- eigen(crossprod(init.M, M) %*% init.M)
    e2 <- eigen(crossprod(init.M, invMU) %*% init.M)
    obj4 <- sum(log(e1$values)) + sum(log(e2$values))
    if (obj4 < obj1) {
      init <- init.M
      obj1 <- obj4
    }
    GEidx <- .GaussElim(init)
    Ginit <- init %*% .ridgeinv(init[GEidx[1], ])
    i <- 1
    while (i < maxiter) {
      fobj <- function(x) {
        T1 <- crossprod(x, x)
        T2 <- crossprod(x, M) %*% x
        T3 <- crossprod(x, invMU) %*% x
        -2 * log(T1) + log(T2) + log(T3)
      }

      gobj <- function(x) {
        W1 <- crossprod(x, x)
        T1 <- x / as.vector(W1)
        W2 <- crossprod(x, M) %*% x
        T2 <- M %*% x / as.vector(W2)
        W3 <- crossprod(x, invMU) %*% x
        T3 <- invMU %*% x / as.vector(W3)
        -2 * T1 + T2 + T3
      }

      res <- nlminb(Ginit, fobj, gobj)
      g <- as.matrix(res$par)
      a <- qr(g)
      Gammahat <- qr.Q(a)
      e1 <- eigen(crossprod(Gammahat, M) %*% Gammahat)
      e2 <- eigen(crossprod(Gammahat, invMU) %*% Gammahat)
      obj5 <- sum(log(e1$values)) + sum(log(e2$values))
      if (abs(obj1 - obj5) < ftol * abs(obj1)) {
        break
      } else {
        obj1 <- obj5
        i <- i + 1
      }
    }
    Gamma0hat <- qr.Q(a, complete = TRUE)[, (rank+1):r]
    objfun <- obj5 + sum(log(tmp.MU$values))
    Gammahat <- as.matrix(Gammahat)
    Gamma0hat <- as.matrix(Gamma0hat)

  } else if (rank == r - 1 & rank != 1) {

    maxiter = 250
    ftol = 0.0005
    MU <- M + U
    tmp.MU <- eigen(MU)
    invMU <- tcrossprod(sweep(tmp.MU$vectors, MARGIN = 2, 1 / tmp.MU$values, '*') , tmp.MU$vectors)
    invMU2 <- tcrossprod(sweep(tmp.MU$vectors, MARGIN = 2, 1 / sqrt(tmp.MU$values), '*') , tmp.MU$vectors)
    midmatrix <- U
    startv <- function(a) crossprod(a, midmatrix) %*% a
    tmp2.MU <- apply(tmp.MU$vectors, 2, startv)
    tmp3.MU <- sort(tmp2.MU, decreasing = TRUE, index.return = TRUE)
    init <- as.matrix(tmp.MU$vectors[, tmp3.MU$ix[1:rank]])
    eig1 <- eigen(crossprod(init, M) %*% init)
    eig2 <- eigen(crossprod(init, invMU) %*% init)
    obj1 <- sum(log(eig1$values)) + sum(log(eig2$values))
    midmatrix <- invMU2 %*% tcrossprod(U, invMU2)
    tmp2.MU <- apply(tmp.MU$vectors, 2, startv)
    tmp3.MU <- sort(tmp2.MU, decreasing = TRUE, index.return = TRUE)
    init.MU <- as.matrix(tmp.MU$vectors[, tmp3.MU$ix[1:rank]])
    e1 <- eigen(crossprod(init.MU, M) %*% init.MU)
    e2 <- eigen(crossprod(init.MU, invMU) %*% init.MU)
    obj2 <- sum(log(e1$values)) + sum(log(e2$values))
    if (obj2 < obj1) {
      init <- init.MU
      obj1 <- obj2
    }
    tmp.M <- eigen(mpd(M))
    midmatrix <- U
    tmp2.M <- apply(tmp.M$vectors, 2, startv)
    tmp3.M <- sort(tmp2.M, decreasing = TRUE, index.return = TRUE)
    init.M <- as.matrix(tmp.M$vectors[, tmp3.M$ix[1:rank]])
    e1 <- eigen(crossprod(init.M, M) %*% init.M)
    e2 <- eigen(crossprod(init.M, invMU) %*% init.M)
    obj3 <- sum(log(e1$values)) + sum(log(e2$values))
    if (obj3 < obj1) {
      init <- init.M
      obj1 <- obj3
    }

    invM2 <- tcrossprod(sweep(tmp.M$vectors, MARGIN = 2, 1 / sqrt(tmp.M$values), '*') , tmp.M$vectors)
    midmatrix <- invM2 %*% tcrossprod(U, invM2)
    tmp2.M <- apply(tmp.M$vectors, 2, startv)
    tmp3.M <- sort(tmp2.M, decreasing = TRUE, index.return = TRUE)
    init.M <- as.matrix(tmp.M$vectors[, tmp3.M$ix[1:rank]])

    e1 <- eigen(crossprod(init.M, M) %*% init.M)
    e2 <- eigen(crossprod(init.M, invMU) %*% init.M)
    obj4 <- sum(log(e1$values)) + sum(log(e2$values))
    if (obj4 < obj1) {
      init <- init.M
      obj1 <- obj4
    }

    GEidx <- .GaussElim(init)
    Ginit <- init %*% pseudoinverse(init[GEidx[1:rank], ])

    j <- GEidx[r]

    g <- as.matrix(Ginit[j, ])
    t2 <- crossprod(Ginit[-j, ], as.matrix(M[-j, j])) / M[j, j]
    t3 <- crossprod(Ginit[-j, ], as.matrix(invMU[-j, j])) / invMU[j, j]

    GUGt2 <- g + t2
    GUG <- crossprod(Ginit, (M %*% Ginit)) - tcrossprod(GUGt2, GUGt2) * M[j, j]

    GVGt2 <- g + t3
    GVG <- crossprod(Ginit, (invMU %*% Ginit)) - tcrossprod(GVGt2, GVGt2) * invMU[j, j]

    invC1 <- .ridgeinv(GUG);invC2 <- .ridgeinv(GVG)

    fobj <- function(x) {
      tmp2 <- x + t2
      tmp3 <- x + t3
      T2 <- invC1 %*% tmp2
      T3 <- invC2 %*% tmp3
      -2 * log(1 + sum(x^2)) + log(1 + M[j, j] * crossprod(tmp2, T2)) + log(1 + invMU[j, j] * crossprod(tmp3, T3))
    }

    gobj <- function(x) {
      tmp2 <- x + t2
      tmp3 <- x + t3
      T2 <- invC1 %*% tmp2
      T3 <- invC2 %*% tmp3
      -4 	* x %*% .ridgeinv(1 + sum(x^2)) + 2 * T2 / as.numeric(1 / M[j, j] + crossprod(tmp2, T2)) + 2 * T3 / as.numeric(1 / invMU[j, j] + crossprod(tmp3, T3))
    }

    i <- 1
    while (i < maxiter) {
      res <- nlminb(Ginit[j,], fobj, gobj)
      Ginit[j, ] <- res$par
      a <- qr(Ginit)
      Gammahat <- qr.Q(a)
      e1 <- eigen(crossprod(Gammahat, M) %*% Gammahat)
      e2 <- eigen(crossprod(Gammahat, invMU) %*% Gammahat)
      obj5 <- sum(log(e1$values)) + sum(log(e2$values))
      if (abs(obj1 - obj5) < ftol * abs(obj1)) {
        break
      } else {
        obj1 <- obj5
        i <- i + 1
      }
    }

    Gamma0hat <- qr.Q(a, complete = TRUE)[, (rank+1):r, drop = FALSE]
    objfun <- obj5 + sum(log(tmp.MU$values))
    Gammahat <- as.matrix(Gammahat)
    Gamma0hat <- as.matrix(Gamma0hat)

  } else {
    maxiter = 250
    ftol = 0.0005
    MU <- M + U
    tmp.MU <- eigen(MU)
    invMU <- tcrossprod(sweep(tmp.MU$vectors, MARGIN = 2, 1 / tmp.MU$values, '*') , tmp.MU$vectors)
    invMU2 <- tcrossprod(sweep(tmp.MU$vectors, MARGIN = 2, 1 / sqrt(tmp.MU$values), '*') , tmp.MU$vectors)
    midmatrix <- U
    startv <- function(a) crossprod(a, midmatrix) %*% a
    tmp2.MU <- apply(tmp.MU$vectors, 2, startv)
    tmp3.MU <- sort(tmp2.MU, decreasing = TRUE, index.return = TRUE)
    init <- as.matrix(tmp.MU$vectors[, tmp3.MU$ix[1:rank]])
    eig1 <- eigen(crossprod(init, M) %*% init)
    eig2 <- eigen(crossprod(init, invMU) %*% init)
    obj1 <- sum(log(eig1$values)) + sum(log(eig2$values))
    midmatrix <- invMU2 %*% tcrossprod(U, invMU2)
    tmp2.MU <- apply(tmp.MU$vectors, 2, startv)
    tmp3.MU <- sort(tmp2.MU, decreasing = TRUE, index.return = TRUE)
    init.MU <- as.matrix(tmp.MU$vectors[, tmp3.MU$ix[1:rank]])
    e1 <- eigen(crossprod(init.MU, M) %*% init.MU)
    e2 <- eigen(crossprod(init.MU, invMU) %*% init.MU)
    obj2 <- sum(log(e1$values)) + sum(log(e2$values))
    if (obj2 < obj1) {
      init <- init.MU
      obj1 <- obj2
    }

    tmp.M <- eigen(mpd(M))
    midmatrix <- U
    tmp2.M <- apply(tmp.M$vectors, 2, startv)
    tmp3.M <- sort(tmp2.M, decreasing = TRUE, index.return = TRUE)
    init.M <- as.matrix(tmp.M$vectors[, tmp3.M$ix[1:rank]])
    e1 <- eigen(crossprod(init.M, M) %*% init.M)
    e2 <- eigen(crossprod(init.M, invMU) %*% init.M)
    obj3 <- sum(log(e1$values)) + sum(log(e2$values))
    if (obj3 < obj1) {
      init <- init.M
      obj1 <- obj3
    }

    invM2 <- tcrossprod(sweep(tmp.M$vectors, MARGIN = 2, 1 / sqrt(tmp.M$values), '*') , tmp.M$vectors)
    midmatrix <- invM2 %*% tcrossprod(U, invM2)
    tmp2.M <- apply(tmp.M$vectors, 2, startv)
    tmp3.M <- sort(tmp2.M, decreasing = TRUE, index.return = TRUE)
    init.M <- as.matrix(tmp.M$vectors[, tmp3.M$ix[1:rank]])
    e1 <- eigen(crossprod(init.M, M) %*% init.M)
    e2 <- eigen(crossprod(init.M, invMU) %*% init.M)
    obj4 <- sum(log(e1$values)) + sum(log(e2$values))
    if (obj4 < obj1) {
      init <- init.M
      obj1 <- obj4
    }
    GEidx <- .GaussElim(init)
    Ginit <- init %*% pseudoinverse(init[GEidx[1:rank], ])
    GUG <- crossprod(Ginit, (M %*% Ginit))
    GVG <- crossprod(Ginit, (invMU %*% Ginit))
    t4 <- crossprod(Ginit[GEidx[(rank+1):r],], Ginit[GEidx[(rank+1):r], ]) + diag(rank)
    i <- 1
    while (i < maxiter) {

      for (j in GEidx[(rank+1):r]) {
        g <- as.matrix(Ginit[j, ])
        t2 <- crossprod(Ginit[-j, ], as.matrix(M[-j, j])) / M[j, j]
        t3 <- crossprod(Ginit[-j, ], as.matrix(invMU[-j, j])) / invMU[j, j]

        GUGt2 <- g + t2
        GUG <- GUG - tcrossprod(GUGt2, GUGt2) * M[j, j]
        GVGt2 <- g + t3
        GVG <- GVG - tcrossprod(GVGt2, GVGt2) * invMU[j, j]
        t4 <- t4 - tcrossprod(g, g)
        invC1 <- .ridgeinv(GUG)
        invC2 <- .ridgeinv(GVG)
        invt4 <- .ridgeinv(t4)

        fobj <- function(x) {
          tmp2 <- x + t2
          tmp3 <- x + t3
          T1 <- invt4 %*% x
          T2 <- invC1 %*% tmp2
          T3 <- invC2 %*% tmp3
          -2 * log(1 + x %*% T1) + log(1 + M[j, j] * crossprod(tmp2, T2)) + log(1 + invMU[j, j] * crossprod(tmp3, T3))
        }

        gobj <- function(x) {
          tmp2 <- x + t2
          tmp3 <- x + t3
          T1 <- invt4 %*% x
          T2 <- invC1 %*% tmp2
          T3 <- invC2 %*% tmp3
          -4 	* T1 / as.numeric(1 + x %*% T1) + 2 * T2 / as.numeric(1 / M[j, j] + crossprod(tmp2, T2)) + 2 * T3 / as.numeric(1 / invMU[j, j] + crossprod(tmp3, T3))
        }
        res <- nlminb(Ginit[j,], fobj, gobj)
        Ginit[j, ] <- res$par
        g <- as.matrix(Ginit[j, ])
        t4 <- t4 + tcrossprod(g, g)
        GUGt2 <- g + t2
        GUG <- GUG + tcrossprod(GUGt2, GUGt2) * M[j, j]
        GVGt2 <- g + t3
        GVG <- GVG + tcrossprod(GVGt2, GVGt2) * invMU[j, j]
      }
      a <- qr(Ginit)
      Gammahat <- qr.Q(a)
      e1 <- eigen(crossprod(Gammahat, M) %*% Gammahat)
      e2 <- eigen(crossprod(Gammahat, invMU) %*% Gammahat)
      obj5 <- sum(log(e1$values)) + sum(log(e2$values))
      if (abs(obj1 - obj5) < ftol * abs(obj1)) {
        break
      } else {
        obj1 <- obj5
        i <- i + 1
      }
    }
    Gamma0hat <- qr.Q(a, complete = TRUE)[, (rank+1):r]
    objfun <- obj5 + sum(log(tmp.MU$values))
    Gammahat <- as.matrix(Gammahat)
    Gamma0hat <- as.matrix(Gamma0hat)
  }
  return(list(Gammahat = Gammahat, Gamma0hat = Gamma0hat, objfun = objfun))
}



sxenvlp_MU <- function(X, Y, rank, R){

  X <- as.matrix(X)
  Y <- as.matrix(Y)
  a <- dim(Y)
  n <- a[1]
  r <- a[2]
  p <- ncol(X)
  if (a[1] != nrow(X))
    stop("X and Y should have the same number of observations.")
  if (rank > p || rank < 0)
    stop("rank must be an integer between 0 and p.")
  if (sum(duplicated(cbind(X, Y), MARGIN = 2)) > 0)
    stop("Some responses also appear in the predictors, or there maybe duplicated columns in X or Y.")

  SigmaY  <- cov(Y)
  SigmaYX <- cov(Y, X)* ((n-1)/n)
  SigmaX  <- solve(.ridgeinv(cov(X))) * ((n-1)/n)
  TauX <- .ridgeinv(SigmaX);TauY <- .ridgeinv(SigmaY);t1 <- crossprod(SigmaYX, TauY)
  U <- t1 %*% SigmaYX;M <- SigmaX - U;tmp <- envlp_MU(M, U, rank);Gamma <- tmp$Gammahat

  if (length(R) == p) {
    make_objfun <- function(SigmaYX, SigmaY, SigmaX){
      function(d, Gamma, X, Y){
        X <- as.matrix(X);Y <- as.matrix(Y);a <- dim(Y);n <- a[1];r <- a[2];p <- ncol(X)
        TauX <- .ridgeinv(SigmaX);TauY <- .ridgeinv(SigmaY)
        t1 <- crossprod(SigmaYX, TauY);U <- t1 %*% SigmaYX;M <- SigmaX - U
        d1 <- c(1, d);Lambda <- diag(d1);invLambda <- diag(1 / d1)
        m1 <- crossprod(Gamma, Lambda);eigtem1 <- eigen(m1 %*% tcrossprod(TauX, m1))
        m2 <- crossprod(Gamma, invLambda);eigtem2 <- eigen(m2 %*% tcrossprod(M, m2))
        temp1 <- sum(log(eigtem1$values));temp2 <- sum(log(eigtem2$values));objfun <- temp1 + temp2
        return(objfun)
      }
    }
    objfun <- make_objfun(SigmaYX, SigmaY, SigmaX);d.init <- rep(1, (p - 1));k2 <- rep(0, (p - 1))
    tmp.init <- Rsolnp::solnp(pars = d.init, fun = objfun, LB = k2,
                              .control = list(delta = 1e-6, tol = 0.0005, trace = 0),
                              Gamma = Gamma, X = X, Y = Y)
    d <- tmp.init$pars;obj1 <- objfun(d, Gamma, X, Y);maxiter = 250;ftol = 0.0005;i <- 1
    while (i < maxiter) {
      d1 <- c(1, d)
      L1 <- diag(d1)
      invL1 <- diag(1 / d1)
      M1 <- invL1 %*% M %*% invL1
      U1 <- invL1 %*% U %*% invL1
      tmp1 <- envlp_MU(M1, U1, rank)
      Gamma <- tmp1$Gammahat
      temp1 <- Rsolnp::solnp(pars = d, fun = objfun, LB = k2,
                             .control = list(delta = 1e-6, tol = 0.0005, trace = 0),
                             Gamma = Gamma, X = X, Y = Y)
      d <- temp1$pars
      obj2 <- objfun(d, Gamma, X, Y)
      if (abs(obj1 - obj2) < ftol) {
        break
      }
      else {
        obj1 <- obj2
        i <- i + 1
      }
    }
    Gamma0 <- tmp1$Gamma0hat
    d1 <- c(1, d)
    Lambda <- diag(d1)
  } else {

    make_objfun1 <- function(SigmaYX, SigmaY, SigmaX){
      function(d, Gamma, X, Y, R){
        X <- as.matrix(X);Y <- as.matrix(Y);a <- dim(Y);n <- a[1];r <- a[2];p <- ncol(X)
        TauX <- .ridgeinv(SigmaX);TauY <- .ridgeinv(SigmaY);t1 <- crossprod(SigmaYX, TauY)
        U <- t1 %*% SigmaYX;M <- SigmaX - U;Lambda <- diag(d);invLambda <- diag(1 / d)
        m1 <- crossprod(Gamma, Lambda);eigtem1 <- eigen(m1 %*% tcrossprod(TauX, m1))
        m2 <- crossprod(Gamma, invLambda);eigtem2 <- eigen(m2 %*% tcrossprod(M, m2))
        temp1 <- sum(log(eigtem1$values));temp2 <- sum(log(eigtem2$values));objfun <- temp1 + temp2
        return(objfun)
      }
    }

    objfun1 <- make_objfun1(SigmaYX, SigmaY, SigmaX)

    heq <- function (d, Gamma, X, Y, R){
      iter = sum(R)
      i <- 1
      cont <- NULL
      while (i < iter) {
        C <- matrix(0, (R[i] - 1), sum(R))
        for (j in 2 : (sum(R) - 1)){
          if (R[i] == j) {
            for (k in 1 : (j - 1)){
              s <- sum(R[0 : (i - 1)]) + 1
              C[k , s] <- 1
              C[k , (s + k)] <- -1
            }
          }
        }
        cont <- rbind(cont, C)
        if (i == length(R)) {
          c1 <- c(1, rep(0, (sum(R) - 1)))
          cont <- rbind(c1, cont)
          break
        } else {
          i <- i + 1
        }
      }

      g <- rep(NA, nrow(cont))
      g[1] <- d[1]
      for (m in 2 : nrow(cont)){
        g[m] <- d[which(cont[m, ] == 1)] - d[which(cont[m, ] == -1)]
      }
      g
    }
    d.init <- rep(1, p);g1 <- heq(d.init, Gamma, X, Y, R);k1 <- c(1, rep(0, length(g1) - 1));k2 <- rep(0, p)

    tmp.init <- Rsolnp::solnp(pars = d.init, fun = objfun1, eqfun = heq, eqB = k1,
                              LB = k2, .control = list(delta = 1e-6, tol = 0.0005, trace = 0),
                              R = R, Gamma = Gamma, X = X, Y = Y)

    d <- tmp.init$pars;obj1 <- objfun1(d, Gamma, X, Y, R);maxiter = 250;ftol = 0.0005;i <- 1

    while (i < maxiter) {
      L1 <- diag(d)
      invL1 <- diag(1 / d)
      M1 <- invL1 %*% M %*% invL1
      U1 <- invL1 %*% U %*% invL1
      tmp1 <- envlp_MU(M1, U1, rank)
      Gamma <- tmp1$Gammahat
      temp1 <- Rsolnp::solnp(pars = d, fun = objfun1, eqfun = heq, eqB = k1,
                             LB = k2, .control = list(delta = 1e-6, tol = 0.0005, trace = 0),
                             R = R, Gamma = Gamma, X = X, Y = Y)
      d <- temp1$pars
      obj2 <- objfun1(d, Gamma, X, Y, R)
      if (abs(obj1 - obj2) < ftol) {
        break
      }
      else {
        obj1 <- obj2
        i <- i + 1
      }
    }
    Gamma0 <- tmp1$Gamma0hat
    Lambda <- diag(d)
  }

  return(list(Gammahat = Gamma, Gamma0hat = Gamma0, Lambdahat = Lambda, objfun = obj2))
}


xyenvlp_MU <- function(X, Y, xrank, yrank){

  X <- as.matrix(X);Y <- as.matrix(Y)
  a <- dim(Y);n <- a[1];r <- a[2];p <- ncol(X)
  if (a[1] != nrow(X))
    stop("X and Y should have the same number of observations.")
  if (yrank > r || yrank < 0)
    stop("yrank must be an integer between 0 and r.")
  if (xrank > p || xrank < 0)
    stop("xrank must be an integer between 0 and p.")
  if (sum(duplicated(cbind(X, Y), MARGIN = 2)) > 0)
    stop("There appears to be duplicated columns in X and Y.")

  SigmaY <- solve(.ridgeinv(cov(Y))) * ((n-1)/n)
  SigmaYX <- cov(Y, X)* ((n-1)/n)
  SigmaX <- solve(.ridgeinv(cov(X))) * ((n-1)/n)
  TauX <- .ridgeinv(SigmaX);TauY <- .ridgeinv(SigmaY)
  betaOLS <- SigmaYX %*% TauX
  U1 <- crossprod(SigmaYX, TauY) %*% SigmaYX
  M1 <- SigmaX - U1
  tmp1 <- envlp_MU(M1, U1, xrank)
  Phi <- tmp1$Gammahat
  Phi0 <- tmp1$Gamma0hat
  E1 <- crossprod(Phi, SigmaX) %*% Phi
  invE1 <- .ridgeinv(E1)
  E2 <- SigmaYX %*% Phi
  U2 <- E2 %*% tcrossprod(invE1, E2)
  M2 <- SigmaY - U2
  tmp2 <- envlp_MU(M2, U2, yrank)
  Ga <- tmp2$Gammahat
  Ga0 <- tmp2$Gamma0hat
  m1 <- crossprod(Phi, SigmaX) %*% Phi
  m2 <- crossprod(Phi0, SigmaX) %*% Phi0
  m3 <- crossprod(Ga0, SigmaY) %*% Ga0
  m4 <- crossprod(Ga, M2) %*% Ga
  e1 <- eigen(m1);e2 <- eigen(m2);e3 <- eigen(m3);e4 <- eigen(m4)
  obj1 <- (sum(log(e1$values)) + sum(log(e2$values))  + sum(log(e3$values)) + sum(log(e4$values)))

  maxiter = 250
  ftol = 0.0005
  i <- 1
  while(i < maxiter){
    E3 <- crossprod(Ga, SigmaY) %*% Ga
    invE3 <- .ridgeinv(E3)
    E4 <- crossprod(SigmaYX, Ga)
    U3 <- E4 %*% tcrossprod(invE3, E4)
    M3 <- SigmaX - U3
    tmp3 <- envlp_MU(M3, U3, xrank)
    Phi <- tmp3$Gammahat
    Phi0 <- tmp3$Gamma0hat
    E5 <- crossprod(Phi, SigmaX) %*% Phi
    invE5 <- .ridgeinv(E5)
    E6 <- SigmaYX %*% Phi
    U4 <- E6 %*% tcrossprod(invE5, E6)
    M4 <- SigmaY - U4
    tmp4 <- envlp_MU(M4, U4, yrank)
    Ga <- tmp4$Gammahat
    Ga0 <- tmp4$Gamma0hat
    m5 <- crossprod(Phi, SigmaX) %*% Phi
    m6 <- crossprod(Phi0, SigmaX) %*% Phi0
    m7 <- crossprod(Ga0, SigmaY) %*% Ga0
    m8 <- crossprod(Ga, M4) %*% Ga
    e5 <- eigen(m5);e6 <- eigen(m6);e7 <- eigen(m7);e8 <- eigen(m8)
    obj2 <- (sum(log(e5$values)) + sum(log(e6$values)) + sum(log(e7$values)) + sum(log(e8$values)))
    if (abs(obj1 - obj2) < ftol) {
      break
    }
    else {
      obj1 <- obj2
      i <- i + 1
    }
  }
  return(list(Gammahat = Ga, Gamma0hat = Ga0,Phihat = Phi, Phi0hat = Phi0, objfun = obj2))
}




binom.envlp_MU <- function(X, Y, rank){
  X <- as.matrix(X);Y <- as.matrix(Y)
  a <- dim(Y);n <- a[1];r <- a[2];p <- ncol(X)
  if (a[1] != nrow(X))
    stop("X and Y should have the same number of observations.")
  if (rank > p || rank < 0)
    stop("rank must be an integer between 0 and r.")
  if (sum(duplicated(cbind(X, Y), MARGIN = 2)) > 0)
    stop("Some responses also appear in the predictors, or there maybe duplicated columns in X or Y.")

  options(warn = -1)
  fit <- cvreg:::.biasreducedglm(cbind(1,X), as.vector(Y), family = binomial())
  mu <- as.vector(fit$coef[1])
  beta <- as.vector(fit$coef[2 : (p + 1)])
  theta <- matrix(1, n, 1) %*% mu + X %*% beta
  c.theta <- - exp(theta) / (1 + exp(theta))^2
  c.theta.mean <- mean(c.theta)
  weight <- c.theta / c.theta.mean
  mu1 <- exp(theta) / (1 + exp(theta))
  V <- theta + ((Y - mu1) / weight)
  W <- diag(as.vector(weight))
  wx <- W %*% X
  mean.wx <- apply(wx, 2, mean)
  wxx <- X - matrix(1, nrow = n) %*% mean.wx
  Sigmawx <- crossprod(wxx, W) %*% wxx / n
  wv <- W %*% V
  mean.wv <- mean(wv)
  wvv <- V - matrix(1, nrow = n) %*% mean.wv
  Sigmawxv <- crossprod(wxx, W) %*% wvv / n
  Tauwx <- .ridgeinv(Sigmawx)

  M.init <- Tauwx / (- c.theta.mean)
  U.init <- tcrossprod(beta)
  tmp1 <- envlp_MU(M.init, U.init, rank)
  gamma.init <- tmp1$Gammahat
  gamma0.init <- tmp1$Gamma0hat

  SigmaX <- stats::cov(X) * ((n - 1) / n)
  TauX <- .ridgeinv(SigmaX)
  SigmaX <- solve(SigmaX)

  if (rank == 0) {
    Gammahat <- NULL
    Gamma0hat <- diag(r)
    tmp.M <- eigen(SigmaX)
    mu <- log(mean(Y) / (1 - mean(Y)))
    muh <- matrix(1, n, 1) %*% mu
    c1 <- log(1 + exp(muh))
    Cn1 <- crossprod(Y, muh) - colSums(c1)
    eta <- NULL
    var <- NULL
    weight <- rep(1, n)
    V <- Y
    objfun <- Cn1 - n/2 * sum(log(tmp.M$values))
  } else if (rank == p) {
    Gammahat <- diag(r)
    Gamma0hat <- NULL
    tmp.M <- eigen(SigmaX)
    eta <- as.matrix(beta)
    var <- Tauwx / (- c.theta.mean)
    c1 <- log(1 + exp(theta))
    Cn1 <- crossprod(Y, theta) - colSums(c1)
    objfun <- Cn1 - n/2 * sum(log(tmp.M$values))
  } else if (rank == p - 1) {
    maxiter = 250
    ftol = 0.0005

    M <- SigmaX
    MU <- SigmaX
    tmp.MU <- eigen(MU)
    invMU <- tcrossprod(sweep(tmp.MU$vectors, MARGIN = 2, 1/tmp.MU$values, "*"), tmp.MU$vectors)
    c1 <- log(1 + exp(theta))
    temp1 <- crossprod(Y, theta) - colSums(c1)
    e1 <- eigen(crossprod(gamma.init, M) %*% gamma.init)
    e2 <- eigen(crossprod(gamma.init, invMU) %*% gamma.init)
    e3 <- eigen(SigmaX)
    temp2 <- sum(log(e1$values)) + sum(log(e2$values)) + sum(log(e3$values))
    obj1 <- temp1 - (n / 2) * temp2
    GiX <- X %*% gamma.init
    fit <- cvreg:::.biasreducedglm(cbind(1,GiX), as.vector(Y), family = binomial())
    eta <- as.matrix(fit$coef[2 : (rank + 1)])
    init <- gamma.init
    GEidx <- .GaussElim(init)
    Ginit <- init %*% pseudoinverse(init[GEidx[1:rank], ])
    j <- GEidx[p]
    g <- as.matrix(Ginit[j, ])
    g1 <- Ginit[-j, ]
    t2 <- crossprod(g1, as.matrix(M[-j, j]))/M[j, j]
    t3 <- crossprod(g1, as.matrix(invMU[-j, j]))/invMU[j, j]
    GUGt2 <- g + t2
    GUG <- crossprod(Ginit, (M %*% Ginit)) - tcrossprod(GUGt2, GUGt2) * M[j, j]
    GVGt2 <- g + t3
    GVG <- crossprod(Ginit, (invMU %*% Ginit)) - tcrossprod(GVGt2, GVGt2) * invMU[j, j]
    invC1 <- .ridgeinv(GUG)
    invC2 <- .ridgeinv(GVG)

    X1 <- as.matrix(X[ , j])
    X2 <- as.matrix(X[ , -j])
    t5 <- tcrossprod(X1,g) %*% eta
    t6 <- matrix(1, n, 1) %*% mu + X2 %*% g1 %*% eta
    t7 <- t6 + t5
    t8 <- crossprod(Y, t7)
    et6 <- log(1 + exp(t7))
    t9 <- colSums(et6)

    fobj <- function(x) {
      tmp2 <- x + t2
      tmp3 <- x + t3
      tmp4 <- tcrossprod(X1,x) %*% eta + t6
      T2 <- invC1 %*% tmp2
      T3 <- invC2 %*% tmp3
      T4 <- log(1 + exp(tmp4))
      n /2 * (- 2 * log(1 + sum(x^2)) + log(1 + M[j, j] * crossprod(tmp2,T2)) + log(1 + invMU[j, j] * crossprod(tmp3,T3))) - t(Y) %*% tmp4 + colSums(T4)
    }

    gobj <- function(x) {
      tmp2 <- x + t2
      tmp3 <- x + t3
      tmp4 <- tcrossprod(X1,x) %*% eta + t6
      Cn <- Y - (exp(tmp4) / (1 + exp(tmp4)))
      T2 <- invC1 %*% tmp2
      T3 <- invC2 %*% tmp3
      A1 <- crossprod(g1, Sigmawx[-j, -j]) %*% g1 + as.matrix(x) %*% Sigmawx[j, -j] %*% g1 + tcrossprod(crossprod(g1, Sigmawx[-j, j]), x) + tcrossprod(x) * Sigmawx[j , j]
      invC3 <- .ridgeinv(A1)
      T5 <- tcrossprod(crossprod(X, Cn),eta)
      tmp5 <- tcrossprod(X1,x) + X2 %*% g1
      T6 <- tcrossprod(Sigmawxv, Cn) %*% tmp5 %*% invC3
      tmp6 <- as.matrix(Sigmawx[ , -j]) %*% g1 + tcrossprod(Sigmawx[ , j], x)
      T7 <- tmp6 %*% invC3 %*% tcrossprod(crossprod(tmp5, Cn), eta)
      T8 <- tmp6 %*% eta %*% crossprod(Cn, tmp5) %*% invC3
      r1 <- rep(0, p)
      r1[j] <- 1
      T9 <- crossprod(r1, (T5 + T6 - T7 - T8))
      - t(T9) + n * (- 4 * x %*% solve(1 + sum(x^2)) + 2 * T2/as.numeric(1/M[j, j] + crossprod(tmp2, T2)) + 2 * T3/as.numeric(1/invMU[j, j] + crossprod(tmp3, T3)))
    }

    i <- 1
    while (i < maxiter) {
      res <- nlminb(Ginit[j, ], fobj, gobj)
      Ginit[j, ] <- res$par
      GiX <- X %*% Ginit
      fit <- cvreg:::.biasreducedglm(cbind(1,GX), as.vector(Y), family = binomial())
      mu <- as.vector(fit$coef[1])
      eta <- as.matrix(fit$coef[2 : (rank + 1)])
      beta <- Ginit %*% eta
      theta <- matrix(1, n, 1) %*% mu + X %*% beta
      c.theta <- - exp(theta) / (1 + exp(theta))^2
      c.theta.mean <- mean(c.theta)
      weight <- c.theta / c.theta.mean
      mu1 <- exp(theta) / (1 + exp(theta))
      V <- theta + ((Y - mu1) / weight)
      W <- diag(as.vector(weight))
      wx <- W %*% X
      mean.wx <- apply(wx, 2, mean)
      wxx <- X - matrix(1, nrow = n) %*% mean.wx
      Sigmawx <- crossprod(wxx, W) %*% wxx / n
      wv <- W %*% V
      mean.wv <- mean(wv)
      wvv <- V - matrix(1, nrow = n) %*% mean.wv
      Sigmawxv <- crossprod(wxx, W) %*% wvv / n
      Tauwx <- .ridgeinv(Sigmawx)
      e1 <- eigen(crossprod(Ginit, M) %*% Ginit)
      e2 <- eigen(crossprod(Ginit, invMU) %*% Ginit)
      e3 <- eigen(crossprod(Ginit))
      e4 <- matrix(1, n, 1) %*% mu + X %*% beta
      e5 <- crossprod(Y, e4) - colSums(log(1 + exp(e4)))
      obj2 <- - n/2 * (sum(log(e1$values)) + sum(log(e2$values)) + sum(log(e3$values))) + e5
      if (abs(obj1 - obj2) < ftol) {
        break
      }
      else {
        obj1 <- obj2
        i <- i + 1
      }
    }
    a <- qr(Ginit)
    Gammahat <- qr.Q(a)
    GX <- X %*% Gammahat
    fit <- cvreg:::.biasreducedglm(cbind(1,GX), as.vector(Y), family = binomial())
    mu <- as.vector(fit$coef[1])
    eta <- matrix(fit$coef[2 : (rank + 1)])
    beta <- Gammahat %*% eta
    theta <- matrix(1, n, 1) %*% mu + X %*% beta
    c.theta <- - exp(theta) / (1 + exp(theta))^2
    c.theta.mean <- mean(c.theta)
    weight <- c.theta / c.theta.mean
    mu1 <- exp(theta) / (1 + exp(theta))
    V <- theta + ((Y - mu1) / weight)
    W <- diag(as.vector(weight))
    wx <- W %*% X
    mean.wx <- apply(wx, 2, mean)
    wxx <- X - matrix(1, nrow = n) %*% mean.wx
    Sigmawx <- crossprod(wxx, W) %*% wxx / n
    wv <- W %*% V
    mean.wv <- mean(wv)
    wvv <- V - matrix(1, nrow = n) %*% mean.wv
    Sigmawxv <- crossprod(wxx, W) %*% wvv / n
    Tauwx <- .ridgeinv(Sigmawx)
    var <- Tauwx / (- c.theta.mean)
    e1 <- eigen(crossprod(Gammahat, M) %*% Gammahat)
    e2 <- eigen(crossprod(Gammahat, invMU) %*% Gammahat)
    e3 <- eigen(mpd(M))
    e4 <- matrix(1, n, 1) %*% mu + X %*% Gammahat %*% eta
    e5 <- crossprod(Y, e4) - colSums(log(1 + exp(e4)))
    Gamma0hat <- qr.Q(a, complete = TRUE)[, (rank + 1):p, drop = FALSE]
    objfun <- - n/2 * (sum(log(e1$values)) + sum(log(e2$values)) + sum(log(e3$values))) + e5
    Gammahat <- as.matrix(Gammahat)
    Gamma0hat <- as.matrix(Gamma0hat)
  } else {
    maxiter = 250
    ftol = 0.0005
    M <- SigmaX
    MU <- SigmaX
    tmp.MU <- eigen(MU)
    invMU <- tcrossprod(sweep(tmp.MU$vectors, MARGIN = 2, 1/tmp.MU$values, "*"), tmp.MU$vectors)
    c1 <- log(1 + exp(theta))
    temp1 <- crossprod(Y, theta) - colSums(c1)
    e1 <- eigen(crossprod(gamma.init, M) %*% gamma.init)
    e2 <- eigen(crossprod(gamma.init, invMU) %*% gamma.init)
    e3 <- eigen(SigmaX)
    temp2 <- sum(log(e1$values)) + sum(log(e2$values)) + sum(log(e3$values))
    obj1 <- temp1 - (n / 2) * temp2
    options(warn = -1)
    GiX <- X %*% gamma.init
    fit <- cvreg:::.biasreducedglm(cbind(1,GiX), as.vector(Y), family = binomial())
    eta <- as.matrix(fit$coef[2 : (rank + 1)])
    init <- gamma.init
    GEidx <- .GaussElim(init)
    Ginit <- init %*% pseudoinverse(init[GEidx[1:rank], ])
    GUG <- crossprod(Ginit, (M %*% Ginit))
    GVG <- crossprod(Ginit, (invMU %*% Ginit))
    t4 <- crossprod(Ginit[GEidx[(rank + 1):p], ], Ginit[GEidx[(rank + 1):p], ]) + diag(rank)
    i <- 1
    while (i < maxiter) {
      for (j in GEidx[(rank + 1):p]) {
        g <- as.matrix(Ginit[j, ])
        g1 <- as.matrix(Ginit[-j, ])
        t2 <- crossprod(g1, as.matrix(M[-j, j]))/M[j, j]
        t3 <- crossprod(g1, as.matrix(invMU[-j, j]))/invMU[j, j]
        GUGt2 <- g + t2
        GUG <- GUG - tcrossprod(GUGt2, GUGt2) * M[j, j]
        GVGt2 <- g + t3
        GVG <- GVG - tcrossprod(GVGt2, GVGt2) * invMU[j, j]
        t4 <- t4 - tcrossprod(g, g)
        invC1 <- .ridgeinv(GUG)
        invC2 <- .ridgeinv(GVG)
        invt4 <- .ridgeinv(t4)
        X1 <- X[ , j]
        X2 <- X[ , -j]
        t5 <- tcrossprod(X1,g) %*% eta
        t6 <- matrix(1, n, 1) %*% mu + X2 %*% g1 %*% eta
        t7 <- t5 + t6
        t8 <- crossprod(Y, t7)
        et6 <- log(1 + exp(t7))
        t9 <- colSums(et6)

        fobj <- function(x) {
          tmp2 <- x + t2
          tmp3 <- x + t3
          tmp4 <- tcrossprod(X1, x) %*% eta + t6
          T1 <- invt4 %*% x
          T2 <- invC1 %*% tmp2
          T3 <- invC2 %*% tmp3
          T4 <- log(1 + exp(tmp4))
          n /2 * (-2 * log(1 + x %*% T1) + log(1 + M[j, j] * crossprod(tmp2, T2)) + log(1 + invMU[j, j] *crossprod(tmp3, T3))) - t(Y) %*% tmp4 + colSums(T4)
        }

        gobj <- function(x) {
          tmp2 <- x + t2
          tmp3 <- x + t3
          tmp4 <- tcrossprod(X1, x) %*% eta + t6
          Cn <- Y - (exp(tmp4) / (1 + exp(tmp4)))
          T1 <- invt4 %*% x
          T2 <- invC1 %*% tmp2
          T3 <- invC2 %*% tmp3
          A1 <- crossprod(g1, Sigmawx[-j, -j]) %*% g1 + as.matrix(x) %*% Sigmawx[j, -j] %*% g1 + tcrossprod(crossprod(g1, Sigmawx[-j, j]) , x) + tcrossprod(x) * Sigmawx[j , j]
          invC3 <- .ridgeinv(A1)
          T5 <-  tcrossprod(crossprod(X, Cn), eta)
          tmp5 <- tcrossprod(X1, x) + X2 %*% g1
          T6 <- tcrossprod(Sigmawxv, Cn) %*% tmp5 %*% invC3
          tmp6 <- Sigmawx[ , -j] %*% g1 + tcrossprod(Sigmawx[ , j], x)
          T7 <- tmp6 %*% invC3 %*% tcrossprod(crossprod(tmp5, Cn), eta)
          T8 <- tmp6 %*% eta %*% crossprod(Cn, tmp5) %*% invC3
          r1 <- rep(0, p)
          r1[j] <- 1
          T9 <- crossprod(r1, (T5 + T6 - T7 - T8))
          - t(T9) + n * (- 4 * T1/as.numeric(1 + x %*% T1) + 2 * T2/as.numeric(1/M[j, j] + crossprod(tmp2, T2)) + 2 * T3/as.numeric(1/invMU[j, j] + crossprod(tmp3, T3)))
        }
        res <- nlminb(Ginit[j, ], fobj, gobj)
        Ginit[j, ] <- res$par
        g <- as.matrix(Ginit[j, ])
        t4 <- t4 + tcrossprod(g, g)
        GUGt2 <- g + t2
        GUG <- GUG + tcrossprod(GUGt2, GUGt2) * M[j, j]
        GVGt2 <- g + t3
        GVG <- GVG + tcrossprod(GVGt2, GVGt2) * invMU[j, j]
      }

      GiX <- X %*% Ginit
      fit <- cvreg:::.biasreducedglm(cbind(1,GiX), as.vector(Y), family = binomial())
      mu <- as.vector(fit$coef[1])
      eta <- as.matrix(fit$coef[2 : (rank + 1)])
      beta <- Ginit %*% eta
      theta <- matrix(1, n, 1) %*% mu + X %*% beta
      c.theta <- - exp(theta) / (1 + exp(theta))^2
      c.theta.mean <- mean(c.theta)
      weight <- c.theta / c.theta.mean
      mu1 <- exp(theta) / (1 + exp(theta))
      V <- theta + ((Y - mu1) / weight)
      W <- diag(as.vector(weight))
      wx <- W %*% X
      mean.wx <- apply(wx, 2, mean)
      wxx <- X - matrix(1, nrow = n) %*% mean.wx
      Sigmawx <- crossprod(wxx, W) %*% wxx / n
      wv <- W %*% V
      mean.wv <- mean(wv)
      wvv <- V - matrix(1, nrow = n) %*% mean.wv
      Sigmawxv <- crossprod(wxx, W) %*% wvv / n
      Tauwx <- .ridgeinv(Sigmawx)
      e1 <- eigen(crossprod(Ginit, M) %*% Ginit)
      e2 <- eigen(crossprod(Ginit, invMU) %*% Ginit)
      e3 <- eigen(crossprod(Ginit))
      e4 <- matrix(1, n, 1) %*% mu + X %*% beta
      e5 <- crossprod(Y, e4) - colSums(log(1 + exp(e4)))
      obj2 <- - n/2 * (sum(log(e1$values)) + sum(log(e2$values)) + sum(log(e3$values))) + e5
      if (abs(obj1 - obj2) < ftol) {
        break
      }
      else {
        obj1 <- obj2
        i <- i + 1
      }
    }
    a <- qr(Ginit)
    Gammahat <- qr.Q(a)
    GX <- X %*% Gammahat
    fit <- cvreg:::.biasreducedglm(cbind(1,GX), as.vector(Y), family = binomial())
    mu <- as.vector(fit$coef[1])
    eta <- matrix(fit$coef[2 : (rank + 1)])
    beta <- Gammahat %*% eta
    theta <- matrix(1, n, 1) %*% mu + X %*% beta
    c.theta <- - exp(theta) / (1 + exp(theta))^2
    c.theta.mean <- mean(c.theta)
    weight <- c.theta / c.theta.mean
    mu1 <- exp(theta) / (1 + exp(theta))
    V <- theta + ((Y - mu1) / weight)
    W <- diag(as.vector(weight))
    wx <- W %*% X
    mean.wx <- apply(wx, 2, mean)
    wxx <- X - matrix(1, nrow = n) %*% mean.wx
    Sigmawx <- crossprod(wxx, W) %*% wxx / n
    wv <- W %*% V
    mean.wv <- mean(wv)
    wvv <- V - matrix(1, nrow = n) %*% mean.wv
    Sigmawxv <- crossprod(wxx, W) %*% wvv / n
    Tauwx <- .ridgeinv(Sigmawx)
    var <- Tauwx / (- c.theta.mean)
    e1 <- eigen(crossprod(Gammahat, M) %*% Gammahat)
    e2 <- eigen(crossprod(Gammahat, invMU) %*% Gammahat)
    e3 <- eigen(mpd(M))
    e4 <- matrix(1, n, 1) %*% mu + X %*% Gammahat %*% eta
    e5 <- crossprod(Y, e4) - colSums(log(1 + exp(e4)))
    Gamma0hat <- qr.Q(a, complete = TRUE)[, (rank + 1):p]
    objfun <- - n/2 * (sum(log(e1$values)) + sum(log(e2$values)) + sum(log(e3$values))) + e5
    Gammahat <- as.matrix(Gammahat)
    Gamma0hat <- as.matrix(Gamma0hat)

  }
  return(list(Gammahat = Gammahat, Gamma0hat = Gamma0hat, muhat = mu,
              etahat = eta, weighthat = weight, Vhat = V,
              avar = var, objfun = objfun))
}


poisson.envlp_MU <- function(X, Y, rank){

  X <- as.matrix(X)
  Y <- as.matrix(Y)
  a <- dim(Y)
  n <- a[1]
  r <- a[2]
  p <- ncol(X)
  if (a[1] != nrow(X))
    stop("X and Y should have the same number of observations.")
  if (rank > p || rank < 0)
    stop("rank must be an integer between 0 and r.")
  if (sum(duplicated(cbind(X, Y), MARGIN = 2)) > 0)
    stop("Some responses also appear in the predictors, or there maybe duplicated columns in X or Y.")

  options(warn = -1)
  fit <- cvreg:::.biasreducedglm(cbind(1,X), as.vector(Y), family = poisson())
  mu <- as.vector(fit$coef[1])
  beta <- as.vector(fit$coef[2 : (p + 1)])
  theta <- matrix(1, n, 1) %*% mu + X %*% beta
  c.theta <- -exp(theta)
  c.theta.mean <- mean(c.theta)
  weight <- c.theta / c.theta.mean
  V <- theta + ((Y - c.theta) / weight)
  W <- diag(as.vector(weight))
  wx <- W %*% X
  mean.wx <- apply(wx, 2, mean)
  wxx <- X - matrix(1, nrow = n) %*% mean.wx
  Sigmawx <- crossprod(wxx, W) %*% wxx / n
  wv <- W %*% V
  mean.wv <- mean(wv)
  wvv <- V - matrix(1, nrow = n) %*% mean.wv
  Sigmawxv <- crossprod(wxx, W) %*% wvv / n
  Tauwx <- .ridgeinv(Sigmawx)

  M.init <- Tauwx / (- c.theta.mean)
  U.init <- tcrossprod(beta)
  tmp1 <- envlp_MU(M.init, U.init, rank)
  gamma.init <- tmp1$Gammahat
  gamma0.init <- tmp1$Gamma0hat
  SigmaX <- solve(.ridgeinv(stats::cov(X))) * ((n-1)/n)
  TauX <- .ridgeinv(SigmaX)

  if (rank == 0) {
    Gammahat <- NULL
    Gamma0hat <- diag(r)
    tmp.M <- eigen(SigmaX)
    mu <- log(mean(Y))
    muh <- matrix(1, n, 1) %*% mu
    Cn1 <- crossprod(Y, muh) - colSums(exp(muh))
    eta <- NULL
    var <- NULL
    weight <- rep(1, n)
    V <- Y
    objfun <- Cn1 - n/2 * sum(log(tmp.M$values))
  } else if (rank == p) {
    Gammahat <- diag(r)
    Gamma0hat <- NULL
    tmp.M <- eigen(SigmaX)
    eta <- beta
    var <- Tauwx / (- c.theta.mean)
    Cn1 <- crossprod(Y, theta) - colSums(exp(theta))
    objfun <- Cn1 - n/2 * sum(log(tmp.M$values))
  } else if (rank == p - 1) {
    maxiter = 250
    ftol = 0.0005

    M <- SigmaX
    MU <- SigmaX
    tmp.MU <- eigen(MU)
    invMU <- tcrossprod(sweep(tmp.MU$vectors, MARGIN = 2, 1/tmp.MU$values, "*") ,tmp.MU$vectors)

    Cn1 <- crossprod(Y, theta) - colSums(exp(theta))
    e1 <- eigen(crossprod(gamma.init, M) %*% gamma.init)
    e2 <- eigen(crossprod(gamma.init, invMU) %*% gamma.init)
    e3 <- eigen(SigmaX)
    temp1 <- sum(log(e1$values)) + sum(log(e2$values)) + sum(log(e3$values))
    obj1 <- Cn1 - (n / 2) * temp1

    GiX <- X %*% gamma.init
    fit <- cvreg:::.biasreducedglm(cbind(1,GiX), as.vector(Y), family = poisson())
    eta <- as.matrix(fit$coef[2 : (rank + 1)])

    init <- gamma.init
    GEidx <- .GaussElim(init)
    Ginit <- init %*% pseudoinverse(init[GEidx[1:rank], ])
    j <- GEidx[p]
    g <- as.matrix(Ginit[j, ])
    g1 <- Ginit[-j, ]
    t2 <- crossprod(g1, as.matrix(M[-j, j]))/M[j, j]
    t3 <- crossprod(g1, as.matrix(invMU[-j, j]))/invMU[j, j]
    GUGt2 <- g + t2
    GUG <- crossprod(Ginit, (M %*% Ginit)) - tcrossprod(GUGt2, GUGt2) * M[j, j]
    GVGt2 <- g + t3
    GVG <- crossprod(Ginit, (invMU %*% Ginit)) - tcrossprod(GVGt2, GVGt2) * invMU[j, j]
    invC1 <- .ridgeinv(GUG)
    invC2 <- .ridgeinv(GVG)

    X1 <- X[ , j]
    X2 <- X[ , -j]
    t5 <- tcrossprod(X1,g) %*% eta
    t6 <- matrix(1, n, 1) %*% mu + X2 %*% g1 %*% eta
    t7 <- t6 + t5
    t8 <- crossprod(Y, t7)
    et6 <- exp(t7)
    t9 <- colSums(et6)

    fobj <- function(x) {
      tmp2 <- x + t2
      tmp3 <- x + t3
      tmp4 <- tcrossprod(X1, x) %*% eta + t6
      T2 <- invC1 %*% tmp2
      T3 <- invC2 %*% tmp3
      T4 <- exp(tmp4)
      n /2 * (- 2 * log(1 + sum(x^2)) + log(1 + M[j, j] * crossprod(tmp2,T2)) + log(1 + invMU[j, j] * crossprod(tmp3,T3))) - t(Y) %*% tmp4 + colSums(T4)
    }

    gobj <- function(x) {
      tmp2 <- x + t2
      tmp3 <- x + t3
      tmp4 <- tcrossprod(X1, x) %*% eta + t6
      Cn <- Y - exp(tmp4)
      T2 <- invC1 %*% tmp2
      T3 <- invC2 %*% tmp3
      A1 <- crossprod(g1, Sigmawx[-j, -j]) %*% g1 + as.matrix(x) %*% Sigmawx[j, -j] %*% g1 + tcrossprod(crossprod(g1, Sigmawx[-j, j]),x) + tcrossprod(x) * Sigmawx[j , j]
      invC3 <- .ridgeinv(A1)
      T5 <-  tcrossprod(crossprod(X, Cn), eta)
      tmp5 <- tcrossprod(X1, x) + X2 %*% g1
      T6 <- tcrossprod(Sigmawxv, Cn) %*% tmp5 %*% invC3
      tmp6 <- as.matrix(Sigmawx[ , -j]) %*% g1 + tcrossprod(Sigmawx[ , j], x)
      T7 <- tmp6 %*% invC3 %*% tcrossprod(crossprod(tmp5, Cn), eta)
      T8 <- tmp6 %*% eta %*% crossprod(Cn, tmp5) %*% invC3
      r1 <- rep(0, p)
      r1[j] <- 1
      T9 <- crossprod(r1, (T5 + T6 - T7 - T8))
      - t(T9) + n * (- 4 * x %*% .ridgeinv(1 + sum(x^2)) + 2 * T2/as.numeric(1/M[j, j] + crossprod(tmp2, T2)) + 2 * T3/as.numeric(1/invMU[j, j] + crossprod(tmp3, T3)))
    }

    i <- 1
    while (i < maxiter) {
      res <- nlminb(Ginit[j, ], fobj, gobj)
      Ginit[j, ] <- res$par

      GX <- X %*% Ginit
      fit <- cvreg:::.biasreducedglm(cbind(1,GX), as.vector(Y), family = poisson())
      mu <- as.vector(fit$coef[1])
      eta <- as.matrix(fit$coef[2 : (rank + 1)])
      beta <- Ginit %*% eta
      theta <- matrix(1, n, 1) %*% mu + X %*% beta
      c.theta <- - exp(theta)
      c.theta.mean <- mean(c.theta)
      weight <- c.theta / c.theta.mean
      V <- theta + ((Y - c.theta) / weight)
      W <- diag(as.vector(weight))
      wx <- W %*% X
      mean.wx <- apply(wx, 2, mean)
      wxx <- X - matrix(1, nrow = n) %*% mean.wx
      Sigmawx <- crossprod(wxx, W) %*% wxx / n
      wv <- W %*% V
      mean.wv <- mean(wv)
      wvv <- V - matrix(1, nrow = n) %*% mean.wv
      Sigmawxv <- crossprod(wxx, W) %*% wvv / n
      Tauwx <- .ridgeinv(Sigmawx)
      e1 <- eigen(crossprod(Ginit, M) %*% Ginit)
      e2 <- eigen(crossprod(Ginit, invMU) %*% Ginit)
      e3 <- eigen(crossprod(Ginit))
      e4 <- matrix(1, n, 1) %*% mu + X %*% beta
      e5 <- crossprod(Y, e4) - colSums(exp(e4))
      obj2 <- - n/2 * (sum(log(e1$values)) + sum(log(e2$values)) + sum(log(e3$values))) + e5
      if (abs(obj1 - obj2) < ftol) {
        break
      }
      else {
        obj1 <- obj2
        i <- i + 1
      }
    }
    a <- qr(Ginit)
    Gammahat <- qr.Q(a)
    GX <- X %*% Gammahat
    fit <- cvreg:::.biasreducedglm(cbind(1,X), as.vector(Y), family = poisson())
    mu <- as.vector(fit$coef[1])
    eta <- matrix(fit$coef[2 : (rank + 1)])
    beta <- Gammahat %*% eta
    theta <- matrix(1, n, 1) %*% mu + X %*% beta
    theta <- matrix(1, n, 1) %*% mu + X %*% beta
    c.theta <- - exp(theta)
    c.theta.mean <- mean(c.theta)
    weight <- c.theta / c.theta.mean
    V <- theta + ((Y - c.theta) / weight)
    W <- diag(as.vector(weight))
    wx <- W %*% X
    mean.wx <- apply(wx, 2, mean)
    wxx <- X - matrix(1, nrow = n) %*% mean.wx
    Sigmawx <- crossprod(wxx, W) %*% wxx / n
    wv <- W %*% V
    mean.wv <- mean(wv)
    wvv <- V - matrix(1, nrow = n) %*% mean.wv
    Sigmawxv <- crossprod(wxx, W) %*% wvv / n
    Tauwx <- .ridgeinv(Sigmawx)
    var <- Tauwx / (- c.theta.mean)
    e1 <- eigen(crossprod(Gammahat, M) %*% Gammahat)
    e2 <- eigen(crossprod(Gammahat, invMU) %*% Gammahat)
    e3 <- eigen(mpd(M))
    e4 <- matrix(1, n, 1) %*% mu + X %*% Gammahat %*% eta
    e5 <- crossprod(Y, e4) - colSums(exp(e4))
    Gamma0hat <- qr.Q(a, complete = TRUE)[, (rank + 1):r, drop = FALSE]
    objfun <- - n/2 * (sum(log(e1$values)) + sum(log(e2$values)) + sum(log(e3$values))) + e5
    Gammahat <- as.matrix(Gammahat)
    Gamma0hat <- as.matrix(Gamma0hat)

  } else {
    maxiter = 250
    ftol = 0.0005

    M <- SigmaX
    MU <- SigmaX
    tmp.MU <- eigen(MU)
    invMU <- tcrossprod(sweep(tmp.MU$vectors, MARGIN = 2, 1/tmp.MU$values, "*") ,tmp.MU$vectors)

    Cn1 <- crossprod(Y, theta) - colSums(exp(theta))
    e1 <- eigen(crossprod(gamma.init, M) %*% gamma.init)
    e2 <- eigen(crossprod(gamma.init, invMU) %*% gamma.init)
    e3 <- eigen(SigmaX)
    temp1 <- sum(log(e1$values)) + sum(log(e2$values)) + sum(log(e3$values))
    obj1 <- Cn1 - (n / 2) * temp1

    GiX <- X %*% gamma.init
    fit <- cvreg:::.biasreducedglm(cbind(1,GiX), as.vector(Y), family = poisson())
    eta <- as.matrix(fit$coef[2 : (rank + 1)])

    init <- gamma.init
    GEidx <- .GaussElim(init)
    Ginit <- init %*% pseudoinverse(init[GEidx[1:rank], ])
    GUG <- crossprod(Ginit, (M %*% Ginit))
    GVG <- crossprod(Ginit, (invMU %*% Ginit))
    t4 <- crossprod(Ginit[GEidx[(rank + 1):p], ], Ginit[GEidx[(rank + 1):p], ]) + diag(rank)

    i <- 1
    while (i < maxiter) {
      for (j in GEidx[(rank + 1):p]) {
        g <- as.matrix(Ginit[j, ])
        g1 <- as.matrix(Ginit[-j, ])
        t2 <- crossprod(g1, as.matrix(M[-j, j]))/M[j, j]
        t3 <- crossprod(g1, as.matrix(invMU[-j, j]))/invMU[j, j]
        GUGt2 <- g + t2
        GUG <- GUG - tcrossprod(GUGt2, GUGt2) * M[j, j]
        GVGt2 <- g + t3
        GVG <- GVG - tcrossprod(GVGt2, GVGt2) * invMU[j, j]
        t4 <- t4 - tcrossprod(g)
        invC1 <-.ridgeinv(GUG)
        invC2 <-.ridgeinv(GVG)
        invt4 <-.ridgeinv(t4)

        X1 <- X[ , j]
        X2 <- X[ , -j]
        t5 <- tcrossprod(X1,g) %*% eta
        t6 <- matrix(1, n, 1) %*% mu + X2 %*% g1 %*% eta
        t7 <- t5 + t6
        t8 <- crossprod(Y, t7)
        et7 <- exp(t7)
        t9 <- colSums(et7)

        fobj <- function(x) {
          tmp2 <- x + t2
          tmp3 <- x + t3
          tmp4 <- tcrossprod(X1, x) %*% eta + t6
          T1 <- invt4 %*% x
          T2 <- invC1 %*% tmp2
          T3 <- invC2 %*% tmp3
          T4 <- exp(tmp4)
          n /2 * (-2 * log(1 + x %*% T1) + log(1 + M[j, j] * crossprod(tmp2, T2)) + log(1 + invMU[j, j] *crossprod(tmp3, T3))) - t(Y) %*% tmp4 + colSums(T4)
        }

        gobj <- function(x) {
          tmp2 <- x + t2
          tmp3 <- x + t3
          tmp4 <- tcrossprod(X1, x) %*% eta + t6
          Cn <- Y - exp(tmp4)
          T1 <- invt4 %*% x
          T2 <- invC1 %*% tmp2
          T3 <- invC2 %*% tmp3
          A1 <- crossprod(g1, Sigmawx[-j, -j]) %*% g1 + as.matrix(x) %*% Sigmawx[j, -j] %*% g1 + tcrossprod(crossprod(g1, Sigmawx[-j, j]), x) + tcrossprod(x) * Sigmawx[j , j]
          invC3 <-.ridgeinv(A1)
          T5 <-  tcrossprod(crossprod(X, Cn), eta)
          tmp5 <- tcrossprod(X1, x) + X2 %*% g1
          T6 <- tcrossprod(Sigmawxv, Cn) %*% tmp5 %*% invC3
          tmp6 <- as.matrix(Sigmawx[ , -j]) %*% g1 + tcrossprod(Sigmawx[ , j], x)
          T7 <- tmp6 %*% invC3 %*% tcrossprod(crossprod(tmp5, Cn), eta)
          T8 <- tmp6 %*% eta %*% crossprod(Cn, tmp5) %*% invC3
          r1 <- rep(0, p)
          r1[j] <- 1
          T9 <- crossprod(r1, (T5 + T6 - T7 - T8))
          - t(T9) + n * (- 4 * T1/as.numeric(1 + x %*% T1) + 2 * T2/as.numeric(1/M[j, j] + crossprod(tmp2, T2)) + 2 * T3/as.numeric(1/invMU[j, j] + crossprod(tmp3, T3)))
        }
        res <- nlminb(Ginit[j, ], fobj, gobj)
        Ginit[j, ] <- res$par
        g <- as.matrix(Ginit[j, ])
        t4 <- t4 + tcrossprod(g, g)
        GUGt2 <- g + t2
        GUG <- GUG + tcrossprod(GUGt2, GUGt2) * M[j, j]
        GVGt2 <- g + t3
        GVG <- GVG + tcrossprod(GVGt2, GVGt2) * invMU[j, j]
      }

      GX <- X %*% Ginit
      fit <-  cvreg:::.biasreducedglm(cbind(1,GX), as.vector(Y), family = poisson())
      mu <- as.vector(fit$coef[1])
      eta <- as.matrix(fit$coef[2 : (rank + 1)])
      beta <- Ginit %*% eta
      theta <- matrix(1, n, 1) %*% mu + X %*% beta
      c.theta <- - exp(theta)
      c.theta.mean <- mean(c.theta)
      weight <- c.theta / c.theta.mean
      V <- theta + ((Y - c.theta) / weight)
      W <- diag(as.vector(weight))
      wx <- W %*% X
      mean.wx <- apply(wx, 2, mean)
      wxx <- X - matrix(1, nrow = n) %*% mean.wx
      Sigmawx <- crossprod(wxx, W) %*% wxx / n
      wv <- W %*% V
      mean.wv <- mean(wv)
      wvv <- V - matrix(1, nrow = n) %*% mean.wv
      Sigmawxv <- crossprod(wxx, W) %*% wvv / n
      Tauwx <-.ridgeinv(Sigmawx)

      e1 <- eigen(crossprod(Ginit, M) %*% Ginit)
      e2 <- eigen(crossprod(Ginit, invMU) %*% Ginit)
      e3 <- eigen(crossprod(Ginit))
      e4 <- matrix(1, n, 1) %*% mu + X %*% beta
      e5 <- crossprod(Y, e4) - colSums(exp(e4))
      obj2 <- - n/2 * (sum(log(e1$values)) + sum(log(e2$values)) + sum(log(e3$values))) + e5
      if (abs(obj1 - obj2) < ftol) {
        break
      }
      else {
        obj1 <- obj2
        i <- i + 1
      }
    }
  }
  a <- qr(Ginit)
  Gammahat <- qr.Q(a)
  GX <- X %*% Gammahat
  fit <-  cvreg:::.biasreducedglm(cbind(1,GX), as.vector(Y), family = poisson())
  mu <- as.vector(fit$coef[1])
  eta <- matrix(fit$coef[2 : (rank + 1)])
  beta <- Gammahat %*% eta
  theta <- matrix(1, n, 1) %*% mu + X %*% beta
  c.theta <- - exp(theta)
  c.theta.mean <- mean(c.theta)
  weight <- c.theta / c.theta.mean
  V <- theta + ((Y - c.theta) / weight)
  W <- diag(as.vector(weight))
  wx <- W %*% X
  mean.wx <- apply(wx, 2, mean)
  wxx <- X - matrix(1, nrow = n) %*% mean.wx
  Sigmawx <- crossprod(wxx, W) %*% wxx / n
  wv <- W %*% V
  mean.wv <- mean(wv)
  wvv <- V - matrix(1, nrow = n) %*% mean.wv
  Sigmawxv <- crossprod(wxx, W) %*% wvv / n
  Tauwx <- .ridgeinv(Sigmawx)
  var <- Tauwx / (- c.theta.mean)
  e1 <- eigen(crossprod(Gammahat, M) %*% Gammahat)
  e2 <- eigen(crossprod(Gammahat, invMU) %*% Gammahat)
  e3 <- eigen(mpd(M))
  e4 <- matrix(1, n, 1) %*% mu + X %*% Gammahat %*% eta
  e5 <- crossprod(Y, e4) - colSums(exp(e4))
  Gamma0hat <- qr.Q(a, complete = TRUE)[, (rank + 1):p]
  objfun <- - n/2 * (sum(log(e1$values)) + sum(log(e2$values)) + sum(log(e3$values))) + e5
  Gammahat <- as.matrix(Gammahat)
  Gamma0hat <- as.matrix(Gamma0hat)

  return(list(Gammahat = Gammahat, Gamma0hat = Gamma0hat, muhat = mu,
              etahat = eta, weighthat = weight, Vhat = V,
              avar = var, objfun = objfun))
}


henvlp_MU<- function(M, U, MU, rank, n, ng, L){
  p <- L
  dimM <- dim(M[[1]])
  dimU <- dim(U[[1]])
  r <- dimM[1]
  auxinit <- function(M, U, MU, rank, ng, n, p){
    tmp.MU <- eigen(MU)
    invMU <- tcrossprod(sweep(tmp.MU$vectors, MARGIN = 2, 1 / tmp.MU$values, '*'), tmp.MU$vectors)
    invMU2 <- tcrossprod(sweep(tmp.MU$vectors, MARGIN = 2, 1 / sqrt(tmp.MU$values), '*') , tmp.MU$vectors)
    midmatrix <- invMU2
    startv4 <- function(a) crossprod(a,midmatrix) %*% a
    tmp2.MU <- apply(tmp.MU$vectors, 2, startv4)
    tmp3.MU <- sort(tmp2.MU, decreasing = TRUE, index.return = TRUE)
    init <- as.matrix(tmp.MU$vectors[, tmp3.MU$ix[1:rank]])
    obj1a <- c()
    for(i in 1:p){
      eig1 <- eigen(crossprod(init, M[[i]]) %*% init)
      eig2 <- eigen(crossprod(init, invMU) %*% init)
      obj1a[i] <- sum(log(eig1$values))*ng[i]/n + sum(log(eig2$values))
    }
    obj1 <- sum(obj1a)
    return(list(init = init, obj1 = obj1, invMU = invMU))
  }
  auxf1 <- function(M1, U1, rank, n, ng, p, init, x, r){
    M <- M1
    U <- U1
    MU <- M + U
    tmp.MU <- eigen(MU)
    invMU <- tcrossprod(sweep(tmp.MU$vectors, MARGIN = 2, 1 / tmp.MU$values, '*') , tmp.MU$vectors)
    GEidx <- .GaussElim(init)
    Ginit <- init %*% pseudoinverse(init[GEidx[1:rank], ])
    j <- GEidx[r]
    g <- as.matrix(Ginit[j, ])
    t2 <- crossprod(Ginit[-j, ], as.matrix(M[-j, j])) / M[j, j]
    t3 <- crossprod(Ginit[-j, ], as.matrix(invMU[-j, j])) / invMU[j, j]
    GUGt2 <- g + t2
    GUG <- crossprod(Ginit, (M %*% Ginit)) - tcrossprod(GUGt2, GUGt2) * M[j, j]
    GVGt2 <- g + t3
    GVG <- crossprod(Ginit, (invMU %*% Ginit)) - tcrossprod(GVGt2, GVGt2) * invMU[j, j]
    invc1 <- .ridgeinv(GUG)
    invc2 <- .ridgeinv(GVG)
    tmp2 <- x + t2
    tmp3 <- x + t3
    T2 <- invc1 %*% tmp2
    T3 <- invc2 %*% tmp3
    out <- ng * log(1 + M[j, j] * crossprod(tmp2, T2))/n + log(1 + invMU[j, j] * crossprod(tmp3, T3))/p
    return(out)
  }

  auxf2 <- function(M1, U1, t2, t3, invc1, invc2, ng, n, p, x, j){
    M <- M1
    U <- U1
    MU <- M + U
    tmp.MU <- eigen(MU)
    invMU <- tcrossprod(sweep(tmp.MU$vectors, MARGIN = 2, 1 / tmp.MU$values, '*') , tmp.MU$vectors)
    tmp2 <- x + t2
    tmp3 <- x + t3
    T2 <- invc1 %*% tmp2
    T3 <- invc2 %*% tmp3
    out <- ng * log(1 + M[j, j] * crossprod(tmp2, T2))/n + log(1 + invMU[j, j] * crossprod(tmp3, T3))/p
    return(out)
  }

  auxg1 <- function(M1, U1, rank, n, ng, p, init, x, r){
    M <- M1
    U <- U1
    MU <- M + U
    tmp.MU <- eigen(MU)
    invMU <- tcrossprod(sweep(tmp.MU$vectors, MARGIN = 2, 1 / tmp.MU$values, '*') , tmp.MU$vectors)
    GEidx <- .GaussElim(init)
    Ginit <- init %*% pseudoinverse(init[GEidx[1 : rank], ])
    j <- GEidx[r]
    g <- as.matrix(Ginit[j, ])
    t2 <- crossprod(Ginit[-j, ], as.matrix(M[-j, j])) / M[j, j]
    t3 <- crossprod(Ginit[-j, ], as.matrix(invMU[-j, j])) / invMU[j, j]
    GUGt2 <- g + t2
    GUG <- crossprod(Ginit, (M %*% Ginit)) - tcrossprod(GUGt2, GUGt2) * M[j, j]
    GVGt2 <- g + t3
    GVG <- crossprod(Ginit, (invMU %*% Ginit)) - tcrossprod(GVGt2, GVGt2) * invMU[j, j]
    invC1 <- .ridgeinv(GUG)
    invC2 <- .ridgeinv(GVG)
    tmp2 <- x + t2
    tmp3 <- x + t3
    T2 <- invC1 %*% tmp2
    T3 <- invC2 %*% tmp3
    out <-  2 * ng * T2 / (n * as.numeric(1 / M[j, j] + crossprod(tmp2, T2))) + 2 * T3 /(p * as.numeric(1 / invMU[j, j] + crossprod(tmp3, T3)))
    return(out)
  }

  auxg2 <- function(M1, U1, t2, t3, invc1, invc2, ng, n, p, x, j){
    M <- M1
    U <- U1
    MU <- M + U
    tmp.MU <- eigen(MU)
    invMU <- tcrossprod(sweep(tmp.MU$vectors, MARGIN = 2,  1 / tmp.MU$values, '*') , tmp.MU$vectors)
    tmp2 <- x + t2
    tmp3 <- x + t3
    T2 <- invc1 %*% tmp2
    T3 <- invc2 %*% tmp3
    out <-  2 * T2 * ng / (n * as.numeric(1 / M[j, j] + crossprod(tmp2, T2))) + 2 * T3 / (p * as.numeric(1 / invMU[j, j] + crossprod(tmp3, T3)))
    return(out)

  }
  if(rank == 0){
    Gammahat <- NULL
    Gamma0hat <- diag(r)
  }else if (rank == r){
    Gammahat <- diag(r)
    Gamma0hat <- NULL
  }else if (rank == r - 1){
    maxiter = 250
    ftol = 0.001
    initout <- auxinit(M, U, MU, rank, ng, n, p)
    init <- initout$init
    obj1 <- initout$obj1
    invMU <- initout$invMU
    GEidx <- .GaussElim(init)
    Ginit <- init %*% pseudoinverse(init[GEidx[1 : rank], ])
    j <- GEidx[r]
    fobj <- function(x) {
      res <- -2 * log(1 + sum(x^2))
      for(i in 1:p){
        res <- res + auxf1(M[[i]], U[[i]], rank, n, ng[i], p, init, x, r)
      }
      return(res)
    }
    gobj <- function(x) {
      res <- -4 * x %*% solve(1 + sum(x^2))
      for(i in 1:p){
        res <- res + auxg1(M[[i]], U[[i]], rank, n, ng[i], p, init, x, r)
      }
      return(res)
    }
    i <- 1
    while (i < maxiter) {
      if (rank == 1){
        res <- nlminb(Ginit[j,], fn = fobj, gr = gobj)
      } else {
        res <- nlminb(Ginit[j,], fn = fobj, gr = gobj)
      }
      Ginit[j, ] <- res$par
      a <- qr(Ginit)
      Gammahat <- qr.Q(a)
      obj5a <- c()
      for(i in 1:p){
        e1 <- eigen(crossprod(Gammahat, (M[[i]] %*% Gammahat)))
        e2 <- eigen(crossprod(Gammahat, (invMU %*% Gammahat)))
        obj5a[i] <- sum(log(e1$values))*ng[i]/n + sum(log(e2$values))/p
      }
      obj5 <- sum(obj5a)
      if (abs(obj1 - obj5) < ftol * abs(obj1)) {
        break
      } else {
        obj1 <- obj5
        i <- i + 1
      }
    }
    Gamma0hat <- qr.Q(a, complete = TRUE)[, (rank + 1) : r, drop = FALSE]
    Gammahat <- as.matrix(Gammahat)
    Gamma0hat <- as.matrix(Gamma0hat)
  }else{
    maxiter <- 250
    ftol <- 0.001
    initout <- auxinit(M, U, MU, rank, ng, n, p)
    init <- initout$init
    obj1 <- initout$obj1
    GEidx <- .GaussElim(init)
    Ginit <- init %*% pseudoinverse(init[GEidx[1 : rank], ])
    GUG = GVG <- list(length = p)
    for (k in 1 : p){
      MU <- M[[k]] + U[[k]]
      tmp.MU <- eigen(MU)
      invMU <- tcrossprod(sweep(tmp.MU$vectors, MARGIN = 2, 1 / tmp.MU$values, '*') , tmp.MU$vectors)
      GUG[[k]] <- crossprod(Ginit, (M[[k]] %*% Ginit))
      GVG[[k]] <- crossprod(Ginit, (invMU %*% Ginit))
    }
    t4 <- crossprod(Ginit[GEidx[(rank + 1):r],], Ginit[GEidx[(rank + 1):r], ]) + diag(rank)
    i <- 1
    while (i < maxiter) {
      for (j in GEidx[(rank+1):r]) {
        g <- as.matrix(Ginit[j, ])
        t4 <- t4 - tcrossprod(g, g)
        invt4 <- .ridgeinv(t4)
        t2 = t3 = GUGt2 = GVGt2 = invc1 = invc2 <- list(length=p)
        for(k in 1:p){
          Maux <- M[[k]]
          MU <- M[[k]] + U[[k]]
          tmp.MU <- eigen(MU)
          invMU <- tcrossprod(sweep(tmp.MU$vectors, MARGIN = 2, 1 / tmp.MU$values, '*'),tmp.MU$vectors)
          t2[[k]] <- crossprod(Ginit[-j, ], as.matrix(Maux[-j, j])) / Maux[j, j]
          t3[[k]] <- crossprod(Ginit[-j, ], as.matrix(invMU[-j, j])) / invMU[j, j]
          GUGt2[[k]] <- g + t2[[k]]
          GUG[[k]] <- GUG[[k]] - tcrossprod(GUGt2[[k]], GUGt2[[k]]) * Maux[j, j]
          GVGt2[[k]] <- g + t3[[k]]
          GVG[[k]] <- GVG[[k]] - tcrossprod(GVGt2[[k]], GVGt2[[k]]) * invMU[j, j]
          invc1[[k]] <- .ridgeinv(GUG[[k]])
          invc2[[k]] <- .ridgeinv(GVG[[k]])
        }
        fobj <- function(x) {
          res <- -2 * log(1 + x %*% invt4 %*% x)
          for(k in 1:p){
            res <- res + auxf2(M[[k]], U[[k]], t2[[k]], t3[[k]], invc1[[k]], invc2[[k]], ng[k], n, p, x, j)
          }
          return(res)
        }
        gobj <- function(x) {
          res <- -4	* invt4 %*% x / as.numeric(1 + x %*% invt4 %*% x)
          for(k in 1:p){
            res <- res + auxg2(M[[k]], U[[k]], t2[[k]], t3[[k]], invc1[[k]], invc2[[k]], ng[k], n, p, x, j)
          }
          return(res)
        }
        if (rank == 1){
          res <- nlminb(Ginit[j,], fn = fobj, gr = gobj)
        } else{
          res <- nlminb(Ginit[j,], fn = fobj, gr = gobj)
        }
        Ginit[j, ] <- res$par
        g <- as.matrix(Ginit[j, ])
        t4 <- t4 + tcrossprod(g, g)
        for(k in 1:p){
          Maux <- M[[k]]
          MU <- M[[k]] + U[[k]]
          tmp.MU <- eigen(MU)
          invMU <- tcrossprod(sweep(tmp.MU$vectors, MARGIN = 2, 1 / tmp.MU$values, '*'), tmp.MU$vectors)
          GUGt2[[k]] <- g + t2[[k]]
          GUG[[k]] <- GUG[[k]] + tcrossprod(GUGt2[[k]], GUGt2[[k]]) * Maux[j, j]

          GVGt2[[k]] <- g + t3[[k]]
          GVG[[k]] <- GVG[[k]] + tcrossprod(GVGt2[[k]], GVGt2[[k]]) * invMU[j, j]
        }
      }
      a <- qr(Ginit)
      Gammahat <- qr.Q(a)
      obj5a <- c()
      for(i in 1 : p){
        e1 <- eigen(crossprod(Gammahat, (M[[i]] %*% Gammahat)))
        e2 <- eigen(crossprod(Gammahat, (invMU %*% Gammahat)))
        obj5a[i] <- sum(log(e1$values))*ng[i]/n + sum(log(e2$values))/p
      }
      obj5 <- sum(obj5a)
      if (abs(obj1 - obj5) < ftol * abs(obj1)) {
        break
      } else {
        obj1 <- obj5
        i <- i + 1
      }
    }
    Gamma0hat <- qr.Q(a, complete = TRUE)[, (rank + 1) : r]
    Gammahat <- as.matrix(Gammahat)
    Gamma0hat <- as.matrix(Gamma0hat)
  }
  return(list(Gammahat = Gammahat, Gamma0hat = Gamma0hat))
}


.GaussElim <- function(A) {
  a <- dim(A);n <- a[1];p <- a[2];idx <- rep(0, p);res.idx <- 1:n;i <- 1
  while (i <= p) {
    tmp <- max(abs(A[res.idx, i]))
    Stmp <- setdiff(which(abs(A[, i]) == tmp), idx)
    idx[i] <- Stmp[1]
    res.idx <- setdiff(res.idx, idx[i])
    for (j in 1:(n-i)) {
      A[res.idx[j], ] <- A[res.idx[j], ] - A[res.idx[j], i] / A[idx[i], i] * A[idx[i], ]
    }
    i <- i + 1
  }
  c(idx, res.idx)
}

.ridgeinv <- function(m){
  cvreg:::ridgepow(m, -1)
}

.expan <- function(d) {
  E <- matrix(0, d^2,d*(d+1)/2)
  for (i in 1:d) {
    for (j in 1:d) {
      if (j >= i) {
        E[d*(i-1)+j,(2*d-i)/2*(i-1)+j] <- 1
      } else {
        E[d*(i-1)+ j,(2*d-j)/2*(j-1)+i] <- 1
      }
    }
  }
  return(E)
}
.contr <- function(d) {
  C <- matrix(0, d * (d + 1) / 2, d ^ 2)
  for (i in 1:d) {
    for (j in 1:d) {
      if (j == i) {
        C[(2*d-i)/2*(i-1)+j,d*(i-1)+j] <- 1
      } else if (j > i) {
        C[(2*d-i)/2*(i-1)+j,d*(i-1)+j] <- 0.50
      } else {
        C[(2*d-j)/2*(j-1)+i,d*(i-1)+j] <- 0.50
      }
    }
  }
  return(C)
}
abnormally-distributed/cvreg documentation built on May 3, 2020, 3:45 p.m.