Page 1 of 1

Error in LC MMNL

Posted: 28 May 2025, 12:31
by fsophie
Dear Stephane and Apollo team,

I am trying to construct a Latent Class Mixed Multinomial Model following your 2012 paper for data that stems from a DCE. The experiment has two unlabeled alternatives and 6 attributes. However, I have been running into an error and cannot figure out how to solve it. I am using the latest versions of R and Apollo (0.3.5).

This is the code:

Code: Select all

apollo_initialise()

apollo_control = list(
  modelName       = "LCMMNL_Model",
  modelDescr      = "Latent class mixed multinomial logit",
  indivID         = "pat_id",
  outputDirectory = "output",
  nCores          = 4,
  panelData       = TRUE,
  mixing          = TRUE
)


apollo_beta = c(
  asc2 = 0,
  asc1 = 0,
  
  mu_adverse = -0.3,
  mu_protect = 0.2,
  mu_admin  = 0.1,
  mu_doses  = -0.1,
  mu_price  = -0.5,
  mu_popfam = 0.15,
  mu_popchild = 0.1,
  
  sigma_adverse = 0.1,
  sigma_protect = 0.1,
  sigma_admin   = 0.1,
  sigma_doses   = 0.1,
  sigma_price   = 0.1,
  sigma_popfam  = 0.1,
  sigma_popchild= 0.1,
  
  delta2 = -2,
  delta3 = -1,
  delta4 = 0,
  delta5 = 1,
  delta6 = 2,
  delta7 = 3,
  delta8 = 4
)

apollo_fixed = c("asc1")

apollo_draws = list(
  interDrawsType = "sobol",
  interNDraws = 500,
  interNormDraws = c("draws_adverse", "draws_protect", "draws_admin",
                     "draws_doses", "draws_price", "draws_popfam", "draws_popchild")
)
apollo_randCoeff = function(apollo_beta, apollo_inputs) {
  with(as.list(apollo_beta), {
    list(
      beta_adverse   = mu_adverse   + sigma_adverse   * draws_adverse,
      beta_protect   = mu_protect   + sigma_protect   * draws_protect,
      beta_admin     = mu_admin     + sigma_admin     * draws_admin,
      beta_doses     = mu_doses     + sigma_doses     * draws_doses,
      beta_price     = mu_price     + sigma_price     * draws_price,
      beta_pop_fam   = mu_popfam    + sigma_popfam    * draws_popfam,
      beta_pop_child = mu_popchild  + sigma_popchild  * draws_popchild
    )
  })
}
apollo_lcPars = function(apollo_beta, apollo_inputs) {
  n <- nrow(apollo_inputs$database)
  V <- list(
    class1 = rep(0, n),
    class2 = rep(delta2, n),
    class3 = rep(delta3, n),
    class4 = rep(delta4, n),
    class5 = rep(delta5, n),
    class6 = rep(delta6, n),
    class7 = rep(delta7, n),
    class8 = rep(delta8, n)
  )
  expV <- lapply(V, exp)
  denom <- Reduce(`+`, expV)
  pi_values <- lapply(expV, function(ev) ev / denom)
  return(list(pi_values = pi_values))
}
apollo_probabilities = function(apollo_beta, apollo_inputs, functionality = "estimate") {
  apollo_attach(apollo_beta, apollo_inputs)
  on.exit(apollo_detach(apollo_beta, apollo_inputs))
  P <- list()
  
  for (k in 1:8) {
    V <- list()
    
    V[["alt1"]] = 0
    if (k != 2) V[["alt1"]] = V[["alt1"]] + beta_adverse   * adverse_react1
    if (k != 3) V[["alt1"]] = V[["alt1"]] + beta_protect   * protection_duration1
    if (k != 4) V[["alt1"]] = V[["alt1"]] + beta_admin     * administration1
    if (k != 5) V[["alt1"]] = V[["alt1"]] + beta_doses     * doses1
    if (k != 6) V[["alt1"]] = V[["alt1"]] + beta_price     * price1
    if (k != 7) V[["alt1"]] = V[["alt1"]] + beta_pop_fam   * target_fam1
    if (k != 8) V[["alt1"]] = V[["alt1"]] + beta_pop_child * target_hijos1
    
    V[["alt2"]] = asc2
    if (k != 2) V[["alt2"]] = V[["alt2"]] + beta_adverse   * adverse_react2
    if (k != 3) V[["alt2"]] = V[["alt2"]] + beta_protect   * protection_duration2
    if (k != 4) V[["alt2"]] = V[["alt2"]] + beta_admin     * administration2
    if (k != 5) V[["alt2"]] = V[["alt2"]] + beta_doses     * doses2
    if (k != 6) V[["alt2"]] = V[["alt2"]] + beta_price     * price2
    if (k != 7) V[["alt2"]] = V[["alt2"]] + beta_pop_fam   * target_fam2
    if (k != 8) V[["alt2"]] = V[["alt2"]] + beta_pop_child * target_hijos2
    
    mnl_settings = list(
      alternatives = c(alt1 = 1, alt2 = 2),
      avail        = list(alt1 = 1, alt2 = 1),
      choiceVar    = choice,
      utilities    = V,
      componentName = paste0("Class_", k)
    )
    
    P[[paste0("Class_", k)]] = apollo_mnl(mnl_settings, functionality)
  }
  
  lcpars = apollo_inputs$apollo_lcPars(apollo_beta, apollo_inputs)
  lc_settings = list(
    inClassProb = P,
    classProb = lcpars$pi_values,
    componentName = "lc"
  )
  
  P[["model"]] = apollo_lc(lc_settings, apollo_inputs, functionality)
  P[["model"]] = apollo_panelProd(P[["model"]], apollo_inputs, functionality)
  P = apollo_prepareProb(P, apollo_inputs, functionality)
  return(P)
}
apollo_inputs = apollo_validateInputs()
apollo_inputs$apollo_lcPars <- apollo_lcPars

model = apollo_estimate(apollo_beta, apollo_fixed, apollo_probabilities, apollo_inputs)
apollo_modelOutput(model)

And this the output:

Code: Select all

> apollo_inputs = apollo_validateInputs()
apollo_draws and apollo_randCoeff were found, so apollo_control$mixing was set to TRUE
All checks on apollo_control completed.
All checks on database completed.
Generating inter-individual draws ....... Done
> apollo_inputs$apollo_lcPars <- apollo_lcPars
> 
> model = apollo_estimate(apollo_beta, apollo_fixed, apollo_probabilities, apollo_inputs)
Preparing user-defined functions.
Error in test && (as.character(e[[3]][[1]]) %in% c("apollo_mnl", "apollo_el",  : 
  'length = 3' in coercion to 'logical(1)'[code]

Do you have any idea where the error might come from? I appreciate your time and help.

Best,
Sophie

Re: Error in LC MMNL

Posted: 28 May 2025, 12:55
by stephanehess
Hi

the if statements in your code might be causing the issue.

Maybe try:

Code: Select all

V[["alt1"]] = 0 +
    (k != 2) * beta_adverse   * adverse_react1 +
    (k != 3) * beta_protect   * protection_duration1
    ...
    

Re: Error in LC MMNL

Posted: 28 May 2025, 17:44
by fsophie
Dear Stephane,

Thank you very much for your timely response.

I updated the code accordingly, but encountered the same error message. Overall, I am doubting if this model specification is equivalent to your LC-MMNL in your 2012 paper - inferring attribute non-attendance with the latent classes while accounting for preference heterogeneity using a mixed multinomial model. I could not find an example in the Apollo guide or on this forum. Again, I appreciate your help a lot.

Code: Select all

apollo_initialise()

apollo_control = list(
  modelName       = "LCMMNL_Model",
  modelDescr      = "Latent class mixed multinomial logit",
  indivID         = "pat_id",
  outputDirectory = "output",
  nCores          = 4,
  panelData       = TRUE,
  mixing          = TRUE
)
packageVersion("apollo")

apollo_beta = c(
  asc2 = 0,
  asc1 = 0,
  
  mu_adverse = -0.3,
  mu_protect = 0.2,
  mu_admin  = 0.1,
  mu_doses  = -0.1,
  mu_price  = -0.5,
  mu_popfam = 0.15,
  mu_popchild = 0.1,
  
  sigma_adverse = 0.1,
  sigma_protect = 0.1,
  sigma_admin   = 0.1,
  sigma_doses   = 0.1,
  sigma_price   = 0.1,
  sigma_popfam  = 0.1,
  sigma_popchild= 0.1,
  
  delta2 = -2,
  delta3 = -1,
  delta4 = 0,
  delta5 = 1,
  delta6 = 2,
  delta7 = 3,
  delta8 = 4
)

apollo_fixed = c("asc1")

apollo_draws = list(
  interDrawsType = "sobol",
  interNDraws = 500,
  interNormDraws = c("draws_adverse", "draws_protect", "draws_admin",
                     "draws_doses", "draws_price", "draws_popfam", "draws_popchild")
)
apollo_randCoeff = function(apollo_beta, apollo_inputs) {
  with(as.list(apollo_beta), {
    list(
      beta_adverse   = mu_adverse   + sigma_adverse   * draws_adverse,
      beta_protect   = mu_protect   + sigma_protect   * draws_protect,
      beta_admin     = mu_admin     + sigma_admin     * draws_admin,
      beta_doses     = mu_doses     + sigma_doses     * draws_doses,
      beta_price     = mu_price     + sigma_price     * draws_price,
      beta_pop_fam   = mu_popfam    + sigma_popfam    * draws_popfam,
      beta_pop_child = mu_popchild  + sigma_popchild  * draws_popchild
    )
  })
}
apollo_lcPars = function(apollo_beta, apollo_inputs) {
  n <- nrow(apollo_inputs$database)
  V <- list(
    class1 = rep(0, n),
    class2 = rep(delta2, n),
    class3 = rep(delta3, n),
    class4 = rep(delta4, n),
    class5 = rep(delta5, n),
    class6 = rep(delta6, n),
    class7 = rep(delta7, n),
    class8 = rep(delta8, n)
  )
  expV <- lapply(V, exp)
  denom <- Reduce(`+`, expV)
  pi_values <- lapply(expV, function(ev) ev / denom)
  return(list(pi_values = pi_values))
}
apollo_probabilities = function(apollo_beta, apollo_inputs, functionality = "estimate") {
  apollo_attach(apollo_beta, apollo_inputs)
  on.exit(apollo_detach(apollo_beta, apollo_inputs))
  P <- list()
  
  for (k in 1:8) {
    V <- list()
    
    V[["alt1"]] = 0 +
     (k != 2) * beta_adverse   * adverse_react1 +
     (k != 3) * beta_protect   * protection_duration1 +
     (k != 4) * beta_admin     * administration1 +
     (k != 5) * beta_doses     * doses1 +
     (k != 6) * beta_price     * price1 +
     (k != 7) * beta_pop_fam   * target_fam1 +
     (k != 8) * beta_pop_child * target_hijos1 
    
    V[["alt2"]] = asc2 +
     (k != 2) * beta_adverse   * adverse_react2 +
     (k != 3) * beta_protect   * protection_duration2+
     (k != 4) * beta_admin     * administration2+
     (k != 5) * beta_doses     * doses2+
     (k != 6) * beta_price     * price2+
     (k != 7) * beta_pop_fam   * target_fam2+
     (k != 8) * beta_pop_child * target_hijos2
    
    mnl_settings = list(
      alternatives = c(alt1 = 1, alt2 = 2),
      avail        = list(alt1 = 1, alt2 = 1),
      choiceVar    = choice,
      utilities    = V,
      componentName = paste0("Class_", k)
    )
    
    P[[paste0("Class_", k)]] = apollo_mnl(mnl_settings, functionality)
  }
  
  lcpars = apollo_inputs$apollo_lcPars(apollo_beta, apollo_inputs)
  lc_settings = list(
    inClassProb = P,
    classProb = lcpars$pi_values,
    componentName = "lc"
  )
  
  P[["model"]] = apollo_lc(lc_settings, apollo_inputs, functionality)
  P[["model"]] = apollo_panelProd(P[["model"]], apollo_inputs, functionality)
  P = apollo_prepareProb(P, apollo_inputs, functionality)
  return(P)
}
apollo_inputs = apollo_validateInputs()
apollo_inputs$apollo_lcPars <- apollo_lcPars

model = apollo_estimate(apollo_beta, apollo_fixed, apollo_probabilities, apollo_inputs)
apollo_modelOutput(model)
Error:

Code: Select all

> apollo_inputs$apollo_lcPars <- apollo_lcPars
> 
> model = apollo_estimate(apollo_beta, apollo_fixed, apollo_probabilities, apollo_inputs)
Preparing user-defined functions.
Error in test && (as.character(e[[3]][[1]]) %in% c("apollo_mnl", "apollo_el",  : 
  'length = 3' in coercion to 'logical(1)'
> 
Best,
Sophie

Re: Error in LC MMNL

Posted: 29 May 2025, 07:40
by stephanehess
Hi

are you able to share your code and data with me and I'll have a look?

Stephane

Re: Error in LC MMNL

Posted: 29 May 2025, 10:38
by fsophie
Dear Stephane,

As I cannot share the data, I tried to simulate it, including non-attendance affecting two attributes. (Please note: in the final model, all attribute levels will be dummy-coded.)

Code: Select all

# ################################################################# #
# ### LC-MMNL - using simulated data                                                      ### #
# ################################################################# #
#rm(list = ls())
#rm(pat_id, choice_set, choice)

library(apollo)

set.seed(123456)
# parameters
n_indiv <- 100
n_sets <- 6
n_alts <- 2
n_rows <- n_indiv * n_sets

# simulate attributes for each alterntaive 
simulate_attributes <- function(n) {
  data.frame(
    adverse_react = sample(0:2, n, replace = TRUE, prob = c(0.3, 0.4, 0.3)),
    protection_duration = sample(0:2, n, replace = TRUE),
    administration = sample(0:1, n, replace = TRUE),
    doses = sample(2:4, n, replace = TRUE),
    price = sample(c(0, 0.5, 1), n, replace = TRUE),
    target_fam = sample(0:1, n, replace = TRUE),
    target_hijos = sample(0:1, n, replace = TRUE)
  )
}

#  attributes
attr_alt1 <- simulate_attributes(n_rows)
attr_alt2 <- simulate_attributes(n_rows)

# data frame structure
data <- data.frame(
  pat_id = rep(1:n_indiv, each = n_sets),
  choice_set = rep(1:n_sets, times = n_indiv)
)

# ANA: 20% of individuals shall ignore target population & number of doses
data$ANA_group <- rbinom(n_indiv, 1, 0.2) 
data <- data[rep(1:nrow(data), each = 1), ]
ana_flag <- rep(data$ANA_group, each = 1)

# True coefficients
coef <- list(
  adverse_react = -0.3,
  protection_duration = 0.2,
  administration = 0.1,
  doses = -0.1,
  price = -0.5,
  target_fam = 0.15,
  target_hijos = 0.1
)

# coefficients if in ANA group
coef$doses <- ifelse(ana_flag == 1, 0, coef$doses)
coef$target_fam <- ifelse(ana_flag == 1, 0, coef$target_fam)
coef$target_hijos <- ifelse(ana_flag == 1, 0, coef$target_hijos)

# utilities
utility1 <- with(attr_alt1,
                 coef$adverse_react * adverse_react +
                   coef$protection_duration * protection_duration +
                   coef$administration * administration +
                   coef$doses * doses +
                   coef$price * price +
                   coef$target_fam * target_fam +
                   coef$target_hijos * target_hijos +
                   rnorm(n_rows, 0, 0.1)
)

utility2 <- with(attr_alt2,
                 coef$adverse_react * adverse_react +
                   coef$protection_duration * protection_duration +
                   coef$administration * administration +
                   coef$doses * doses +
                   coef$price * price +
                   coef$target_fam * target_fam +
                   coef$target_hijos * target_hijos +
                   rnorm(n_rows, 0, 0.1)
)

# Determine choice
choice <- ifelse(utility1 > utility2, 1, 2)

# whole dataset
wide_data <- cbind(
  data,
  adverse_react1 = attr_alt1$adverse_react,
  protection_duration1 = attr_alt1$protection_duration,
  administration1 = attr_alt1$administration,
  doses1 = attr_alt1$doses,
  price1 = attr_alt1$price,
  target_fam1 = attr_alt1$target_fam,
  target_hijos1 = attr_alt1$target_hijos,
  
  adverse_react2 = attr_alt2$adverse_react,
  protection_duration2 = attr_alt2$protection_duration,
  administration2 = attr_alt2$administration,
  doses2 = attr_alt2$doses,
  price2 = attr_alt2$price,
  target_fam2 = attr_alt2$target_fam,
  target_hijos2 = attr_alt2$target_hijos,
  
  choice = choice
)
summary(wide_data)
database <- wide_data
rm(list = setdiff(ls(), "database"))

# ################################################################# #
# ### INITIALISE APOLLO                                                                           ### #
# ################################################################# #

apollo_initialise()

apollo_control = list(
  modelName       = "LCMMNL_Model",
  modelDescr      = "Latent class mixed multinomial logit",
  indivID         = "pat_id",
  outputDirectory = "output",
  nCores          = 4,
  panelData       = TRUE,
  mixing          = TRUE
)

# ################################################################# #
# ### PARAMETERS                                                                                   ### #
# ################################################################# #

apollo_beta = c(
  asc2 = 0,
  asc1 = 0,
  
  mu_adverse = -0.3,
  mu_protect = 0.2,
  mu_admin  = 0.1,
  mu_doses  = -0.1,
  mu_price  = -0.5,
  mu_popfam = 0.15,
  mu_popchild = 0.1,
  
  sigma_adverse = 0.1,
  sigma_protect = 0.1,
  sigma_admin   = 0.1,
  sigma_doses   = 0.1,
  sigma_price   = 0.1,
  sigma_popfam  = 0.1,
  sigma_popchild= 0.1,
  
  delta2 = -2,
  delta3 = -1,
  delta4 = 0,
  delta5 = 1,
  delta6 = 2,
  delta7 = 3,
  delta8 = 4
)

apollo_fixed = c("asc1")

apollo_draws = list(
  interDrawsType = "sobol",
  interNDraws = 500,
  interNormDraws = c("draws_adverse", "draws_protect", "draws_admin",
                     "draws_doses", "draws_price", "draws_popfam", "draws_popchild")
)
apollo_randCoeff = function(apollo_beta, apollo_inputs) {
  with(as.list(apollo_beta), {
    list(
      beta_adverse   = mu_adverse   + sigma_adverse   * draws_adverse,
      beta_protect   = mu_protect   + sigma_protect   * draws_protect,
      beta_admin     = mu_admin     + sigma_admin     * draws_admin,
      beta_doses     = mu_doses     + sigma_doses     * draws_doses,
      beta_price     = mu_price     + sigma_price     * draws_price,
      beta_pop_fam   = mu_popfam    + sigma_popfam    * draws_popfam,
      beta_pop_child = mu_popchild  + sigma_popchild  * draws_popchild
    )
  })
}
apollo_lcPars = function(apollo_beta, apollo_inputs) {
  n <- nrow(apollo_inputs$database)
  V <- list(
    class1 = rep(0, n),
    class2 = rep(delta2, n),
    class3 = rep(delta3, n),
    class4 = rep(delta4, n),
    class5 = rep(delta5, n),
    class6 = rep(delta6, n),
    class7 = rep(delta7, n),
    class8 = rep(delta8, n)
  )
  expV <- lapply(V, exp)
  denom <- Reduce(`+`, expV)
  pi_values <- lapply(expV, function(ev) ev / denom)
  return(list(pi_values = pi_values))
}

# ################################################################# #
# ### DEFINE MODEL AND LIKELIHOOD FUNCTION                                       ### #
# ################################################################# #

apollo_probabilities = function(apollo_beta, apollo_inputs, functionality = "estimate") {
  apollo_attach(apollo_beta, apollo_inputs)
  on.exit(apollo_detach(apollo_beta, apollo_inputs))
  P <- list()
  
  for (k in 1:8) {
    V <- list()
    
    V[["alt1"]] = 0 +
     (k != 2) * beta_adverse   * adverse_react1 +
     (k != 3) * beta_protect   * protection_duration1 +
     (k != 4) * beta_admin     * administration1 +
     (k != 5) * beta_doses     * doses1 +
     (k != 6) * beta_price     * price1 +
     (k != 7) * beta_pop_fam   * target_fam1 +
     (k != 8) * beta_pop_child * target_hijos1 
    
    V[["alt2"]] = asc2 +
     (k != 2) * beta_adverse   * adverse_react2 +
     (k != 3) * beta_protect   * protection_duration2+
     (k != 4) * beta_admin     * administration2+
     (k != 5) * beta_doses     * doses2+
     (k != 6) * beta_price     * price2+
     (k != 7) * beta_pop_fam   * target_fam2+
     (k != 8) * beta_pop_child * target_hijos2
    
    mnl_settings = list(
      alternatives = c(alt1 = 1, alt2 = 2),
      avail        = list(alt1 = 1, alt2 = 1),
      choiceVar    = choice,
      utilities    = V,
      componentName = paste0("Class_", k)
    )
    
    P[[paste0("Class_", k)]] = apollo_mnl(mnl_settings, functionality)
  }
  
  lcpars = apollo_inputs$apollo_lcPars(apollo_beta, apollo_inputs)
  lc_settings = list(
    inClassProb = P,
    classProb = lcpars$pi_values,
    componentName = "lc"
  )
  
  P[["model"]] = apollo_lc(lc_settings, apollo_inputs, functionality)
  P[["model"]] = apollo_panelProd(P[["model"]], apollo_inputs, functionality)
  P = apollo_prepareProb(P, apollo_inputs, functionality)
  return(P)
}

# ################################################################# #
# ### VALIDATE INPUTS                                                                            ### #
# ################################################################# #

apollo_inputs = apollo_validateInputs()
apollo_inputs$apollo_lcPars <- apollo_lcPars

# ################################################################# #
# ### MODEL ESTIMATION                                                                         ### #
# ################################################################# #

model = apollo_estimate(apollo_beta, apollo_fixed, apollo_probabilities, apollo_inputs)
apollo_modelOutput(model)
I appreciate your time and help a lot.

Kind regards,
Sophie

Re: Error in LC MMNL

Posted: 13 Jun 2025, 16:49
by stephanehess
Hi

there were quite a few problems with your code, including that you were referring to apollo_lcPars inside apollo_probabilities, but primarily also the way you created the list in apollo_randCoeff, which prevented Apollo from reading the syntax in a way it could add the gradients.

This now works:

Code: Select all

# ################################################################# #
# ### LC-MMNL - using simulated data                                                      ### #
# ################################################################# #
#rm(list = ls())
#rm(pat_id, choice_set, choice)

library(apollo)

set.seed(123456)
# parameters
n_indiv <- 100
n_sets <- 6
n_alts <- 2
n_rows <- n_indiv * n_sets

# simulate attributes for each alterntaive 
simulate_attributes <- function(n) {
  data.frame(
    adverse_react = sample(0:2, n, replace = TRUE, prob = c(0.3, 0.4, 0.3)),
    protection_duration = sample(0:2, n, replace = TRUE),
    administration = sample(0:1, n, replace = TRUE),
    doses = sample(2:4, n, replace = TRUE),
    price = sample(c(0, 0.5, 1), n, replace = TRUE),
    target_fam = sample(0:1, n, replace = TRUE),
    target_hijos = sample(0:1, n, replace = TRUE)
  )
}

#  attributes
attr_alt1 <- simulate_attributes(n_rows)
attr_alt2 <- simulate_attributes(n_rows)

# data frame structure
data <- data.frame(
  pat_id = rep(1:n_indiv, each = n_sets),
  choice_set = rep(1:n_sets, times = n_indiv)
)

# ANA: 20% of individuals shall ignore target population & number of doses
data$ANA_group <- rbinom(n_indiv, 1, 0.2) 
data <- data[rep(1:nrow(data), each = 1), ]
ana_flag <- rep(data$ANA_group, each = 1)

# True coefficients
coef <- list(
  adverse_react = -0.3,
  protection_duration = 0.2,
  administration = 0.1,
  doses = -0.1,
  price = -0.5,
  target_fam = 0.15,
  target_hijos = 0.1
)

# coefficients if in ANA group
coef$doses <- ifelse(ana_flag == 1, 0, coef$doses)
coef$target_fam <- ifelse(ana_flag == 1, 0, coef$target_fam)
coef$target_hijos <- ifelse(ana_flag == 1, 0, coef$target_hijos)

# utilities
utility1 <- with(attr_alt1,
                 coef$adverse_react * adverse_react +
                   coef$protection_duration * protection_duration +
                   coef$administration * administration +
                   coef$doses * doses +
                   coef$price * price +
                   coef$target_fam * target_fam +
                   coef$target_hijos * target_hijos +
                   rnorm(n_rows, 0, 0.1)
)

utility2 <- with(attr_alt2,
                 coef$adverse_react * adverse_react +
                   coef$protection_duration * protection_duration +
                   coef$administration * administration +
                   coef$doses * doses +
                   coef$price * price +
                   coef$target_fam * target_fam +
                   coef$target_hijos * target_hijos +
                   rnorm(n_rows, 0, 0.1)
)

# Determine choice
choice <- ifelse(utility1 > utility2, 1, 2)

# whole dataset
wide_data <- cbind(
  data,
  adverse_react1 = attr_alt1$adverse_react,
  protection_duration1 = attr_alt1$protection_duration,
  administration1 = attr_alt1$administration,
  doses1 = attr_alt1$doses,
  price1 = attr_alt1$price,
  target_fam1 = attr_alt1$target_fam,
  target_hijos1 = attr_alt1$target_hijos,
  
  adverse_react2 = attr_alt2$adverse_react,
  protection_duration2 = attr_alt2$protection_duration,
  administration2 = attr_alt2$administration,
  doses2 = attr_alt2$doses,
  price2 = attr_alt2$price,
  target_fam2 = attr_alt2$target_fam,
  target_hijos2 = attr_alt2$target_hijos,
  
  choice = choice
)
summary(wide_data)
database <- wide_data
rm(list = setdiff(ls(), "database"))

# ################################################################# #
# ### INITIALISE APOLLO                                                                           ### #
# ################################################################# #

apollo_initialise()

apollo_control = list(
  modelName       = "LCMMNL_Model",
  modelDescr      = "Latent class mixed multinomial logit",
  indivID         = "pat_id",
  outputDirectory = "output",
  nCores          = 4,
  panelData       = TRUE,
  mixing          = TRUE,
  noValidation    = TRUE
)

# ################################################################# #
# ### PARAMETERS                                                                                   ### #
# ################################################################# #

apollo_beta = c(
  asc2 = 0,
  asc1 = 0,
  
  mu_adverse = -0.3,
  mu_protect = 0.2,
  mu_admin  = 0.1,
  mu_doses  = -0.1,
  mu_price  = -0.5,
  mu_popfam = 0.15,
  mu_popchild = 0.1,
  
  sigma_adverse = 0.1,
  sigma_protect = 0.1,
  sigma_admin   = 0.1,
  sigma_doses   = 0.1,
  sigma_price   = 0.1,
  sigma_popfam  = 0.1,
  sigma_popchild= 0.1,
  
  delta2 = -2,
  delta3 = -1,
  delta4 = 0,
  delta5 = 1,
  delta6 = 2,
  delta7 = 3,
  delta8 = 4
)

apollo_fixed = c("asc1")

apollo_draws = list(
  interDrawsType = "sobol",
  interNDraws = 500,
  interNormDraws = c("draws_adverse", "draws_protect", "draws_admin",
                     "draws_doses", "draws_price", "draws_popfam", "draws_popchild")
)
apollo_randCoeff = function(apollo_beta, apollo_inputs) {
  #with(as.list(apollo_beta), {
    randCoeff=list()
      randCoeff[["beta_adverse"]]   = mu_adverse   + sigma_adverse   * draws_adverse
      randCoeff[["beta_protect"]]   = mu_protect   + sigma_protect   * draws_protect
      randCoeff[["beta_admin"]]     = mu_admin     + sigma_admin     * draws_admin
      randCoeff[["beta_doses"]]     = mu_doses     + sigma_doses     * draws_doses
      randCoeff[["beta_price"]]     = mu_price     + sigma_price     * draws_price
      randCoeff[["beta_pop_fam"]]   = mu_popfam    + sigma_popfam    * draws_popfam
      randCoeff[["beta_pop_child"]] = mu_popchild  + sigma_popchild  * draws_popchild
    #)
  #})
      return(randCoeff)
}
apollo_lcPars = function(apollo_beta, apollo_inputs) {
  #n <- nrow(apollo_inputs$database)
  V <- list(
    class1 = 0,
    class2 = delta2,
    class3 = delta3,
    class4 = delta4,
    class5 = delta5,
    class6 = delta6,
    class7 = delta7,
    class8 = delta8
  )
  
  ### Settings for class allocation models
  classAlloc_settings = list(
    classes      = setNames(1:8,paste0("Class_",1:8)), 
    utilities    = V  
  )
  
  lcpars = list()
  lcpars[["pi_values"]] = apollo_classAlloc(classAlloc_settings)
  
  return(lcpars)
  
}

apollo_inputs = apollo_validateInputs()

# ################################################################# #
# ### DEFINE MODEL AND LIKELIHOOD FUNCTION                                       ### #
# ################################################################# #

apollo_probabilities = function(apollo_beta, apollo_inputs, functionality = "estimate") {
  apollo_attach(apollo_beta, apollo_inputs)
  on.exit(apollo_detach(apollo_beta, apollo_inputs))
  P <- list()
  
  for (k in 1:8) {
    V <- list()
    
    V[["alt1"]] = 0 +
      (k != 2) * beta_adverse   * adverse_react1 +
      (k != 3) * beta_protect   * protection_duration1 +
      (k != 4) * beta_admin     * administration1 +
      (k != 5) * beta_doses     * doses1 +
      (k != 6) * beta_price     * price1 +
      (k != 7) * beta_pop_fam   * target_fam1 +
      (k != 8) * beta_pop_child * target_hijos1 
    
    V[["alt2"]] = asc2 +
      (k != 2) * beta_adverse   * adverse_react2 +
      (k != 3) * beta_protect   * protection_duration2+
      (k != 4) * beta_admin     * administration2+
      (k != 5) * beta_doses     * doses2+
      (k != 6) * beta_price     * price2+
      (k != 7) * beta_pop_fam   * target_fam2+
      (k != 8) * beta_pop_child * target_hijos2
    
    mnl_settings = list(
      alternatives = c(alt1 = 1, alt2 = 2),
      avail        = list(alt1 = 1, alt2 = 1),
      choiceVar    = choice,
      utilities    = V,
      componentName = paste0("Class_", k)
    )
    
    P[[paste0("Class_", k)]] = apollo_mnl(mnl_settings, functionality)
    P[[paste0("Class_", k)]] = apollo_panelProd(P[[paste0("Class_", k)]], apollo_inputs, functionality)
    P[[paste0("Class_", k)]] = apollo_avgInterDraws(P[[paste0("Class_", k)]], apollo_inputs, functionality)
    
    
  }
  
  #lcpars = apollo_inputs$apollo_lcPars(apollo_beta, apollo_inputs)
  lc_settings = list(
    inClassProb = P,
    classProb = pi_values,
    componentName = "lc"
  )
  
  P[["model"]] = apollo_lc(lc_settings, apollo_inputs, functionality)
  
  P = apollo_prepareProb(P, apollo_inputs, functionality)
  return(P)
}


# ################################################################# #
# ### MODEL ESTIMATION                                                                         ### #
# ################################################################# #

model = apollo_estimate(apollo_beta, apollo_fixed, apollo_probabilities, apollo_inputs)

Re: Error in LC MMNL

Posted: 20 Jun 2025, 15:36
by fsophie
Dear Stephane,

Thank you very much for your help, the model now runs without errors. However, it does not achieve estimation.

I tried to follow the suggestions from the FAQs and other blog posts, but have not found a solution. The MNL, MMNL and LC-MNL (with different numbers of classes) all run smoothly. The latter two indicate the presence of ANA and preference heterogeneity, so I hope to account for both using a LC-MMNL model. No matter which classes I define or which model I take the starting values from, the estimation fails. My database has roughly 9000 observations (12 observations per 750 individuals).

Please find here a slightly modified model with 2 classes only, plus output.

Again, thank you very much for your time.

Best,
Sophie

Code: Select all

# ################################################################# #
# ### INITIALISE APOLLO                                                                          ### #
# ################################################################# #

apollo_initialise()

apollo_control = list(
  modelName              = "LCMMNL_Model",
  modelDescr             = "Latent class mixed multinomial logit",
  indivID                = "id",
  outputDirectory        = "output",
  mixing                 = TRUE,
  panelData              = TRUE,
  noValidation           = TRUE,
  hb                     = FALSE,
  analyticGrad           = TRUE,      
  optimRoutine           = "bhhh",
  simulatePosteriors     = TRUE,
  nCores                 = 4
)

# ################################################################# #
# ### PARAMETERS                                                ### #
# ################################################################# #

apollo_beta = c(
  asc2 = -0.02,
  asc1 = 0,
  
  mu_AR_M      = -0.15,
  mu_AR_H      = -0.75,
  mu_pro2      = -0.34,
  mu_pro3      = -0.33,
  mu_admin     = -0.48,
  mu_doses     = -0.06,
  mu_price     = -2.1, 
  mu_popfam    = 1.18,
  mu_popchild  = 0.33,
  
  sigma_AR_M   = 0.01,
  sigma_AR_H   = -1.33,
  sigma_pro2   = 0.09,
  sigma_pro3   = 0.04,
  sigma_admin   = 0.1,
  sigma_doses   = -0.01,
  sigma_price   = -2, 
  sigma_popfam  = 1.67, 
  sigma_popchild= 0.76,
  
  delta2 = 1,
  delta3 = 2 
)

apollo_fixed = c("asc1" )

apollo_draws = list(
  interDrawsType = "sobol",
  interNDraws = 500,
  interNormDraws = c("draws_AR_M", "draws_AR_H", "draws_pro2", "draws_pro3", "draws_admin",
                     "draws_doses", "draws_price", "draws_popfam", "draws_popchild")
)
apollo_randCoeff = function(apollo_beta, apollo_inputs) {
  #with(as.list(apollo_beta), {
  randCoeff=list()
  randCoeff[["beta_AR_M"]]      = mu_AR_M   + sigma_AR_M   * draws_AR_M
  randCoeff[["beta_AR_H"]]      = mu_AR_H   + sigma_AR_H   * draws_AR_H
  randCoeff[["beta_pro2"]]      = mu_pro2   + sigma_pro2   * draws_pro2
  randCoeff[["beta_pro3"]]      = mu_pro3   + sigma_pro3   * draws_pro3
  randCoeff[["beta_admin"]]     = mu_admin         + sigma_admin     * draws_admin
  randCoeff[["beta_doses"]]     = mu_doses         + sigma_doses     * draws_doses
  randCoeff[["beta_price"]]     =-exp(mu_price     + sigma_price     * draws_price)
  randCoeff[["beta_pop_fam"]]   = mu_popfam    + sigma_popfam    * draws_popfam
  randCoeff[["beta_pop_child"]] = mu_popchild  + sigma_popchild  * draws_popchild
  #)
  #})
  return(randCoeff)
}
apollo_lcPars = function(apollo_beta, apollo_inputs) {
  V <- list(
    class1 = 0,
    class2 = delta2 
  )
  
  ### Settings for class allocation models
  classAlloc_settings = list(
    classes      = setNames(1:2, paste0("Class_",1:2)), 
    utilities    = V  
  )
  
  lcpars = list()
  lcpars[["pi_values"]] = apollo_classAlloc(classAlloc_settings)
  
  return(lcpars)
  
}
apollo_inputs = apollo_validateInputs()

# ################################################################# #
# ### DEFINE MODEL AND LIKELIHOOD FUNCTION                                      ### #
# ################################################################# #

apollo_probabilities = function(apollo_beta, apollo_inputs, functionality = "estimate") {
  apollo_attach(apollo_beta, apollo_inputs)
  on.exit(apollo_detach(apollo_beta, apollo_inputs))
  P <- list()
  
  for (k in 1:2) {
    V <- list()
    
    V[["alt1"]] = 0 +
      (k != 1) * (beta_AR_M*AR_M1 + beta_AR_H*AR_H1 + beta_admin*administration1 ) +
      (k != 2) * (beta_price*price1 + beta_pop_fam*target_fam1 +
                    beta_pop_child*target_hijos1 + beta_doses*doses1 + beta_pro2*pro_21 + beta_pro3*pro_31)   
    
    V[["alt2"]] = asc2 +
      (k != 1) * (beta_AR_M*AR_M2 + beta_AR_H*AR_H2 + beta_admin*administration2 ) +
      (k != 2) * (beta_price*price2 + beta_pop_fam*target_fam2 +
                    beta_pop_child*target_hijos2 + beta_doses*doses2 + beta_pro2*pro_22 + beta_pro3*pro_32 ) 
    
    mnl_settings = list(
      alternatives = c(alt1 = 1, alt2 = 2),
      avail        = list(alt1 = 1, alt2 = 1),
      choiceVar    = choice,
      utilities    = V,
      componentName = paste0("Class_", k)
    )
    
    P[[paste0("Class_", k)]] = apollo_mnl(mnl_settings, functionality)
    P[[paste0("Class_", k)]] = apollo_panelProd(P[[paste0("Class_", k)]], apollo_inputs, functionality)
    P[[paste0("Class_", k)]] = apollo_avgInterDraws(P[[paste0("Class_", k)]], apollo_inputs, functionality)
  }

  lc_settings = list(
    inClassProb = P,
    classProb = pi_values,
    componentName = "lc"
  )
  
  P[["model"]] = apollo_lc(lc_settings, apollo_inputs, functionality)
  
  P = apollo_prepareProb(P, apollo_inputs, functionality)
  return(P)
}

# ################################################################# #
# ### MODEL ESTIMATION                                                                         ### #
# ################################################################# #

model = apollo_estimate(apollo_beta, apollo_fixed, apollo_probabilities, apollo_inputs)
apollo_modelOutput(model)
beep() 
output:

Code: Select all

Preparing user-defined functions.

Testing likelihood function...
Apollo found a model component of type classAlloc without a componentName. The name was set to "classAlloc" by default.
INFORMATION: Setting "avail" is missing, so full availability is assumed. 

Overview of choices for MNL model component Class_1:
                                    alt1    alt2
Times available                  9229.00 9229.00
Times chosen                     4510.00 4719.00
Percentage chosen overall          48.87   51.13
Percentage chosen when available   48.87   51.13


Overview of choices for MNL model component Class_2:
                                    alt1    alt2
Times available                  9229.00 9229.00
Times chosen                     4510.00 4719.00
Percentage chosen overall          48.87   51.13
Percentage chosen when available   48.87   51.13


Summary of class allocation for model component lc:
         Mean prob.
Class_1      0.2689
Class_2      0.7311

Pre-processing likelihood function...
Creating cluster...
Preparing workers for multithreading...

Starting main estimation

BGW using analytic model derivatives supplied by caller...


Iterates will be written to: 
 output/LCMMNL_Model_iterations.csv
    it    nf     F            RELDF    PRELDF    RELDX    MODEL stppar
     0     1 5.838129284e+03
     1     2 5.824184660e+03 2.389e-03 4.947e-03 7.97e-03   G   2.93e+01
     2     3 5.751017654e+03 1.256e-02 1.389e-02 2.33e-02   G   8.67e+00
     3     4 5.466665261e+03 4.944e-02 3.619e-02 1.07e-01   G   7.68e-01
     4     5 5.456751150e+03 1.814e-03 3.696e-02 3.05e-01   G   -1.16e-04
     5     6 5.157053379e+03 5.492e-02 4.915e-02 1.42e-01   G   1.64e-01
     6     7 5.022483071e+03 2.609e-02 2.307e-02 1.36e-01   G   4.51e-02
     7     8 4.906926088e+03 2.301e-02 3.789e-02 1.31e-01   S   3.24e-01
     8     9 4.745316775e+03 3.293e-02 3.672e-02 1.55e-01   S   2.64e-01
     9    10 4.606802142e+03 2.919e-02 2.432e-02 1.72e-01   S   2.64e-04
    10    12 4.539523129e+03 1.460e-02 1.156e-02 1.17e-01  S-G  -1.57e-05
    11    14 4.504900076e+03 7.627e-03 8.190e-03 5.02e-02   S   1.95e+00
    12    15 4.469144516e+03 7.937e-03 8.213e-03 1.21e-01   S   1.66e-01
    13    16 4.462050097e+03 1.587e-03 2.587e-03 1.48e-01   S   1.66e-04
    14    18 4.459299036e+03 6.165e-04 6.658e-04 5.50e-02  G-S  -1.66e-04
    15    19 4.458218208e+03 2.424e-04 1.999e-04 1.83e-02   S   -1.66e-07
    16    20 4.457984899e+03 5.233e-05 4.817e-05 1.68e-02   S   -1.66e-07
    17    22 4.457941502e+03 9.735e-06 7.571e-06 7.98e-03  G-S  -1.66e-07
    18    23 4.457913239e+03 6.340e-06 5.449e-06 8.02e-03   S   -1.66e-07
    19    24 4.457891696e+03 4.832e-06 3.313e-06 5.32e-03   S   -1.66e-07
    20    25 4.457875120e+03 3.718e-06 4.002e-06 8.96e-03   S   -1.66e-07
    21    28 4.457853062e+03 4.948e-06 5.429e-06 2.45e-03  S-G  7.88e-01
    22    29 4.457838173e+03 3.340e-06 3.206e-06 3.40e-03   G   2.41e-01
    23    30 4.457813746e+03 5.480e-06 6.444e-06 9.59e-03   S   5.49e-02
    24    31 4.457788379e+03 5.690e-06 4.131e-06 9.00e-03   S   5.49e-05
    25    32 4.457583627e+03 4.593e-05 8.993e-06 1.18e-02   S   5.14e-02
    26    33 4.457226334e+03 8.015e-05 4.077e-05 1.64e-02   S   -5.34e-06
    27    34 4.456992034e+03 5.257e-05 5.523e-05 2.84e-02   S   -5.34e-06
    28    35 4.456923229e+03 1.544e-05 1.422e-05 6.34e-03   S   -5.34e-06
    29    36 4.456886897e+03 8.152e-06 6.391e-06 3.53e-03   S   -5.34e-06
    30    37 4.456863645e+03 5.217e-06 4.255e-06 5.77e-03   S   -5.34e-06
    31    38 4.456857509e+03 1.377e-06 1.737e-06 4.32e-03   S   -5.34e-09
    32    39 4.456855638e+03 4.198e-07 4.113e-07 1.04e-03   S   -5.34e-09
    33    40 4.456855304e+03 7.483e-08 7.068e-08 4.43e-04   S   -5.34e-09
    34    42 4.456855216e+03 1.982e-08 2.089e-08 2.75e-04  G-S  -5.34e-09
    35    43 4.456855201e+03 3.255e-09 3.212e-09 1.50e-04   S   -5.34e-09
    36    44 4.456855198e+03 7.741e-10 6.924e-10 7.00e-05   S   -5.34e-12
    37    45 4.456855198e+03 5.710e-11 5.133e-11 1.56e-05   S   -5.34e-15

***** Singular convergence *****

Estimated parameters:
                  Estimate
asc2              -0.08708
asc1               0.00000
mu_AR_M           -1.53950
mu_AR_H           -4.45696
mu_pro2           -0.48017
mu_pro3           -0.42803
mu_admin           3.09410
mu_doses          -0.01659
mu_price          -0.02901
mu_popfam          1.37709
mu_popchild        0.29563
sigma_AR_M         0.89924
sigma_AR_H        -3.23348
sigma_pro2        -0.18494
sigma_pro3        -0.04137
sigma_admin        7.22568
sigma_doses       -0.02211
sigma_price       -1.37562
sigma_popfam       1.17347
sigma_popchild     0.89563
delta2            -0.52038
delta3             2.00000

Final LL: -4456.8552

WARNING: Estimation failed. No covariance matrix to compute. 

Summary of class allocation for model component lc:
         Mean prob.
Class_1      0.6272
Class_2      0.3728

Calculating log-likelihood at equal shares (LL(0)) for applicable models...
Calculating log-likelihood at observed shares from estimation data (LL(c)) for applicable models...
Calculating LL of each model component...
Calculating other model fit measures

Your model was estimated using the BGW algorithm. Please acknowledge this by citing Bunch et al. (1993) - doi.org/10.1145/151271.151279

Please acknowledge the use of Apollo by citing Hess & Palma (2019) - doi.org/10.1016/j.jocm.2019.100170
> apollo_modelOutput(model)
Model run by fsophie using Apollo 0.3.5 on R 4.4.2 for Windows.
Please acknowledge the use of Apollo by citing Hess & Palma (2019)
  DOI 10.1016/j.jocm.2019.100170
  www.ApolloChoiceModelling.com

Model name                                  : LCMMNL_Model
Model description                           : Latent class mixed multinomial logit
Model run at                                : 2025-06-20 16:23:50.03629
Estimation method                           : bgw
Model diagnosis                             : Singular convergence
Number of individuals                       : 770
Number of rows in database                  : 9229
Number of modelled outcomes                 : 9229

Number of cores used                        :  4 
Number of inter-individual draws            : 500 (sobol)

LL(start)                                   : -5838.13
LL (whole model) at equal shares, LL(0)     : -6397.06
LL (whole model) at observed shares, LL(C)  : -6394.69
LL(final, whole model)                      : -4456.86
Rho-squared vs equal shares                  :  0.3033 
Adj.Rho-squared vs equal shares              :  0.3 
Rho-squared vs observed shares               :  0.303 
Adj.Rho-squared vs observed shares           :  0.3001 
AIC                                         :  8955.71 
BIC                                         :  9105.44 

LL(0,Class_1)                    : -6397.06
LL(final,Class_1)                : -6004.39
LL(0,Class_2)                    : -6397.06
LL(final,Class_2)                : -7191.44

Estimated parameters                        : 21
Time taken (hh:mm:ss)                       :  00:07:25.27 
     pre-estimation                         :  00:01:3.8 
     estimation                             :  00:06:7.01 
     post-estimation                        :  00:00:14.46 
Iterations                                  :  37 (Singular convergence) 

Unconstrained optimisation.

Estimates:
                  Estimate        s.e.   t.rat.(0)    Rob.s.e. Rob.t.rat.(0)
asc2              -0.08708          NA          NA          NA            NA
asc1               0.00000          NA          NA          NA            NA
mu_AR_M           -1.53950          NA          NA          NA            NA
mu_AR_H           -4.45696          NA          NA          NA            NA
mu_pro2           -0.48017          NA          NA          NA            NA
mu_pro3           -0.42803          NA          NA          NA            NA
mu_admin           3.09410          NA          NA          NA            NA
mu_doses          -0.01659          NA          NA          NA            NA
mu_price          -0.02901          NA          NA          NA            NA
mu_popfam          1.37709          NA          NA          NA            NA
mu_popchild        0.29563          NA          NA          NA            NA
sigma_AR_M         0.89924          NA          NA          NA            NA
sigma_AR_H        -3.23348          NA          NA          NA            NA
sigma_pro2        -0.18494          NA          NA          NA            NA
sigma_pro3        -0.04137          NA          NA          NA            NA
sigma_admin        7.22568          NA          NA          NA            NA
sigma_doses       -0.02211          NA          NA          NA            NA
sigma_price       -1.37562          NA          NA          NA            NA
sigma_popfam       1.17347          NA          NA          NA            NA
sigma_popchild     0.89563          NA          NA          NA            NA
delta2            -0.52038          NA          NA          NA            NA
delta3             2.00000          NA          NA          NA            NA


Summary of class allocation for model component lc:
         Mean prob.
Class_1      0.6272
Class_2      0.3728

Re: Error in LC MMNL

Posted: 26 Jun 2025, 09:56
by stephanehess
You havea parameter delta3 that is not actually used in the model. That causes your identification issue