R/auxiliary_functions.R

Defines functions unit_lik_all extract_theta f_obj unit_lik_0 unit_lik unit_lk local_units IPF linkla liderla fit_ag fiscoxLC Omega TraSeM_unit TraSeM linklac cubic liderlac fiscoVT votlan_unit votlan model_building Imod_matrix to_two_row_matrix_constraint to_matrix_constraint preprocessing test_p_real test_real test_n text2number to_matrix tests_inputs

## Auxiliary functions

## function tests_inputs ##
tests_inputs <- function(argg){

  # Tests numeric data
  x <- as.matrix(argg$X)
  y <- as.matrix(argg$Y)
  if (nrow(x) != nrow(y))
    stop('The number of units (rows) is different in arguments "X" and "Y".')
  if (min(x,y) < 0L) stop('Negative values are not allowed in arguments "X" and "Y".')

  # Test census.changes
  if (!(argg$census.changes %in% c("adjust1", "adjust2")))
    stop("The value set for argument 'census.changes' is invalid. Only 'adjust1' and 'adjust2' are allowed.")

  # Test local
  if (!(argg$local %in% c("none", "IPF", "lik")))
    stop("The value set for argument 'local' is invalid. Only 'none', 'IPF' and 'lik' are allowed.")

  # Test stability.par
  if (argg$stability.par < 0)
    stop("The argument 'stability.par' must be non-negative.")

  # Test confidence
  if(argg$confidence >= 1L | argg$confidence <= 0L)
    stop('Only values in the interval (0, 1) are allowed for the "confidence" argument.')

  # Test max.iter
  if(argg$max.iter < 0 | abs(argg$max.iter - round(argg$max.iter)) > 0)
    stop('Only positive integers are allowed for the "max.iter" argument.')

  # Test covariates
  covars <- argg$covariates
  if(!is.null(argg$covariates)){
    if (length(covars) != 2L) {
      stop('The object used as argument "covariates" does not have the proper structure. It should have two components.')
    }
    covars[[2L]] <- to_matrix(covars[[2L]])
    if (nrow(covars[[1L]]) != nrow(x)) {
      stop('The object used as argument "covariates" does not have the proper structure. The number of units for which covariates are defined is different to the number of units (rows) in arguments "X" and "Y".')
    }
    if (ncol(covars[[2L]]) != 3L) {
      stop('The object used as argument "covariates" does not have the proper structure. The matrix of metadata "meta" should have three columns.')
    }
    if (nrow(covars[[2L]]) < 1L) {
      stop('The object used as argument "covariates" does not have the proper structure. The matrix of metadata "meta" should have at least a row.')
    }
    n.covariate <- text2number(colnames(covars[[1L]]), covars[[2L]][, 3L])
    if (sum(is.na(n.covariate)) > 0L){
      stop('The object used as argument "covariates" does not have the proper structure. At least a name declared in "meta" is not in "covar".')
    } else {
      covars[[2L]][, 3L] <- n.covariate
    }
    n.rows <- text2number(colnames(x), covars[[2L]][, 1L])
    if (sum(is.na(n.rows)) > 0L){
      stop('The object used as argument "covariates" does not have the proper structure. At least a name declared in "meta" is not in "X".')
    } else {
      covars[[2L]][, 1L] <- n.rows
    }
    n.cols <- text2number(colnames(y), covars[[2L]][, 2L])
    if (sum(is.na(n.cols)) > 0L){
      stop('The object used as argument "covariates" does not have the proper structure. At least a name declared in "meta" is not in "Y".')
    } else {
      covars[[2L]][, 2L] <- n.cols
    }
    covars[[2L]] <- to_matrix(suppressWarnings(apply(covars[[2L]], 2, as.numeric)))
    invalid <- test_n(ncol(x), covars[[2L]][, 1L]) + test_n(ncol(y), covars[[2L]][, 2L]) +
      test_n(ncol(covars[[1L]]), covars[[2L]][, 3L])
    if (invalid > 0L)
      stop('The object used as argument "covariates" is not properly defined. At least a number included in "meta" is invalid.')
  }


  # Test null.cells
  null.cells <- to_matrix(argg$null.cells)
  if(!is.null(null.cells)){
    if (ncol(null.cells) != 2L) {
      stop('The object used as argument "null.cells" does not have the proper structure. It should have two columns.')
    }
    if (nrow(null.cells) < 1L) {
      stop('The object used as argument "null.cells" does not have the proper structure.')
    }
    n.rows <- text2number(colnames(x), null.cells[, 1L])
    if (sum(is.na(n.rows)) > 0L){
      stop('The object used as argument "null.cells" is not properly defined. At least a name declared is not in "X".')
    } else {
      null.cells[, 1L] <- n.rows
    }
    n.cols <- text2number(colnames(y), null.cells[, 2L])
    if (sum(is.na(n.cols)) > 0L){
      stop('The object used as argument "null.cells" is not properly defined. At least a name declared is not in "Y".')
    } else {
      null.cells[, 2L] <- n.cols
    }
    null.cells <- to_matrix(suppressWarnings(apply(null.cells, 2L, as.numeric)))
    invalid <- test_n(ncol(x), null.cells[, 1L]) + test_n(ncol(y) - 1L, null.cells[, 2L])
    if (invalid > 0L)
      stop('The object used as argument "null.cells" is not properly defined. At least a number included is invalid.')
  }


  # Test row.cells.relationships
  row.cells.relationships <- to_matrix(argg$row.cells.relationships)
  if(!is.null(row.cells.relationships)){
    if (ncol(row.cells.relationships) != 4L) {
      stop('The object used as argument "row.cells.relationships" does not have the proper structure. It should have four columns.')
    }
    if (nrow(row.cells.relationships) < 1L) {
      stop('The object used as argument "row.cells.relationships" does not have the proper structure.')
    }
    n.rows <- text2number(colnames(x), row.cells.relationships[, 1L])
    if (sum(is.na(n.rows)) > 0L){
      stop('The object used as argument "row.cells.relationships" is not properly defined. At least a name declared is not in "X".')
    } else {
      row.cells.relationships[, 1L] <- n.rows
    }
    n.cols <- text2number(colnames(y), row.cells.relationships[, 2L])
    if (sum(is.na(n.cols)) > 0L){
      stop('The object used as argument "row.cells.relationships" is not properly defined. At least a name declared is not in "Y".')
    } else {
      row.cells.relationships[, 2L] <- n.cols
    }
    n.cols <- text2number(colnames(y), row.cells.relationships[, 3L])
    if (sum(is.na(n.cols)) > 0L){
      stop('The object used as argument "row.cells.relationships" is not properly defined. At least a name declared is not in "Y".')
    } else {
      row.cells.relationships[, 3L] <- n.cols
    }
    row.cells.relationships <- to_matrix(suppressWarnings(apply(row.cells.relationships, 2L, as.numeric)))
    invalid <- test_n(ncol(x), row.cells.relationships[, 1L]) + test_n(ncol(y) - 1L, row.cells.relationships[, 2L]) +
      test_n(ncol(y) - 1L, row.cells.relationships[, 3L]) + test_p_real(row.cells.relationships[, 4L])
    if (invalid > 0L)
      stop('The object used as argument "row.cells.relationships" is not properly defined. At least a number included is invalid.')
  }


  # Test row.cells.relationships.C
  row.cells.relationships.C <- to_matrix(argg$row.cells.relationships.C)
  if(!is.null(row.cells.relationships.C)){
    if (ncol(row.cells.relationships.C) != 3L) {
      stop('The object used as argument "row.cells.relationships.C" does not have the proper structure. It should have three columns.')
    }
    if (nrow(row.cells.relationships.C) < 1L) {
      stop('The object used as argument "row.cells.relationships.C" does not have the proper structure.')
    }
    n.rows <- text2number(colnames(x), row.cells.relationships.C[, 1L])
    if (sum(is.na(n.rows)) > 0L){
      stop('The object used as argument "row.cells.relationships.C" is not properly defined. At least a name declared is not in "X".')
    } else {
      row.cells.relationships.C[, 1L] <- n.rows
    }
    n.cols <- text2number(colnames(y), row.cells.relationships.C[, 2L])
    if (sum(is.na(n.cols)) > 0L){
      stop('The object used as argument "row.cells.relationships.C" is not properly defined. At least a name declared is not in "Y".')
    } else {
      row.cells.relationships.C[, 2L] <- n.cols
    }
    row.cells.relationships.C <- to_matrix(suppressWarnings(apply(row.cells.relationships.C, 2, as.numeric)))
    invalid <- test_n(ncol(x), row.cells.relationships.C[, 1L]) + test_n(ncol(y) - 1L, row.cells.relationships.C[, 2L]) +
      test_p_real(row.cells.relationships.C[, 3L])
    if (invalid > 0L)
      stop('The object used as argument "row.cells.relationships.C" is not properly defined. At least a number included is invalid.')
  }


  # Test pair.cells.relationships
  pair.cells.relationships  <- to_matrix(argg$pair.cells.relationships)
  if(!is.null(pair.cells.relationships)){
    if (ncol(pair.cells.relationships) != 7L) {
      stop('The object used as argument "pair.cells.relationships" does not have the proper structure. It should have seven columns.')
    }
    if (nrow(pair.cells.relationships) < 1L) {
      stop('The object used as argument "pair.cells.relationships" does not have the proper structure.')
    }
    n.rows <- text2number(colnames(x), pair.cells.relationships[, 1L])
    if (sum(is.na(n.rows)) > 0L){
      stop('The object used as argument "pair.cells.relationships" is not properly defined. At least a name declared is not in "X".')
    } else {
      pair.cells.relationships[, 1L] <- n.rows
    }
    n.cols <- text2number(colnames(y), pair.cells.relationships[, 2L])
    if (sum(is.na(n.cols)) > 0L){
      stop('The object used as argument "pair.cells.relationships" is not properly defined. At least a name declared is not in "Y".')
    } else {
      pair.cells.relationships[, 2L] <- n.cols
    }
    n.cols <- text2number(colnames(y), pair.cells.relationships[, 3L])
    if (sum(is.na(n.cols)) > 0L){
      stop('The object used as argument "pair.cells.relationships" is not properly defined. At least a name declared is not in "Y".')
    } else {
      pair.cells.relationships[, 3L] <- n.cols
    }
    n.rows <- text2number(colnames(x), pair.cells.relationships[, 4L])
    if (sum(is.na(n.rows)) > 0L){
      stop('The object used as argument "pair.cells.relationships" is not properly defined At least a name declared is not in "X".')
    } else {
      pair.cells.relationships[, 4L] <- n.rows
    }
    n.cols <- text2number(colnames(y), pair.cells.relationships[, 5L])
    if (sum(is.na(n.cols)) > 0L){
      stop('The object used as argument "pair.cells.relationships" is not properly defined. At least a name declared is not in "Y".')
    } else {
      pair.cells.relationships[, 5L] <- n.cols
    }
    n.cols <- text2number(colnames(y), pair.cells.relationships[, 6L])
    if (sum(is.na(n.cols)) > 0L){
      stop('The object used as argument "pair.cells.relationships" is not properly defined. At least a name declared is not in "Y".')
    } else {
      pair.cells.relationships[, 6L] <- n.cols
    }
    pair.cells.relationships <- to_matrix(suppressWarnings(apply(pair.cells.relationships, 2, as.numeric)))
    invalid <- test_n(ncol(x), pair.cells.relationships[, 1L]) + test_n(ncol(y), pair.cells.relationships[, 2L]) +
      test_n(ncol(y), pair.cells.relationships[, 3L]) + test_n(ncol(x), pair.cells.relationships[, 4L]) +
      test_n(ncol(y), pair.cells.relationships[, 5L]) + test_n(ncol(y), pair.cells.relationships[, 6L]) +
      test_p_real(pair.cells.relationships[, 7L])
    if (invalid > 0L)
      stop('The object used as argument "pair.cells.relationships" is not properly defined. At least a number included is invalid.')
  }

  # Test cells.fixed.logit
  cells.fixed.logit <- to_matrix(argg$cells.fixed.logit)
  if(!is.null(cells.fixed.logit)){
    if (ncol(cells.fixed.logit) != 3L) {
      stop('The object used as argument "cells.fixed.logit" does not have the proper structure. It should have three columns.')
    }
    if (nrow(cells.fixed.logit) < 1L) {
      stop('The object used as argument "cells.fixed.logit" does not have the proper structure.')
    }
    n.rows <- text2number(colnames(x), cells.fixed.logit[, 1L])
    if (sum(is.na(n.rows)) > 0L){
      stop('The object used as argument "cells.fixed.logit" is not properly defined. At least a name declared is not in "X".')
    } else {
      cells.fixed.logit[, 1L] <- n.rows
    }
    n.cols <- text2number(colnames(y), cells.fixed.logit[, 2L])
    if (sum(is.na(n.cols)) > 0L){
      stop('The object used as argument "cells.fixed.logit" is not properly defined. At least a name declared is not in "Y".')
    } else {
      cells.fixed.logit[, 2L] <- n.cols
    }
    cells.fixed.logit <- to_matrix(suppressWarnings(apply(cells.fixed.logit, 2, as.numeric)))
    invalid <- test_n(ncol(x), cells.fixed.logit[, 1L]) + test_n(ncol(y), cells.fixed.logit[, 2L]) +
      test_real(row.cells.relationships.C[, 3L])
    if (invalid > 0L)
      stop('The object used as argument "cells.fixed.logit" is not properly defined. At least a number included is invalid.')

  }


  # Test dispersion.rows
  dispersion.rows <- to_matrix(argg$dispersion.rows)
  if(!is.null(dispersion.rows)){
    if (ncol(dispersion.rows) != 2L) {
      stop('The object used as argument "dispersion.rows" does not have the proper structure. It should have two columns.')
    }
    if (nrow(dispersion.rows) < 1L) {
      stop('The object used as argument "dispersion.rows" does not have the proper structure.')
    }
    n.rows <- text2number(colnames(x), dispersion.rows[, 1L])
    if (sum(is.na(n.rows)) > 0L){
      stop('The object used as argument "dispersion.rows" is not properly defined. At least a name declared is not in "X".')
    } else {
      dispersion.rows[, 1L] <- n.rows
    }
    n.cols <- text2number(colnames(x), dispersion.rows[, 2L])
    if (sum(is.na(n.cols)) > 0L){
      stop('The object used as argument "dispersion.rows" is not properly defined. At least a name declared is not in "Y".')
    } else {
      dispersion.rows[, 2L] <- n.cols
    }
    dispersion.rows <- to_matrix(suppressWarnings(apply(dispersion.rows, 2, as.numeric)))
    invalid <- test_n(ncol(x), dispersion.rows[, 1L]) + test_n(ncol(x), dispersion.rows[, 2L])
    if (invalid > 0L)
      stop('The object used as argument "dispersion.rows" is not properly defined. At least a number included is invalid.')
    dispersion.rows <- t(apply(dispersion.rows, 1L, sort))
    dispersion.rows <- dispersion.rows[(dispersion.rows[, 2L] - dispersion.rows[, 1L]) != 0L, ]
  }

  return(list("x" = x, "y" = y, "covars" = covars,
              "null.cells" = null.cells,
              "row.cells.relationships" = row.cells.relationships,
              "row.cells.relationships.C" = row.cells.relationships.C,
              "pair.cells.relationships" = pair.cells.relationships,
              "cells.fixed.logit" = cells.fixed.logit,
              "dispersion.rows" = dispersion.rows)
  )
}

##------------------------------------------------------------------------------

## function to_matrix ##
to_matrix <- function(x){
  if(is.null(x)){
    return(x)
  } else {
    x <- as.matrix(x)
    if(ncol(x) == 1L) x <- t(x)
    return(x)
  }
}
##------------------------------------------------------------------------------

## function text2number ##
# This function transform in numbers the definition of variables or options introduced by the user.
text2number <- function(vector1, vector2){
  vector2.t <- suppressWarnings(as.numeric(vector2))
  vector2.t[is.na(vector2.t)] <- match(vector2, vector1)[is.na(vector2.t)]
  return(vector2.t)
}
##------------------------------------------------------------------------------

## functions test_rows, test_columns, test_numbers ##
test_n <- function(n, x){
  out <- sum(!(x %in% 1L:n))
  return(out)
}

test_real <- function(x){
  out <- sum(is.na(is.numeric(x)))
  return(out)
}

test_p_real <- function(x){
  out <- sum(is.na(x < 0L)) + sum(x < 0L & !is.na(x))
  return(out)
}
##------------------------------------------------------------------------------

## function preprocessing() ##
# This function selects stable units and adjust census changes
preprocessing <- function(X0, Y0, stable.units, stability.par, census.changes){
  if(stable.units){
    r <- abs(log(rowSums(Y0) / rowSums(X0)))
    units.selected <- which(r <= stability.par) # = in case zero be selected
    N <- X0[units.selected, ]
    Y <- Y0[units.selected, ]
    # plot(r) # Maybe this should be parametrized!!! and decided if plotted
  } else{
    units.selected <- 1:nrow(X0)
    N <- X0
    Y <- Y0
  }

  # Redimension and re-scale
  if(census.changes == "adjust1"){
    dn <- rowSums(N)
    dy <- rowSums(Y)
    N <- dy / dn * N
  } else if (census.changes == "adjust2"){
    dn <- rowSums(N)
    dy <- rowSums(Y)
    Y <- dn / dy * Y
  }

  output <- list("N" = N, "Y" = Y, "units.selected" = units.selected)
  return(output)
}
##------------------------------------------------------------------------------

## Function to_matrix_constraint() ##
# This function is an auxiliary function for converting the constraint objects into proper matrices
to_matrix_constraint <- function(x){
  output <- x
  if (is.null(x)){
    output <- matrix(0L, nrow = 0L, ncol = 0L)
  } else if (is.null(nrow(x))){
    output <- matrix(x, nrow = 1L)
  }
  return(output)
}
##------------------------------------------------------------------------------

## Function to_two_row_matrix_constraint() ##
# This function is an auxiliary function for converting type 5 constraints into a proper matrix where each constraint takes 4 cells on two consecutive rows of the matrix
to_two_row_matrix_constraint <- function(x){
  if (is.null(x)){
    output <- matrix(0, nrow = 0L, ncol = 0L)
  } else if (is.null(nrow(x))){
    output <- matrix(c(x[1L:3L], x[7L], x[4L:6L], 1L),
                     nrow = 2L, byrow = TRUE)
  } else {
    nf <- nrow(x)
    output <- matrix(1L, nrow = 2L*nf, ncol = 4L)
    for (ff in 1L:nf){
      output[2L*(ff - 1L) + 1L, 1L:3L] <- x[ff, 1L:3L]
      output[2L*(ff - 1L) + 1L, 4L] <- x[ff, 7L]
      output[2L*ff, 1L:3L] <- x[ff, 4L:6L]
    }
  }
  return(output)
}
##------------------------------------------------------------------------------

## Function Imod_matrix() ##
# This functions performs the work relating creating the Imod matrix to handle constraints and covariates
Imod_matrix <- function(I, covariates,
                        null.cells,
                        null.cells.C = NULL,
                        row.cells.relationships,
                        row.cells.relationships.C,
                        pair.cells.relationships,
                        cells.fixed.logit,
                        dispersion.rows,
                        dispersion.row = NULL,
                        dispersion.fixed = NULL){

  # Constraints and covariates

  ########################################################
  # Definition of the auxilar Imod matrix
  # constraints: conventions for Imod
  #     %- cod, row, col1, col2, log(a).
  #     %-> 0: no restriction           => [0 0 0 0 0]
  #     %-> 1: p(ij) = 0                => [1 i j 0 0]
  #     %-> 2: p(iJ) = 0                => [2 i j 0 0] -> j=new ref cat # NOT ACTIVE
  #     %-> 3: p(ik)=a*p(ij)            => [3 i j k log(a)]
  #     %-> 4: p(ij) = a p(iJ)          => [4 i j 0 log(a)]
  #     %-> 5: p(hj)/p(hk) = a p(im)/p(in)
  #         %                           => [5 h j k log(a)
  #         %                           => [5 i m n 0]
  #     %-> 6: la(i) = la(h)            => [6 0 i h 0]
  #     %-> 7: la(i)=0                  => [7 i 0 0 0] # NOT INCLUDED
  #     %-> 8: covariate dicotomicche   => [8 i j 1 0 #1=code
  #         %  o continue che agiscono      8 h k 1 0 #2=row
  #         %  su certi sottogruppi di      8 l m 1 0 #3=col
  #         %  caselle, per "group"         ......... #4=group
  #         %  si intende la colonna        8 r s 2 0
  #         %  della matrice C delle        8 t v 2 0
  #         %  covariate                    .........
  #     %-> 9: fix logit of cell i, j => [9 i j 0 logit ]
  #     %->10: fix la(i)            => [10 i 0 0 valor] # NOT INCLUDED


  # Definition of the auxiliary matrix Imod
  Imod <- NULL

  # 1: cells constrained to be 0
  # Ize <- null.cells
  null.cells <- to_matrix_constraint(null.cells)
  ir <- nrow(null.cells)
  if (ir > 0L) {
    Imod <- rbind(
      Imod,
      cbind(rep(1L, ir), null.cells, matrix(0L, nrow = ir, ncol = 2L))
    )
  }

  # 2: Reference option constrained to be 0: NOT ACTIVE
  # Inz <- null.cells.C
  null.cells.C <- to_matrix_constraint(null.cells.C)
  ir <- nrow(null.cells.C)
  if (ir > 0L) {
    Imod <- rbind(
      Imod,
      cbind(rep(2L, ir), null.cells.C, matrix(0L, nrow = ir, ncol = 2L))
    )
  }

  # 3: constrain relationships between cells in same row
  Icacb <- to_matrix_constraint(row.cells.relationships)
  ir <- nrow(Icacb )
  if (ir > 0L) {
    Icacb[, 4L] <- log(Icacb[, 4L])
    Imod <- rbind(
      Imod,
      cbind(rep(3L, ir), Icacb)
    )
  }

  # 4: constrain relationships with last column reference
  Iac <- to_matrix_constraint(row.cells.relationships.C)
  ir <- nrow(Iac)
  if (ir > 0) {
    Iac <- cbind(Iac, log(Iac[, 3L]))
    Iac[, 3L] <- 0L
    Imod <- rbind(
      Imod,
      cbind(rep(4L, ir), Iac)
    )
  }

  # 5: constraints among 4 cells on two different rows
  Irarb <- to_two_row_matrix_constraint(pair.cells.relationships)
  ir <- nrow(Irarb)
  if (ir > 0) {
    Irarb[, 4L] <- log(Irarb[, 4L])
    Imod <- rbind(
      Imod,
      cbind(rep(5L, ir), Irarb)
    )
  }

  # 9: fixed the logit of selected cells
  IcaL <- to_matrix_constraint(cells.fixed.logit)
  ir <- nrow(IcaL)
  if (ir > 0L) {
    IcaL <- cbind(IcaL, IcaL[, 3L])
    IcaL[, 3L] <- 0L
    Imod <- rbind(
      Imod,
      cbind(rep(9L, ir), IcaL)
    )
  }

  # 6: constant over-dispersions
  Overd <- to_matrix_constraint(dispersion.rows)
  ir <- nrow(Overd)
  if (ir > 0L) {
    Overd <- cbind(rep(6L, ir), rep(0L, ir), Overd, rep(0L, ir))
    Imod <- rbind(
      Imod,
      Overd
    )
  }

  # Covariates
  Ico <- to_matrix_constraint(covariates[[2L]])
  ir <- nrow(Ico)
  if (ir > 0L) {
    Ico <- cbind(Ico, 1L:ir, matrix(0, nrow = ir, ncol = 1))
    Ico <- Ico[, -3L, drop = FALSE]
    Imod <- rbind(
      Imod,
      cbind(rep(8L, ir), Ico)
    )
  }

  return(Imod)

}
##------------------------------------------------------------------------------

#### Function model_building
model_building <- function(N, Y, Imod, start, cvar, cs, seed = NULL) {

  # Preliminaries
  I <- ncol(N)
  J <- ncol(Y)
  Jm <- ncol(Y) - 1L
  Ym <- Y[, 1L:Jm]
  IJm <- I * Jm
  off <- rep(0L, IJm + I)
  X <- diag(IJm + I)
  Z <- matrix(0L, nrow = IJm + I, ncol = 0L)
  ibet <- c()
  if(!is.null(seed)) set.seed(seed)

  if (is.null(start)) {
    # Initial estimates for beta
    La <- rep(1L, I)/100
    P <- kronecker(rep(1L, I), t(colSums(Y) / sum(Y))) +
      matrix(stats::runif(I*J, 0L, 1L), nrow = I, ncol = J) / 50
    P <- P * (P > 10^(-8)) + 10^(-8) * (P <= 10^(-8))
    beta <- log(P[, 1L:Jm]/P[, J])
    beta <- c(t(beta), log(La / (1L - La)))
    beta <- beta + stats::rnorm(IJm + I) / 50
  } else {
    beta <- start
  }

  if(!is.null(Imod)){
    # 1: a transition set to 0
    Im <- which(Imod[, 1L] == 1L)
    ni <- length(Im)
    if (ni > 0L){
      Im <- Imod[Im, , drop = FALSE]
      for (i in 1L:ni) {
        hh <- (Im[i, 2L] - 1L) * Jm + Im[i, 3L]
        ibet <- c(ibet, hh)
        off[hh] <- -20
      }
    }

    # 2: a reference set to 0 ## NOT ACTIVE
    Im <- which(Imod[, 1L] == 2L)
    ni <- length(Im)
    if (ni > 0L){
      Im <- Imod[Im, , drop = FALSE]
      for (i in 1L:ni) {
        h1 <- (Im[i, 2L] - 1L) * Jm
        h2 <- (h1 + 1L):(h1 + Jm)
        h1 <- h1 + Im[i, 3L]
        off[h2] <- off[h2] + 20L
        ibet <- c(ibet, h1)
      }
    }

    # 3:  p(ik) = a*p(ij)
    Im <- which(Imod[, 1L] == 3L)
    ni <- length(Im)
    if (ni > 0L) {
      Im <- Imod[Im, , drop = FALSE]
      for (i in 1L:ni) {
        h1 <- (Im[i, 2L] - 1L) * Jm + Im[i, 3L]
        h2 <- (Im[i, 2L] - 1L) * Jm + Im[i, 4L]
        X[, h1] <- X[, h1] + X[, h2]
        ibet <- c(ibet, h2)
        off[h2] <- Im[i, 5L]
      }
    }

    # 4:  p(ij) = a*p(iJ)
    Im <- which(Imod[, 1L] == 4L)
    ni <- length(Im)
    if (ni > 0L) {
      Im <- Imod[Im, , drop = FALSE]
      for (i in 1L:ni) {
        hh <- (Im[i, 2L] - 1L) * Jm + Im[i, 3L]
        ibet <- c(ibet, hh)
        off[hh] <- Im[i, 5L]
      }
    }

    # 5:  p(hj)/p(hk) = a*p(im)/p(in)
    Im <- which(Imod[, 1L] == 5L)
    ni <- length(Im)
    if (ni > 0L) {
      Im <- Imod[Im, , drop = FALSE]
      ni <- ni / 2
      hh <- 2L * (1L:ni)
      Im <- cbind(Im[hh - 1L, , drop = FALSE], Im[hh, , drop = FALSE])
      for (i in 1L:ni) {
        h1 <- (Im[i, 2L] - 1L) * Jm + Im[i, 3L]
        h2 <- (Im[i, 2L] - 1L) * Jm + Im[i, 4L]
        k1 <- (Im[i, 7L] - 1L) * Jm + Im[i, 8L]
        k2 <- (Im[i, 7L] - 1L) * Jm + Im[i, 9L]
        X[, h1] <- X[, h1] - X[, k2]
        X[, h2] <- X[, h2] + X[, k2]
        X[, k1] <- X[, k1] + X[, k2]
        ibet <- c(ibet, k2)
        off[k2] <- Im[i, 5L]
      }
    }

    # 6: equal overdispersion
    Im <- which(Imod[, 1L] == 6L)
    ni <- length(Im)
    if (ni > 0L) {
      Im <- Imod[Im, , drop = FALSE]
      for(i in 1:ni) {
        h1 <- IJm + Im[i, 3L]
        h2 <- IJm + Im[i, 4L]
        X[, h1] <- X[, h1, drop = FALSE] + X[, h2, drop = FALSE]
        ibet <- c(ibet, h2)
      }
    }

    # 7: la(i) set to 0 # NOT ACTIVE
    Im <- which(Imod[, 1L] == 7L)
    ni <- length(Im)
    if (ni > 0L) {
      Im <- Imod[Im, , drop = FALSE]
      for(i in 1:ni) {
        hh <- IJm + Im[i, 2L]
        ibet <- c(ibet, hh)
        off[hh] <- -20
      }
    }

    # 8: effects of covariates
    Im <- which(Imod[, 1L] == 8L, )
    ni <- length(Im)
    if (ni > 0L) {
      Im <- Imod[Im, , drop = FALSE]
      ng <- Im[ni, 4L]
      Z <- matrix(0L, nrow = IJm + I, ncol = ng)
      for (i in 1L:ng) {
        id1 <- c()
        if (Im[i, 3L] <= Jm) {
          # only one logit in the row
          id1 <- c(id1, (Im[i, 2L] - 1L) * Jm + Im[i, 3L])
        } else {
          # all logits in the row
          id1 <- c(id1, ((Im[i, 2L] - 1L) * Jm + 1L):(Im[i, 2L] * Jm))
        }
        Z[id1, i] <- 1L
      }
    }

    # 9: fix the logit of a transition
    Im <- which(Imod[, 1L] == 9L)
    ni <- length(Im)
    if (ni > 0L) {
      Im <- Imod[Im, , drop = FALSE]
      for(i in 1:ni) {
        hh <- (Im[i, 2] - 1) * Jm + Im[i, 3]
        off[hh] <- Im[i, 5]
      }
    }

    # 10: fix overdispersion # NOT ACTIVE
    Im <- which(Imod[, 1L] == 10)
    ni <- length(Im)
    if (ni > 0L) {
      Im <- Imod[Im, , drop = FALSE]
      for(i in 1:ni) {
        hh <- IJm + Im[i, 2L]
        ibet <- c(ibet, hh)
        off[hh] <- log(Im[i, 5L]) # This is in the log scale
      }
    }
  }

  if (!is.null(ibet)){
    ier <- as.numeric(names(table(ibet))[table(ibet) > 1])
    if (length(ier) > 0L){
      fila <- ifelse( ier/Jm - floor(ier/Jm) == 0L, floor(ier/Jm), floor(ier/Jm) + 1L)
      col <- ier - (fila - 1L)*Jm
      fila.p <- fila <= I
      fila.o <- col[fila > I]
      fila.p <- paste0("(", fila[fila.p], ", ", col[fila.p], ")")
      fila.o <- ifelse(fila > I, paste0(" Constraints corresponding to over-dispersions in which the row(s) ", fila.o, ' is/are involved could be not consistent.'), '')
      fila.p <- ifelse(fila <= I, paste0('Constraints in which the cell(s) ', fila.p, ' of the transition matrix is/are involved could be not consistent.'), '')
      message <- paste0(fila.p, fila.o)
      warning(message)
    }
    # Adapt beta and design matrix
    # remove constrained elements
    X <- X[, -ibet]
    #k <- nrow(Z)
    z <- ncol(Z)
    if (is.null(start)) {
      beta <- beta[-ibet]
      if (cvar > 0L) {
        beta <- c(beta, rep(0L, z))
      }
    }
  } else {
    if (is.null(start) & cvar > 0L) {
      z <- ncol(Z)
      beta <- c(beta, rep(0L, z))
    }
  }

  if (cvar == 0L) {
    Z <- matrix(0, nrow = nrow(X), ncol = 0)
  }

  output <- list("X1" = X, "X2" = Z, "off" = off, "cs" = cs, "beta" = beta)
  return(output)
}
##------------------------------------------------------------------------------

## Function votlan()##
votlan <- function(N, Y, C, Imod, start, cs, seed, mit, tol, verbose, save.beta) {
  # N: results in election 1
  # Y: results ion election 2
  # C: matrix of covariates
  # Imod: desing matrix, output of Imod_function
  # start: matrix of initial estimates for the beta vector
  # mit: maximum number of iterations?

  #  -  MLE of vote transitions (Forcina, Marcehtti, CLDAG 2010)
  #     rows of N and Y are electoral results at time t-1 and t
  #     if model is constant across units, beta can be omitted and
  #     initial estimates of P and La provided; last col of P is
  #     used as reference; log-oversdipersion; C contains covariates
  #     or has 0 columns
  #  -  calls  ->  fiscor

  # Preliminaries
  # J <- ncol(Y)
  # I <- ncol(N)
  # Jm <- ncol(Y) - 1L
  # Ym <- Y[, 1L:Jm]
  # IJm <- I * Jm

  # Adapt beta to the model
  Marg <- model_building(N = N, Y = Y, Imod = Imod, start = start, cvar = ncol(C), cs = cs)
  # beta <- Marg[[5L]]
  if(nrow(Y) > 1L){
    Darg <- list(N = N, Y = Y[, 1L:(ncol(Y) - 1L)], C = C)
  } else {
    Darg <- list(N = N, Y = Y[, 1L:(ncol(Y) - 1L)], C = C)
    Darg[[2]] <- matrix(Darg[[2]], nrow = 1L, ncol = length(Darg[[2]]))
  }

  # Fisher scoring
  if (ncol(Darg[[3L]]) > 0) { # with covariates
    f.res <- fiscoVT(Darg = Darg, Marg = Marg, maxit = mit,
                     tol = tol, verbose = verbose, save.beta = save.beta)
  } else { # without covariates
    f.res <- fiscoxLC(Darg = Darg, Marg = Marg, maxit = mit,
                      tol = tol, verbose = verbose, save.beta = save.beta)
  }

  beta <- f.res$beta
  V <- f.res$V

  # Compute estimates
  res <- TraSeM(be = beta, V = V, Darg = Darg, Marg = Marg)
  V[V < 10^-20] <- 10^-20

  return(list("Q" = res$Q, "beta" = beta, "La" = res$La, "Vp" = res$Vp,
              "sbe" = sqrt(diag(V)), "madis" = res$madis, "lk" = f.res$lk, "iter" = f.res$iter,
              "V" = V))
}
##------------------------------------------------------------------------------

##------------------------------------------------------------------------------

## Function votlan_unit()##
votlan_unit <- function(N, Y, C, Imod, start, cs, seed, mit, tol, verbose, save.beta) {
  # N: results in election 1
  # Y: results ion election 2
  # C: matrix of covariates
  # Imod: desing matrix, output of Imod_function
  # start: matrix of initial estimates for the beta vector
  # mit: maximum number of iterations?

  #  -  MLE of vote transitions (Forcina, Marcehtti, CLDAG 2010)
  #     rows of N and Y are electoral results at time t-1 and t
  #     if model is constant across units, beta can be omitted and
  #     initial estimates of P and La provided; last col of P is
  #     used as reference; log-oversdipersion; C contains covariates
  #     or has 0 columns
  #  -  calls  ->  fiscor

  # Preliminaries
  # J <- ncol(Y)
  # I <- ncol(N)
  # Jm <- ncol(Y) - 1L
  # Ym <- Y[, 1L:Jm]
  # IJm <- I * Jm

  # Adapt beta to the model
  Marg <- model_building(N = N, Y = Y, Imod = Imod, start = start, cvar = ncol(C), cs = cs)
  # beta <- Marg[[5L]]
  if(nrow(Y) > 1L){
    Darg <- list(N = N, Y = Y[, 1L:(ncol(Y) - 1L)], C = C)
  } else {
    Darg <- list(N = N, Y = Y[, 1L:(ncol(Y) - 1L)], C = C)
    Darg[[2]] <- matrix(Darg[[2]], nrow = 1L, ncol = length(Darg[[2]]))
  }

  # Fisher scoring
  if (ncol(Darg[[3L]]) > 0) { # with covariates
    f.res <- fiscoVT(Darg = Darg, Marg = Marg, maxit = mit,
                     tol = tol, verbose = verbose, save.beta = save.beta)
  } else { # without covariates PENDIENTE
    f.res <- fiscoxLC(Darg = Darg, Marg = Marg, maxit = mit,
                      tol = tol, verbose = verbose, save.beta = save.beta)
  }

  beta <- f.res$beta
  V <- f.res$V

  # Compute estimates
  res <- TraSeM_unit(be = beta, V = V, Darg = Darg, Marg = Marg)
  res$Q[res$Q < 10^-15] <- 10^-15

  return(list("Q" = res$Q))
}
##------------------------------------------------------------------------------


## Function fiscoVT##
# Performs Fisher scoring when covariates are available
fiscoVT <- function(Darg, Marg, maxit = 100, tol = 0.0001, o = 5.8, verbose = TRUE, save.beta = TRUE) {
  # Darg: data arguments
  # Marg: matrix argument
  # flik: likelihood function
  # Finds mle by Fisher scoring, given data in Darg, retrieved from GM
  # modified version without steepest ascent
  # tol <- 0.0005  # For testing convergence
  # o <- 5.8  # Adjust step length

  # Preliminares
  # Initial variables
  g <- rep(0L, 5L)
  s <- 1L
  ico <- 1L
  beta <- Marg[[5L]]
  q <- length(beta)

  # Inicialization
  it <- 1L
  test <- TRUE
  # vl0 <- rep(Inf, 4L)

  # Iterate
  while (test & it <= maxit) {
    sw <- 3
    Marg[[5]] <- beta
    # First call
    if (ico > 0) {
      res1 <- liderlac(Darg = Darg, Marg = Marg, sw = sw)
      lk <- res1$lk
      d1 <- res1$d1
      D <- res1$d2
      d1r <- d1[abs(beta) < 12]
      tgra <- mean(abs(d1r))

      # check singularity
      eigs <- eigen(D)
      dL <- eigs$values
      mL <- min(dL)
      if (mL > -10^(-10)) {
        dL <- dL + 10^(-9)*rep(1L, length(beta))
      } else {
        stop(paste("Singular matrix encountered. The process stopped before converging.\n",
                   "If you use `save.beta = TRUE` when calling the function, you could restart the process\n",
                   "using as initial values the beta vector saved in `beta.RData`."))
      }
      Di <- eigs$vectors %*% diag(dL^(-1)) %*% t(eigs$vectors)
      dlt <- Di %*% d1
    } else {
      o <- o / 2
      dlt <- dlt + stats::rnorm(q) / 200
    } # steepest ascent

    # compute step direction
    delta <- o * dlt / (sqrt(sum(dlt^2)) + ico + 1)
    if (any(is.complex(delta))) {
      stop(paste("Complex numbers in delta. The process stopped before converging.\n",
                 "If you use `save.beta = TRUE` when calling the function, you could restart the process\n",
                 "using as initial values the beta vector saved in `beta.RData`."))
    }

    # loglik and derivative
    g[1] <- lk
    g[4] <- sum(d1 * dlt)

    # Update beta
    b <- beta + delta
    sw <- 2

    # Second call
    Marg[[5]] <- b
    res2 <- liderlac(Darg = Darg, Marg = Marg, sw = sw)
    d1 <- res2$d1
    d1r <- d1[abs(b) < 12]
    tgra <- min(tgra, mean(abs(d1r))) # test convergence

    # Log-likelihood and derivative
    g[2] <- res2$lk
    g[5] <- sum(d1 * dlt)

    # adjust step length
    if (g[4] > g[5] & g[5] > 0) {
      o <- o * (1 + 2 * (g[5] / g[4]))
    } else if (g[5] < 0 & abs(g[5]) > g[4]) {
      o <- o / 6
    }
    if (o < 0.01) {
      o <- 0.01
    }
    if (o > 8) {
      o <- 8
    }

    # Compute new step length
    s <- cubic(g, 1)
    if (s > 8) {
      s <- 8
    } else if (s < 0.01) {
      s <- 0.01
    }
    # Second guess and update
    sw <- 1
    b3 <- beta + s * delta
    Marg[[5]] <- b3
    res3 <- liderlac(Darg = Darg, Marg = Marg, sw = sw)
    g[3] <- res3$lk

    # check convergence
    if (tgra < tol) {
      test <- FALSE
    }

    # second guess the best
    if (g[3] >= g[1] & g[3] >= g[2]) {
      beta <- b3
      sw <- 3
      ico <- 3
      # first guess the best
    } else if (g[2] > g[3] & g[2] >= g[1]) {
      beta <- b
      sw <- 3
      ico <- 2
      # else steepest ascent or bisect
    } else if (g[1] > g[2] & g[1] > g[3]) {
      # last attempt
      s <- s / 3
      sw <- 1
      b4 <- beta + s * delta
      Marg[[5]] <- b4
      lk <- liderlac(Darg = Darg, Marg = Marg, sw = sw)$lk
      if (lk > g[1]) {
        g[3] <- lk
        beta <- b4
        ico <- 4
      } else {
        ico <- 1
      }
    }

    # if(max(abs(vl0 - c(s, o, ico, tgra))) < tol*10^-4) test <- FALSE
    # vl0 <- c(s, o, ico, tgra)

    # display LL and derivatives
    if(verbose){
      cat("%%%%%\n")
      cat(it, "\n")
      cat(-g[1:3] / 100, "\n")
      cat(g[4:5], "\n")
      cat(s, o, ico, tgra / 100, "\n")
    }
    it <- it + 1L

    if (save.beta) {
       save(beta, file = "beta.RData")
    }

  }  # End while

  # information matrix
  V <- Di

  # print results
  if(verbose){
    cat("%%%%%\n")
    cat(it, -g[1:3], "\n")
    cat(g[4:5], "\n")
    cat(s, o, tgra, "\n")
  }

  # Output
  return(list("beta" = beta, "V" = V, "lk" = g[1L], "iter" = it))
}
##------------------------------------------------------------------------------


## Function liderlac ##
liderlac <- function(Darg, Marg, sw = 3){
  # Marg is the 4 first components of the output of model_building
  # beta is the fifth component of the output of model_building
  # Darg: data N, Ym, C and ncol(C)
  # sw: determines what computes,
  #    1: just likelihood,
  #    2: likelihood and first derivatives,
  #    3: likelihood, first derivatives and matrix of expected values of second derivatives
  #    4: likelihood, first derivatives and empirical information matrix (faster)

  # Preliminaries
  X1 <- Marg[[1L]]
  X2 <- Marg[[2L]]
  csi <- Marg[[4L]]
  beta <- Marg[[5L]]
  N <- Darg[[1L]]
  Ym <- Darg[[2L]]
  C <- Darg[[3L]]
  I <- ncol(N)
  K <- nrow(N)
  Jm <- ncol(Ym)
  IJm <- I * Jm
  d1 <- d2 <- S <- NULL

  # Compute P and Theta
  Q_index_La <- linklac(Darg = Darg, Marg = Marg)
  Q <- Q_index_La$Q
  index <- Q_index_La$index
  La <- Q_index_La$lambda

  # Initialize
  lk <- 0
  sib <- length(beta)
  sid <- IJm + I

  if (sw > 1) {
    d1 <- rep(0L, sib)
  }

  if (sw > 2) {
    d2 <- matrix(0L, nrow = sib, ncol = sib)
  }

  # if (sw > 3) {
  #   S <- d2
  # }

  #if (ncol(C) == 0L) {
  #  C <- matrix(0L, nrow = K, ncol = 1L)
  #}

  # T <- '.-+*'
  U <- as.matrix(rep(1L, IJm + I))

  # Clicking time
  # QK <- ceiling(K * (1:60) / 60)

  # Cycle across units
  for (k in 1L:K) {

    n <- as.matrix(N[k, ])
    y <- as.matrix(Ym[k, ])
    csn <- csi * (n > 0) * (n - 1L) / (n + (n == 0))
    Ck <- U %*% C[k, ]
    X <- cbind(X1, Ck * X2)
    P <- Q[index[, k], ]
    nb <- as.matrix(n * (1L + La * csn))

    # Variance matrix
    Vy <- diag(as.vector(t(P) %*% nb)) - t(P) %*% diag(as.vector(nb)) %*% P
    e <- y - t(P) %*% n
    H <- solve(Vy)

    # Twice likelihood
    lk <- lk - (log(det(Vy)) + t(e) %*% H %*% e)

    # First derivatives
    if (sw > 1) {
      He <- H %*% e
      Hd <- H - He %*% t(He)
      dH <- diag(Hd)
      u <- as.matrix(rep(0L, sid))
      V <- matrix(0L, nrow = sid, ncol = sid)

      for (i in 1L:I) {
        ni <- n[i]
        nbi <- nb[i]
        pi <- P[i, ]
        Oi <- diag(pi) - pi %*% t(pi)
        Lai <- csn[i] * La[i] * (1 - La[i])
        # components wrt P
        rp <- ((i - 1L) * Jm + 1L):(i * Jm)
        u[rp] <- Oi %*% (nbi * (-dH / 2 + Hd %*% pi) + ni * He)
        u[IJm + i] <- -ni * Lai * sum(diag(Hd %*% Oi)) / 2

        # Second derivatives
        if (sw > 2) {
          H2 <- H^2 / 2
          hi <- H %*% pi
          for (l in 1L:I) {
            nl <- n[l]
            nbl <- nb[l]
            pl <- P[l, ]
            Lal <- csn[l] * La[l] * (1 - La[l])
            Ol <- diag(pl) - pl %*% t(pl)
            hl <- H %*% pl
            jn <- ((l - 1L) * Jm + 1L):(l * Jm)
            if (l <= i) {
              # wrt to beta_i, beta_l'
              A <- nbi * nbl * (H2 - diag(as.vector(hl))%*%H - t(diag(as.vector(hi))%*%H) +
                                  hl %*% t(hi) + H * as.vector(t(pl) %*% H %*% pi))
              A <- A + ni * nl * H
              V[rp, jn] <- Oi %*% A %*% Ol
              #  wrt to La_i, La_l
              a <- ni * nl * Lai * Lal / 2
              a <- a * sum(H %*% Ol %*% H %*% Oi)
              V[IJm + i, IJm + l] <- a
            }
            #  wrt to La_i,beta_l'
            a <- ni * nbl * Lai / 2
            B <- H %*% Oi %*% H
            V[IJm + i, jn] <- as.vector(a * (Ol %*% (diag(B) - 2 * B %*% pl)))
          } # end for d2_units
        } # end sw > 2
      } # end for d1_units
    } # end sw > 1

    # Cumulate
    if (sw > 1) {
      si <- t(X) %*% u
      d1 <- d1 + si
    }

    if (sw > 2) {
      V1 <- V2 <- V
      V1[!lower.tri(V, diag = TRUE)] <- 0
      V2[!lower.tri(V, diag = FALSE)] <- 0
      V <- V1 + t(V2)
      d2 <- d2 + t(X) %*% V %*% X
      d2 <- (d2 + t(d2)) / 2
    }

    # if (sw > 3) {
    #    S <- S + si %*% t(si)
    # }
  }

  lk <- lk / 2

  return(list("lk"= lk, "d1" = d1, "d2" = d2, "S"= S))

}
##------------------------------------------------------------------------------

## Function cubic ##
cubic <- function(g, o = 1, s = NULL, L = NULL) {
  # Computes step length by fitting a cubic polynomial
  # Preliminaries
  g <- g[-3]

  # Computation of parameters
  X <- matrix(c(1, 0, 0, 0,           # lk(0)
                1, o, o^2, o^3,       # lk(1)
                0, 1, 0, 0,           # d1(0)
                0, 1, 2*o, 3*o^2),    # d1(1)
              nrow = 4, byrow = TRUE)

  if (!is.null(s) & !is.null(L)) {
    A <- cbind(rep(1, nrow(s)), s, s^2, s^3)
    g <- c(g, L)
    X <- rbind(X, A)
  }

  be <- solve(t(X) %*% X) %*% (t(X) %*% as.matrix(g))

  # Finding the maximum
  c <- be[2]
  b <- 2 * be[3]
  a <- 3 * be[4]

  dis <- b^2 - 4 * a * c

  if (dis > 0) {
    dis <- sqrt(dis)
    xn <- (-b - dis) / (2 * a)
    xp <- (-b + dis) / (2 * a)
    Hn <- b + 2 * a * xn
    Hp <- b + 2 * a * xp

    if (is.na(xn) | is.na(Hn)){
      s <- 0.1
    } else if (xn > 0 & Hn < 0) {
      s <- xn
    } else if (xp > 0 & Hp < 0) {
      s <- xp
    } else {
      s <- 0.1
    }
  } else {
    s <- -1 # steepest ascent
  }

  return(s)
}
##------------------------------------------------------------------------------

## Function linklac ##
linklac <- function(Darg, Marg){
  # This function is used by liderlac

  # Preliminaries
  X1 <- Marg[[1L]]
  X2 <- Marg[[2L]]
  off <- Marg[[3L]]
  beta <- Marg[[5L]]
  C <- as.matrix(Darg[[3L]])
  I <- ncol(Darg[[1L]])
  Jm <- ncol(Darg[[2L]])
  K <- nrow(Darg[[3L]])
  IJm <- I * Jm

  # Initialize variables
  u <- as.matrix(rep(1L, IJm + I))
  P <- matrix(0L, nrow = I * K, ncol = Jm)
  index <- matrix(1L:(I * K), nrow = I)

  # Cycle across units
  for (k in 1L:K) {
    Ck <- u %*% C[k, ]
    X <- cbind(X1, Ck * X2)
    ett <- X %*% beta + off
    ett <- ett * (abs(ett) < 680) + 680 * sign(ett) * (abs(ett) >= 680)
    et <- matrix(exp(ett[1L:IJm]), ncol = Jm, byrow = TRUE)
    # et <- t(matrix(exp(ett[1L:IJm]), ncol = Jm, byrow = FALSE))

    # Compute P
    P1 <- diag(1L / (1L + rowSums(et))) %*% et
    P[index[, k], ] <- P1
  }

  # Compute Lambda
  etl <- ett[(IJm + 1L):(IJm + I)]
  etl <- etl * (etl < 12) + 12 * (etl >= 12)
  La <- exp(etl) / (1L + exp(etl))

  return(list("Q"= P, "index" = index, "lambda" = La))
}
##------------------------------------------------------------------------------

## Function TraSeM()##
TraSeM <- function(be, V, Darg, Marg) {
  # Preliminaries
  X1 <- Marg[[1L]]
  X2 <- Marg[[2L]]
  off <- Marg[[3L]]
  csi <- Marg[[4L]]
  N <- Darg[[1L]]
  Ym <- Darg[[2L]]
  C <- Darg[[3L]]
  K <- nrow(N)
  I <- ncol(N)
  Jm <- ncol(Ym)
  sT <- I * Jm
  IJm <- 1L:sT
  nt <- t(as.matrix(colSums(N)))
  R <- t(diag(1/as.vector(nt))%*% t(N))
  Im <- t(matrix(IJm, nrow = Jm))
  Q <- rep(0L, I * Jm)
  madis <- rep(0, K)
  be <- be * (abs(be) < 640) + 640 * sign(be) * (abs(be) >= 640)

  # Betas for transitions
  X <- cbind(X1, X2)
  isx <- which(colSums(X[IJm, ]) > 0)
  btra <- be[isx]
  Om <- matrix(0, nrow = sT, ncol = sT)
  sx <- length(btra)
  D <- matrix(0, nrow = sT, ncol = sx)
  V <- V[isx, isx]

  # Compute Lambda
  et <- X %*% be + off
  etL <- et[(sT + 1L):(sT + I)]
  La <- exp(etL) / (1 + exp(etL))

  # Compute P and Se
  if (ncol(C) == 0) { # without covariates
    X <- X1[IJm, isx]
    et <- X %*% btra + off[IJm]
    Et <- t(matrix(exp(et), nrow = Jm, byrow = FALSE))
    P <- diag(1/(1 + rowSums(Et))) %*% Et
    for (i in 1:I) {
      pik <- P[i, ]
      im <- Im[i, ]
      Om[im, im] <- diag(pik) - pik %*% t(pik) # Omega(pik)
    }
    for (k in 1:K) {
      # Mahalanobis
      n <- as.matrix(N[k, ])
      y <- as.matrix(Ym[k, ])
      csn <- csi * (n > 1) * (n - 1) / (n + (n == 0))
      nb <- n * (1 + La * csn)
      A <- kronecker(as.matrix(nt), diag(Jm)) %*% Om %*% X
      Vy <- diag(as.vector(t(P) %*% nb)) - t(P) %*% diag(as.vector(nb)) %*% P + A %*% V %*% t(A)
      e <- y - t(P) %*% n
      madis[k] <- t(e) %*% solve(Vy) %*% e
    }
    D <- Om %*% X
    Q <- P

  } else { # with covariates

    for (k in 1:K) {
      # Compute P
      ck <- C[k, ]
      if (ncol(C) == 1L) ck <- as.matrix(ck)
      X <- cbind(X1, X2 %*% diag(ck))[IJm, isx]
      et <- X %*% btra + off[IJm]
      Et <- t(matrix(exp(et), nrow = Jm, byrow = FALSE))
      Pk <- diag(1 / (1 + rowSums(Et))) %*% Et

      # Variances of transitions
      for (i in 1:I) {
        pik <- Pk[i, ]
        im <- Im[i, ]
        Om[im, im] <- Omega(pik)
      } # end for i

      A <- kronecker(diag(R[k, ]), diag(Jm))
      Q <- Q + A %*% as.vector(t(Pk))
      A <- A %*% Om %*% X
      D <- D + A

      # Mahalanobis
      n <- as.matrix(N[k, ])
      y <- as.matrix(Ym[k, ])
      csn <- csi * (n > 1) * (n - 1) / (n + (n == 0))
      nb <- n * (1 + La * csn)
      A <- kronecker(as.matrix(nt), diag(Jm)) %*% Om %*% X
      Vy <- diag(as.vector(t(Pk) %*% nb)) - t(Pk) %*% diag(as.vector(nb)) %*% Pk + A %*% V %*% t(A)
      e <- y - t(Pk) %*% n
      madis[k] <- t(e) %*% solve(Vy) %*% e
    } # end for k
    Q <- t(matrix(Q, nrow = Jm, byrow = FALSE))
  }

  # Se transitions
  Vp <- D %*% V %*% t(D)
  Q <- cbind(Q, 1 - rowSums(Q))
  Q[Q < 10^-15] <- 10^-15
  TT <- kronecker(diag(I), rbind(diag(Jm), rep(1, Jm)))
  Vp <- TT %*% Vp %*% t(TT)
  Vp[Vp < 10^-10] <- 10^-10
  Vp <- sqrt(t(matrix(diag(Vp), nrow = Jm + 1L, byrow = FALSE)))
  return(list("Q" = Q, "Vp" = Vp, "La" = La, "madis" = madis))
}
##------------------------------------------------------------------------------

## Function TraSeM_unit()##
TraSeM_unit <- function(be, V, Darg, Marg) {
  # Preliminaries
  X1 <- Marg[[1L]]
  X2 <- Marg[[2L]]
  off <- Marg[[3L]]
  csi <- Marg[[4L]]
  N <- Darg[[1L]]
  Ym <- Darg[[2L]]
  C <- Darg[[3L]]
  K <- nrow(N)
  I <- ncol(N)
  Jm <- ncol(Ym)
  sT <- I * Jm
  IJm <- 1L:sT
  nt <- t(as.matrix(colSums(N)))
  R <- t(diag(1/as.vector(nt))%*% t(N))
  Im <- t(matrix(IJm, nrow = Jm))
  Q <- rep(0L, I * Jm)
  be <- be * (abs(be) < 640) + 640 * sign(be) * (abs(be) >= 640)

  # Betas for transitions
  X <- cbind(X1, X2)
  isx <- which(colSums(X[IJm, ]) > 0)
  btra <- be[isx]
  Om <- matrix(0, nrow = sT, ncol = sT)
  sx <- length(btra)
  D <- matrix(0, nrow = sT, ncol = sx)
  V <- V[isx, isx]

  # Compute Lambda
  et <- X %*% be + off
  etL <- et[(sT + 1L):(sT + I)]
  La <- exp(etL) / (1 + exp(etL))

  # Compute P and Se
  if (ncol(C) == 0) { # without covariates
    X <- X1[IJm, isx]
    et <- X %*% btra + off[IJm]
    Et <- t(matrix(exp(et), nrow = Jm, byrow = FALSE))
    P <- diag(1/(1 + rowSums(Et))) %*% Et
    for (i in 1:I) {
      pik <- P[i, ]
      im <- Im[i, ]
      Om[im, im] <- diag(pik) - pik %*% t(pik) # Omega(pik)
    }
    for (k in 1:K) {
      # Mahalanobis
      n <- as.matrix(N[k, ])
      y <- as.matrix(Ym[k, ])
      csn <- csi * (n > 1) * (n - 1) / (n + (n == 0))
      nb <- n * (1 + La * csn)
      A <- kronecker(as.matrix(nt), diag(Jm)) %*% Om %*% X
      Vy <- diag(as.vector(t(P) %*% nb)) - t(P) %*% diag(as.vector(nb)) %*% P + A %*% V %*% t(A)
      e <- y - t(P) %*% n
    }
    D <- Om %*% X
    Q <- P

  } else { # with covariates

    for (k in 1:K) {
      # Compute P
      ck <- C[k, ]
      if (ncol(C) == 1L) ck <- as.matrix(ck)
      X <- cbind(X1, X2 %*% diag(ck))[IJm, isx]
      et <- X %*% btra + off[IJm]
      Et <- t(matrix(exp(et), nrow = Jm, byrow = FALSE))
      Pk <- diag(1 / (1 + rowSums(Et))) %*% Et

      # Variances of transitions
      for (i in 1:I) {
        pik <- Pk[i, ]
        im <- Im[i, ]
        Om[im, im] <- Omega(pik)
      } # end for i

      A <- kronecker(diag(R[k, ]), diag(Jm))
      Q <- Q + A %*% as.vector(t(Pk))
      A <- A %*% Om %*% X
      D <- D + A

      # Mahalanobis
      n <- as.matrix(N[k, ])
      y <- as.matrix(Ym[k, ])
      csn <- csi * (n > 1) * (n - 1) / (n + (n == 0))
      nb <- n * (1 + La * csn)
      A <- kronecker(as.matrix(nt), diag(Jm)) %*% Om %*% X
      Vy <- diag(as.vector(t(Pk) %*% nb)) - t(Pk) %*% diag(as.vector(nb)) %*% Pk + A %*% V %*% t(A)
      e <- y - t(Pk) %*% n
    } # end for k
    Q <- t(matrix(Q, nrow = Jm, byrow = FALSE))
  }

  # Se transitions
  Q <- cbind(Q, 1 - rowSums(Q))
  return(list("Q" = Q))
}
##------------------------------------------------------------------------------


## Function Omega##
Omega <- function(x){
  x <- as.matrix(x)
  size <- dim(x)
  if (size[1L] < size[2L]) x <- t(x)
  y <- diag(as.vector(x)) - x %*% t(x)
  return(y)
}
##------------------------------------------------------------------------------

## Function fiscoxLC ##
fiscoxLC <- function(Darg, Marg, maxit = 100, tol = 0.0002, o = 5, verbose = TRUE, save.beta = FALSE) {
  # Darg: data arguments
  # Marg: matrix argument
  # flik: likelihood function
  # Finds mle by Fisher scoring,
  # Finds mle by Fisher scoring, given data in Darg
  #  Output
  #       s: step length
  #       g(1,2,3): log-lik at the 3 basic steps
  #       g(4,5): derivative of g(1,2)
  #       a(1,2): coefficients of quadratic or linear approximation
  #       a(3): discriminant in quadratic approximation


  # Preliminaries
  # Initial variables
  g <- a <- rep(0L, 5L)
  s <- 0.1
  beta <- Marg[[5L]]
  q <- length(beta)
  sain <- 0

  # Initialize
  it <- 1
  test <- TRUE
  q <- length(beta)

  # Iterate
  while (test & it <= maxit) {
    bind <- 0
    sw <- 3
    Marg[[5]] <- beta
    res1 <- liderla(Darg = Darg, Marg = Marg,  sw = sw)
    lk <- res1$lk
    d1 <- res1$d1
    D <- res1$d2
    tgra <- max(abs(d1))

    # check singularity
    if (sain == 0) {
      eigs <- eigen(D)
      dL <- eigs$values
      mL <- min(dL)
      if (mL < 10^(-6)) {
        dL <- dL + 10^(-6)*rep(1L, length(beta))
        if (mL < 10^(-5) & verbose) print(mL)
      }
      Di <- eigs$vectors %*% diag(dL^(-1)) %*% t(eigs$vectors)
      dlt <- Di %*% d1
    }

    if (sain > 0) { # steepest ascent
      dlt <- d1
    }

    # compute step direction
    delta <- o * dlt / (sqrt(sum(dlt^2)) + sain + 1)

    # Reduce step length if last cycle went too far
    if (s < 0.1) {
      delta <- s * delta
    }

    if (any(!is.na(delta) & !is.complex(delta) == 0)) {
      stop(paste("Step direction contains complex numbers. The process stopped before converging.\n",
                 "If you use `save.beta = TRUE` when calling the function, you could restart the process\n",
                 "using as initial values the beta vector saved in `beta.RData`."))
    }

    # Loglik and der
    g[1] <- 2*lk
    g[4] <- sum(d1 * dlt)

    # Update beta
    b <- beta + delta
    sw <- 2

    # Second call
    Marg[[5]] <- b
    res2 <- liderla(Darg = Darg, Marg = Marg,  sw = sw)
    lk <- res2$lk
    d1 <- res2$d1

    tgra <- min(tgra, max(abs(d1))) #  test convergence

    # Loglik and der
    g[2] <- 2*lk
    g[5] <- sum(d1 * dlt)

    # adjust step length
    if (g[4] > g[5] & g[5] > 0) { #  step too short
      o <- o * (1 + 10 * (g[5] / g[4]) ^ 0.5)
    } else if (g[5] < 0 & abs(g[5]) > g[4]) { # step too long
      o <- o / 1.6
    }
    if (o < 0.01) {
      o <- 0.01
    }
    if (o > 10) {
      o <- 10
    }

    # Compute new step length
    a[1] <- 3 * (g[4] + g[5]) - 6 * (g[2] - g[1])
    a[4] <- g[5] - g[4]
    a[2] <- a[4] - a[1]
    a[3] <- a[2]^2 - 4 * a[1] * g[4]
    a[5] <- abs(a[1])

    s <- fit_ag(a, g)

    if (s > 2) s <- 2
    if (s < 0.1) s <- 0.1

    # Second guess and update
    rep <- TRUE

    # Repeated bisection
    while (rep) {
      rep <- FALSE
      sw <- 1
      b3 <- beta + s * delta
      Marg[[5]] <- b3
      res3 <- liderla(Darg = Darg, Marg = Marg,  sw = sw)
      lk <- 2*res3$lk

      g[3] <- lk

      # display
      if(verbose){
        cat("%%%%%\n")
        cat(it, "\n")
        cat(-g[1:3] / 100, "\n")
        cat(g[4:5], "\n")
        cat(s, o, tgra / 1000, "\n")
      }

      # Check convergence
      if (tgra < tol) {
        test <- FALSE
      }

      # Second guess best
      if (g[3] >= g[1] & g[3] >= g[2]) {
        beta <- b3
        sain <- 0
      }
      # First guess best
      if (g[2] > g[3] & g[2] >= g[1]) {
        beta <- b
        sain <- 0
      }
      # else steepest ascent or bisect
      if (g[1] > g[2] & g[1] > g[3]) {
        if (bind < 1) {
          delta <- delta / 10
          bind <- bind + 1
          sain <- 0
          rep <- TRUE
          if(verbose) cat("Bisection.\n")
        } else {
          sw <- 2
          sain <- sain + 1
          if(verbose) cat("Steepest ascent.\n")
        }
      }
    } # end bisection

    # Save results
    if (any(is.na(beta))) {
      stop(paste("NA produced. The process stopped before converging.\n",
                 "If you use `save.beta = TRUE` when calling the function, you could restart the process\n",
                 "using as initial values the beta vector saved in `beta.RData`."))
    } else if (save.beta) {
       save(beta, file = "beta.RData")
    }

    it <- it + 1L
  } # end while

  # Information matrix
  V <- Di

  if(verbose){
    # Print results
    cat("%%%%%\n")
    cat(it, "\n")
    cat(-g[1:3] / 100, "\n")
    cat(g[4:5], "\n")
    cat(s, o, tgra / 1000, "\n")
  }

  return(list("beta" = beta, "V" = V, "lk" = lk/2, "iter" = it))
}
##------------------------------------------------------------------------------

## Function fit_ag ##
fit_ag <- function(a, g) {
  if (a[3] > 0 & a[5] > 0.1) {
    s <- (-a[2] - sqrt(a[3])) / (2 * a[1])

    if (g[5] < 0) {
      s <- (s - 2 * g[4] / a[4]) / 3
    }
  }

  if (a[3] <= 0 | a[5] <= 0.1) {
    if (a[4] < 0) {
      s <- -g[4] / a[4]
    }

    if (a[4] >= 0) {
      s <- 0.5
    }
  } else {
    s <- 0.1
  }

  return(s)
}
##------------------------------------------------------------------------------

## Function  liderla ##
liderla <- function(Darg, Marg, sw) {
  # Preliminaries
  X <- Marg[[1]]
  cs <- Marg[[4]]
  beta <- Marg[[5]]
  N <- Darg[[1]]
  Ym <- Darg[[2]]
  C <- Darg[[3]]
  I <- ncol(N)
  K <- nrow(N)
  Jm <- ncol(Ym)
  IJm <- I * Jm
  d1 <- d2 <- NULL

  # Compute P and Lambda
  res <- linkla(Darg = Darg, Marg = Marg)
  P <- res$P
  La <- res$La

  # Initialize
  lk <- 0
  sid <- IJm + I

  if (sw > 1) {
    d1 <- matrix(0, nrow = sid, ncol = 1)
  }

  if (sw > 2) {
    d2 <- matrix(0, nrow = sid, ncol = sid)
  }

  # Cycle across units
  # QK <- ceiling(K * (1:60) / 60)

  for (k in 1:K) {
    n <- as.matrix(N[k, ])
    y <- as.matrix(Ym[k, ])
    csn <- cs * (n > 0) * (n - 1L) / (n + (n == 0))
    nb <- as.matrix(n * (1L + La * csn))

    # Variance matrix
    Vy <- diag(as.vector(t(P) %*% nb)) - t(P) %*% diag(as.vector(nb)) %*% P
    e <- y - t(P) %*% n
    H <- solve(Vy)

    # Twice likelihood
    lk <- lk - (log(det(Vy)) + t(e) %*% H %*% e)

    # First derivatives
    if (sw > 1) {
      He <- H %*% e
      Hd <- H - He %*% t(He)
      dH <- diag(Hd)
      u <- as.matrix(rep(0L, sid))
      V <- matrix(0L, nrow = sid, ncol = sid)

      for (i in 1:I) {
        ni <- n[i]
        nbi <- nb[i]
        pi <- P[i, ]
        Oi <- diag(pi) - pi %*% t(pi)
        Lai <- csn[i] * La[i] * (1 - La[i])
        #  components wrt P
        rp <- ((i - 1L) * Jm + 1L):(i * Jm)
        u[rp] <- Oi %*% (nbi * (-dH / 2 + Hd %*% pi) + ni * He)
        u[IJm + i] <- -ni * Lai * sum(diag(Hd %*% Oi)) / 2

        # Second derivatives
        if (sw > 2) {
          H2 <- H * H / 2
          hi <- H %*% pi

          for (l in 1:I) {
            nl <- n[l]
            nbl <- nb[l]
            pl <- P[l, ]
            Lal <- csn[l] * La[l] * (1 - La[l])
            Ol <- diag(pl) - pl %*% t(pl)
            hl <- H %*% pl
            jn <- ((l - 1L) * Jm + 1L):(l * Jm)

            if (l <= i) {
              #  wrt to beta_i, beta_l'
              A <- (H2 - diag(as.vector(hl))%*%H - t(diag(as.vector(hi))%*%H)) +
                hl %*% t(hi) + H * as.vector(t(pl) %*% H %*% pi)
              A <- nbi * nbl * A + ni * nl * H
              V[rp, jn] <- Oi %*% A %*% Ol
              # wrt to La_i, La_l
              a <- ni * nl * Lai * Lal / 2
              a <- a * sum(H %*% Ol %*% H %*% Oi)
              V[IJm + i, IJm + l] <- a
            }

            a <- ni * nbl * Lai / 2
            B <- H %*% Oi %*% H
            V[IJm + i, jn] <- as.vector(a * (Ol %*% (diag(B) - 2 * B %*% pl)))
          }
        }
      }
    }

    # Cumulate
    if (sw > 1) {
      d1 <- d1 + u
    }

    if (sw > 2) {
      d2 <- d2 + V
    }
  }

  lk <- lk/2

  if (sw > 1) {
    d1 <- t(X) %*% d1
  }

  if (sw > 2) {
    V1 <- V2 <- d2
    V1[!lower.tri(V, diag = TRUE)] <- 0
    V2[!lower.tri(V, diag = FALSE)] <- 0
    V <- V1 + t(V2)
    d2 <- t(X) %*% V %*% X
    d2 <- (d2 + t(d2)) / 2
  }

  return(list("lk" = lk, "d1" = d1, "d2" = d2))
}
##------------------------------------------------------------------------------

## Function linkla ##
linkla <- function(Darg, Marg, eps = 1e-11) {
  # This function is used by liderla
  # Preliminaries
  X1 <- Marg[[1L]]
  off <- Marg[[3L]]
  b <- Marg[[5L]]
  I <- ncol(Darg[[1L]])
  Jm <- ncol(Darg[[2L]])
  IJm <- I * Jm

  et <- X1 %*% b + off
  etl <- et[(IJm + 1L):(IJm + I)]
  et <- et[1L:IJm]
  et <- matrix(exp(et), ncol = Jm, byrow = TRUE)

  # Compute P
  o <- matrix(1, nrow = 1, ncol = Jm)
  P <- et / (1 + rowSums(et) %*% o)
  P[P < eps] <- eps
  p <- rowSums(P)
  p[p > 1 - eps] <- 1 - eps
  p <- p / rowSums(P)
  P <- diag(p) %*% P

  # Compute Lambda
  La <- exp(etl) / (1 + exp(etl))

  return(list(P = P, La = La))
}
##------------------------------------------------------------------------------

# Function IPF##
IPF <- function(matriz, vector.columna, vector.fila, precision = 0.000001){
  nc <- length(vector.columna)
  nf <- length(vector.fila)
  vector.fila1 <- rowSums(matriz)
  R1 <- diag(as.vector(vector.fila)) %*% (diag(as.vector(1/vector.fila1)))
  R1[is.nan(R1)] <- 0L
  R1[is.infinite(R1)] <- 1L
  # R1 <- diag(as.vector(vector.fila)) %*% MASS::ginv(diag(as.vector(vector.fila1)))
  X1 <- R1 %*% matriz
  vector.columna1 <- colSums(X1)
  S1 <- diag(as.vector(vector.columna)) * (diag(as.vector(1/vector.columna1)))
  S1[is.nan(S1)] <- 0L
  S1[is.infinite(S1)] <- 1L
  # S1 <-  diag(as.vector(vector.columna)) * ginv(diag(as.vector(vector.columna1)))
  X2 <- X1 %*% S1
  dif <- max(abs(X2 - matriz))

  while (dif > precision){
    matriz <- X2
    vector.fila1 <- rowSums(matriz)
    R1 <- diag(as.vector(vector.fila)) %*% (diag(as.vector(1/vector.fila1)))
    R1[is.nan(R1)] <- 0L
    R1[is.infinite(R1)] <- 1L
    #   R1 <- diag(as.vector(vector.fila)) %*% ginv(diag(as.vector(vector.fila1)))
    X1 <- R1 %*% matriz
    vector.columna1 <- colSums(X1)
    S1 <- diag(as.vector(vector.columna)) * (diag(as.vector(1/vector.columna1)))
    S1[is.nan(S1)] <- 0L
    S1[is.infinite(S1)] <- 1L
    #   S1 <-  diag(as.vector(vector.columna)) * ginv(diag(as.vector(vector.columna1)))
    X2 <- X1 %*% S1
    dif <- max((abs(X2 - matriz)))
  }
  X2 <- X2*(vector.fila/rowSums(X2))
  X2[is.nan(X2)] <- 0L
  X2[is.infinite(X2)] <- 1L
  X2 <- t(t(X2)*(vector.columna/colSums(X2)))
  X2[is.nan(X2)] <- 0L
  X2[is.infinite(X2)] <- 1L

  return(X2)
}
##------------------------------------------------------------------------------

## Function local_units ##
## Function to adjust local margins using IPF
local_units <- function(X, Y, TM, tol){
  I <- ncol(X)
  J <- ncol(Y)
  K <- nrow(X)
  TR.units <- TR.votes.units <- array(NA, c(I, J, K))
  for (ii in 1L:K){
    # Adjustment of initial underlying probabilities
    TR.votes.units[, , ii] <- IPF(TM, Y[ii, ], X[ii, ], precision = tol)
    TR.votes.units[is.nan(TR.votes.units)] <- 0L
    TR.units[, , ii] <- TR.votes.units[, , ii]/rowSums(TR.votes.units[, , ii])
    TR.units[X[ii, ] == 0L, , ii] <- 0L
  }
  # Composition/aggregation matrix
  TR.votes <- apply(TR.votes.units, c(1L, 2L), sum)
  TR <- TR.votes/rowSums(TR.votes)

  return(list("TR" = TR, "TR.votes" = TR.votes, "TR.units" = TR.units,
              "TR.votes.units" = TR.votes.units)
  )
}
##------------------------------------------------------------------------------

## Function unit_lk ##
## Function to estimate each unit table using the likelihood assumed by the model
## employing as initial values the global estimates

unit_lk <- function(N,
                    Y,
                    start,
                    dispersion.rows,
                    cs = 50,
                    seed = NULL,
                    max.iter = 100,
                    tol = 0.0001
){

  I <- ncol(N) # Number of rows of the transfer matrix
  J <- ncol(Y) # Number of columns of the transfer matrix
  K <- nrow(N) # Number of polling units
  Imod.mat <- Imod_matrix(I = I, covariates = NULL,
                          null.cells = NULL,
                          null.cells.C = NULL,
                          row.cells.relationships = NULL,
                          row.cells.relationships.C = NULL,
                          pair.cells.relationships = NULL,
                          cells.fixed.logit = NULL,
                          dispersion.rows = dispersion.rows,
                          dispersion.row = NULL,
                          dispersion.fixed = NULL)

  TR.units <- TR.votes.units <- array(0L, c(I, J, K))

   for (kk in 1L:K){
      vector.row <- N[kk, , drop = FALSE]
      vector.col <- Y[kk, , drop = FALSE]

      TR.units[, , kk] <- votlan_unit(N = vector.row, Y = vector.col,
                                      C = matrix(0, nrow = K, ncol = 0),
                                      Imod = Imod.mat, start = start, cs = cs,
                                      seed = seed, mit = max.iter, tol = tol,
                                      verbose = FALSE, save.beta = FALSE)$Q

      # suma <- sum(abs(vector.row%*%TR.units[, , kk] - vector.col))
      TR.votes.units[, , kk] <- TR.units[, , kk]*as.vector(vector.row)
      TR.votes.units[, , kk] <- IPF(TR.votes.units[, , kk], Y[kk, ],
                                    N[kk, ], precision = tol)
      TR.votes.units[is.nan(TR.votes.units)] <- 0L
      TR.units[, , kk] <- TR.votes.units[, , kk]/rowSums(TR.votes.units[, , kk])
      TR.units[N[kk, ] == 0L, , kk] <- 0L
    }

    TR.votes <- apply(TR.votes.units, c(1L, 2L), sum)
    TR <- TR.votes/rowSums(TR.votes)

  return(list("TR" = TR, "TR.votes" = TR.votes, "TR.units" = TR.units,
              "TR.votes.units" = TR.votes.units))
}
##------------------------------------------------------------------------------

# This function estimates the transfer matrix of a local unit given a global estimate
# fulfilling its row and column constraints using the metric defined by the underlying
# likelihood.

unit_lik <- function(global.sol, vector.fila, vector.columna, theta, cs = 50){
  nr <- length(vector.fila)
  nc <- length(vector.columna)
  x0 <- as.vector(t(global.sol))

  lb <- rep(0L, nr*nc)
  ub <- rep(1L, nr*nc)

  fobj <- function(x){
    f_obj(incog = x,
          vector.fila = vector.fila,
          vector.columna = vector.columna,
          theta = theta,
          cs = cs)
  }

  # Constraints
  A1 <- t(kronecker(vector.fila, diag(rep(1L, nc))))
  A2 <- t(kronecker(diag(rep(1L, nr)), rep(1L, nc)))[-1L, ]
  A <- rbind(A1, A2)

  B <- c(vector.columna, rep(1L, nr - 1L))

  # Depending on the initial x0, in approximately 1/18000 cases the function
  # solnl crashes. IPF is applied in these cases.
  sol <- tryCatch(NlcOptim::solnl(X = x0,
                                  objfun = fobj,
                                  Aeq = A,
                                  Beq = B,
                                  lb = lb,
                                  ub = ub)$par,
                  error = function(e) as.vector(t(IPF(global.sol, as.vector(vector.columna),
                                                      as.vector(vector.fila)))))

  prop <- matrix(sol, nr, nc, byrow = TRUE)
  prop[prop < 0] <- 0L
  votes <- prop*t(kronecker(t(vector.fila), rep(1L, nc)))

  return(list("m.prop" = prop, "m.votes" = votes))
}

##------------------------------------------------------------------------------

# This function estimates the transfer matrix of a local unit given a global estimate
# fulfilling its row and column constraints using the metric defined by the underlying
# likelihood, taking into account that one of his margins could be zero.

unit_lik_0 <- function(global.sol, vector.fila, vector.columna, theta, cs = 50){
  prop0 <- votes0 <- matrix(0, length(vector.fila), length(vector.columna), byrow = TRUE)
  rows0 <- which(vector.fila < 10^-12)
  cols0 <- which(vector.columna < 10^-12)
  if (length(rows0) > 0){
    vector.fila <- vector.fila[-rows0]
    theta <- theta[-rows0]
  } else {
    vector.fila <- as.vector(vector.fila)
  }
  if (length(cols0) > 0){
    vector.columna <- vector.columna[-cols0]
  } else {
    vector.columna <- as.vector(vector.columna)
  }
  if (length(rows0) > 0)
    global.sol <- as.matrix(global.sol[-rows0, , drop = FALSE])
  if (length(cols0) > 0)
    global.sol <- as.matrix(global.sol[, -cols0, drop = FALSE])
  global.sol <- global.sol/rowSums(global.sol)

  nr <- length(vector.fila)
  nc <- length(vector.columna)

  if(nr == 1L){
    prop <- vector.columna/sum(vector.columna)
    votes <- vector.columna
  } else if (nc == 1L){
    prop <- rep(1, length(vector.columna))
    votes <- vector.columna
  } else {
    x0 <- as.vector(t(global.sol))
    lb <- rep(0L, nr*nc)
    ub <- rep(1L, nr*nc)

    fobj <- function(x){
      f_obj(incog = x,
            vector.fila = vector.fila,
            vector.columna = vector.columna,
            theta = theta,
            cs = cs)
    }

    # Constraints
    A1 <- t(kronecker(vector.fila, diag(rep(1L, nc))))
    A2 <- t(kronecker(diag(rep(1L, nr)), rep(1L, nc)))[-1L, ]
    A <- rbind(A1, A2)

    B <- c(vector.columna, rep(1L, nr - 1L))

    # Depending on the initial x0, in approximately 1/18000 cases the function
    # solnl crashes. IPF is applied in these cases.
    sol <- tryCatch(NlcOptim::solnl(X = x0,
                              objfun = fobj,
                              Aeq = A,
                              Beq = B,
                              lb = lb,
                              ub = ub)$par,
                    error = function(e) as.vector(t(IPF(global.sol, as.vector(vector.columna),
                                                        as.vector(vector.fila)))))

    prop <- matrix(sol, nr, nc, byrow = TRUE)
    prop[prop < 0] <- 0L
    votes <- prop*t(kronecker(t(vector.fila), rep(1L, nc)))
  }

  if (length(rows0) > 0 & length(cols0) > 0){
    prop0[-rows0, -cols0] <- prop
    votes0[-rows0, -cols0] <- votes
  } else if (length(rows0) > 0){
    prop0[-rows0, ] <- prop
    votes0[-rows0, ] <- votes
  } else if (length(cols0) > 0){
    prop0[, -cols0] <- prop
    votes0[, -cols0] <- votes
  }

  return(list("m.prop" = prop0, "m.votes" = votes0))
}

##------------------------------------------------------------------------------

# Base function to define the objective function
f_obj <- function(incog, vector.fila, vector.columna, theta, cs = 50){
  nr <- length(vector.fila)
  nc <- length(vector.columna)
  PI <- matrix(incog, nr, nc, byrow = TRUE)
  suma <- sum(vector.fila)
  resi <- (t(vector.columna) - t(vector.fila)%*%PI)/suma
  cova <- 0L
  for (jj in 1L:nr){
    lambda <- 0
    if (vector.fila[jj] > 0){
      cjj <- cs*(vector.fila[jj] - 1)/vector.fila[jj]
      lambda <- (1 + theta[[jj]] * cjj)
      cova <- cova + diag(PI[jj, ]) - PI[jj, ]%*%t(PI[jj, ])*lambda
    }
  }

  if (abs(det(cova)) > 10^-8){
    cova <- solve(cova)
  } else {
    cova <- diag(rep(1, nc))
  }

  valor <- resi %*% cova %*% t(resi) # + log(det(cova)) # + sum(abs(incog - matriz)) #
  return(valor)
}

##------------------------------------------------------------------------------

extract_theta <- function(beta, ir, I, J, dispersion.rows){
  last.over <- length(beta) - ir
  if (is.null(dispersion.rows)){
    inic.over <- length(beta) - I - ir + 1L
    theta <- beta[inic.over:last.over]
  } else{
    inic.over <- length(beta) - I + nrow(dispersion.rows) - ir + 1L
    theta0 <- beta[inic.over:last.over]
    theta <- rep(NA, I)
    theta[unique(dispersion.rows[, 1L])] <- theta0
    for (rr in 1L:nrow(dispersion.rows)){
      theta[dispersion.rows[rr, 2L]] <- theta[dispersion.rows[rr, 1L]]
    }
  }
  theta <- exp(theta)/(1 + exp(theta))
  return(theta)
}
##------------------------------------------------------------------------------

unit_lik_all <- function(N, Y, TM.init, theta, seed, cs = 50){
  if (!is.null(seed)) set.seed(seed)
  VTM.votes <- VTM.prop <- array(NA, dim = c(ncol(N), ncol(Y), nrow(N)))

  for (ii in 1:nrow(N)){
    vector.fila <- as.matrix(N[ii, ])
    vector.columna <- as.matrix(Y[ii, ])

    rows0 <- which(vector.fila < 10^-12)
    cols0 <- which(vector.columna < 10^-12)

    if(length(rows0) == 0 & length(cols0) == 0){
      sol <- unit_lik(global.sol = TM.init,
                      vector.fila = vector.fila,
                      vector.columna = vector.columna,
                      theta = theta,
                      cs = cs)

    } else {
      sol <- unit_lik_0(global.sol = TM.init,
                        vector.fila = vector.fila,
                        vector.columna = vector.columna,
                        theta = theta,
                        cs = cs)
    }

    VTM.votes[, , ii] <- sol$m.votes
    VTM.prop[, , ii] <- sol$m.prop
  }

  TR.votes <- apply(VTM.votes, c(1, 2), sum)
  TR <- TR.votes/rowSums(TR.votes)

  return(list("TR" = TR, "TR.votes" = TR.votes,
              "TR.units" = VTM.prop, "TR.votes.units" = VTM.votes))
}

Try the eiCircles package in your browser

Any scripts or data that you put into this service are public.

eiCircles documentation built on May 29, 2024, 1:25 a.m.