The hardware and bandwidth for this mirror is donated by dogado GmbH, the Webhosting and Full Service-Cloud Provider. Check out our Wordpress Tutorial.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]dogado.de.

Nested comparisons with uncfam_general()

George Kachergis

2026-11-10

The biased associative model of Kachergis, Yu & Shiffrin (2012), uncfam(), has several extensions in this package. uncfam_general() is one model with each extension as a parameter, so the variants are special cases of it:

uncfam_general() setting Equivalent to What it adds
defaults uncfam() attention to pairs by familiarity × uncertainty
B = 0 uncfam(B = 0) familiarity only
gamma = 0 uncfam(variant = "uncertainty-only") uncertainty only
uncertainty = "novelty" uncfam(variant = "novelty") novelty in place of entropy
gamma free uncfam_gamma() how strongly familiarity drives attention
eps free uncfam_elimination() learning by elimination (mutual exclusivity)
kappa = 1 uncfam_attention() more learning on more uncertain trials
K finite uncfam_sampling() attend to K sampled objects per word
rho > 0 (none) a short-term trace: recent pairings draw attention

These aren’t approximations: uncfam() and the other variants are implemented as calls to the same model, and give identical results.

d <- xsl_datasets[[1]]
identical(xsl_run(uncfam(X = .2, B = 3, C = .97), d)$fits[[1]]$perf,
          xsl_run(uncfam_general(X = .2, B = 3, C = .97), d)$fits[[1]]$perf)
#> [1] TRUE

Because the variants are nested, they can be compared directly. Does freeing a parameter improve the fit enough to justify it?

What the extensions do

Elimination (eps). A pair gains weight to the extent that the trial’s other words and objects are already accounted for by each other. Here word 2 is learned first, then appears with novel word 1. Elimination says object 1 must be word 1’s:

elim_data <- xslData(train = list(words = list(2, 2, c(1, 2), c(1, 3)),
                                  objects = list(2, 2, c(1, 2), c(1, 3))))
sapply(c(eps_0 = 0, eps_2 = 2, eps_10 = 10), \(e)
  xsl_run(uncfam_general(X = 1, B = 1, C = 1, eps = e), elim_data)$fits[[1]]$perf)
#>       eps_0     eps_2    eps_10
#> 1 0.7513276 0.9538716 0.9681789
#> 2 0.9892157 0.9898250 0.9900613
#> 3 0.5338423 0.9449978 0.9684734

Short-term trace (rho, Cs). Every update also goes into a second matrix that decays quickly (by Cs per trial), and attention follows familiarity plus rho times this trace. Here word 1 meets object 1 twice, then object 2 twice, then both together. Without a trace the two objects end level; with one, the more recent pairing pulls ahead:

trace_data <- xslData(train = list(words = list(1, 1, 1, 1, 1),
                                   objects = list(1, 1, 2, 2, c(1, 2))))
sapply(c(rho_0 = 0, rho_5 = 5), \(r)
  xsl_run(uncfam_general(X = .5, B = 0, C = 1, rho = r, Cs = .5), trace_data)$fits[[1]]$matrix[1, ])
#>   rho_0    rho_5
#> 1  1.26 1.155173
#> 2  1.26 1.364827

Sampled attention (K). With a finite K, each word attends to K objects drawn in proportion to familiarity × uncertainty, so the model is stochastic. xsl_run() averages xslControl(n_sim = ) simulated learners:

set.seed(1)
xsl_run(uncfam_general(X = .2, B = 3, C = .97, K = 1), d, xslControl(n_sim = 50))$sse
#> [1] 1.872689

A nested comparison

Does freeing the familiarity exponent gamma improve on the original model? The comparison below fits both, by binomial likelihood, to two conditions: 212 (a 4AFC test) and 301 (24 words).

Two precautions keep the comparison fair. First, the larger model’s search starts from the smaller model’s best fit (with gamma = 1), so it can’t fit worse merely because its search got unlucky. Second, the search uses a fixed seed.

labels <- vapply(xsl_datasets, `[[`, "", "label")
ctrl <- xslControl(objective = "binomial", lapse = .02)

fit_nested <- function(data, free, lower, upper, start = NULL) {
  np <- 30
  pop <- NULL
  if (!is.null(start)) {
    set.seed(2)
    pop <- sapply(seq_along(lower), \(j) stats::runif(np, lower[j], upper[j]))
    pop[1, ] <- start
  }
  xsl_fit(uncfam_general(X = .1, B = 1, C = 1), data, lower = lower, upper = upper,
          free = free, control = ctrl, seed = 1,
          deoptim_control = DEoptim::DEoptim.control(NP = np, itermax = 25, trace = FALSE,
                                                     initialpop = pop))[[1]]
}

compare_gamma <- function(label) {
  data <- xsl_datasets[[which(labels == label)]]
  bam <- fit_nested(data, c("X", "B", "C"), c(.001, .1, .5), c(5, 15, 1))
  gam <- fit_nested(data, c("X", "B", "C", "gamma"), c(.001, .1, .5, 0), c(5, 15, 1, 6),
                    start = c(bam$optim$bestmem, gamma = 1))
  lr <- 2 * (gam$loglik - bam$loglik)
  data.frame(condition = label, gamma = gam$optim$bestmem[["gamma"]],
             delta_AIC = gam$aic - bam$aic, LR = lr,
             p = stats::pchisq(lr, df = 1, lower.tail = FALSE))
}
res <- do.call(rbind, lapply(c("212", "301"), compare_gamma))
res
#>   condition      gamma  delta_AIC        LR            p
#> 1       212 2.81229618 -9.3590162 11.359016 0.0007508258
#> 2       301 0.04337925 -0.8448739  2.844874 0.0916657856

The two conditions pull gamma in opposite directions:

A single gamma fit across many conditions would average these. Fitting the nested variants condition by condition can reveal such differences, where a single group fit hides them.

Two caveats:

These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.
Health stats visible at Monitor.