Identifying Non-Identifable Latent Positons

My Stan program:

// How It Works
// Initialization: The model initializes the combined rating differences (gamma) and the white player advantage (W) with normal priors.
// Likelihood Calculation: For each game, the model calculates the probability of the game outcome using the logistic function of the combined rating difference and the white player advantage.
// This model allows you to track the evolution of the combined player ratings and white player advantage over time. The ratings and advantages are dynamically updated based on the outcomes of the games, providing a flexible representation of the changing strengths of the players.

[edit: escaped Stan and R code]

data {
  int<lower=0> N;                     // number of games
  int<lower=1> P;                     // number of players
  array[N] int<lower=0, upper=1> y;   // results of games (1 if player i won, 0 if player j won)
  array[N] int<lower=1> i;            // player i index
  array[N] int<lower=1> j;            // player j index
  array[N] int<lower=0, upper=1> white;// indicator if player i played white
}

parameters {
  vector[N] gamma;  // Combined rating difference for each game
  vector[N] W;      // White player advantage for each game
}

model {
  // Priors
  gamma ~ normal(0, 1);  // Prior for the combined rating difference
  W ~ normal(0, 1);      // Prior for the white player advantage

  // Likelihood
  for (n in 1:N) {
    real eta;
    if (white[n] == 1) {
      eta = gamma[n] + W[n];  // Player i is White
    } else {
      eta = -gamma[n] - W[n];  // Player j is White
    }
    y[n] ~ bernoulli_logit(eta);  // Likelihood for game outcomes
  }
}

I compile this Stan model in R with this code:

# Compile the Stan model
modelName <- "chess_model_ranking_One_Gamma.stan"
mod <- cmdstan_model(modelName)

# Prepare initial values if needed (similar to your existing code)
num_chains <- 4
num_cores <- min(num_chains, parallel::detectCores())

gamma_init_values <- runif(P, min = 0, max = 1)
W_init_values <- runif(P, min = 0, max = 1)

init_list <- lapply(1:num_chains, function(x) {
  list(gamma = gamma_init_values,
       W = W_init_values)
})

# Fit the updated model
fit <- mod$sample(
  data = list(
    P = P,
    N = N,
    N_ij = data_list$N_ij,
    Y_ij = data_list$Y_ij,
    player_i = data_list$player_i,
    player_j = data_list$player_j
  ),
  chains = num_chains,
  parallel_chains = num_cores,
  iter_warmup = 1000,
  iter_sampling = 1000,
  seed = 123,
  init = init_list,
  refresh = 0,
  adapt_delta = 0.95,
  step_size = 0.01
)

On plotting the latent positions of the gamma and W latent variables, respectively, how can I identify non-identifiability??? I am not sure whether I need to use a Procrustes matching for my model.

Hi, @Patrick_O_Rourke and sorry for not seeing this sooner.

This is kind of like a Bradley-Terry model, but you’re not tying this back to player abilities. You have the player indexes as data, but they’re not used in the model.

As you’ve coded it, I think eta should always be gamma[n] + W[n]. But that’s just going to drive things to gamma = +/- infinity.

I would suggest looking up Bradley-Terry models.