paul-buerkner / brms

brms R package for Bayesian generalized multivariate non-linear multilevel models using Stan
https://paul-buerkner.github.io/brms/
GNU General Public License v2.0
1.25k stars 177 forks source link

cannot use tan_half link in custom family #1634

Open venpopov opened 3 months ago

venpopov commented 3 months ago

I tried to specify a link in a custom family as tan_half, but the stancode did not compile because the tan_half, inv_tan_half functions were not defined. I can define them myself and include in the stanvars, but it would be nice to be able to use the builtin functionality for that.

I noticed that this happens for some special link functions, but not for others. For example, adding a cauchit link works. logm1 doesn't.

I tracked this down to the fact that special link functions are loaded in one of two places: 1) stan_globaldefs: all the "if(any(links = ...." include statements there are link functions that you can use in a custom family, because brms recognizes the link and includes the code 2) as explicit include arguments to the .family* functions. for the von_mises, the fun_tan_half.stan is loaded here and cannot be used by custom functions

My suggestion is to make it consistent how "#include" statements are added for special links and load them just from one place.

since the file names match the link labels, instead of this:

  if (any(links == "cauchit")) {
    str_add(out$fun) <- "  #include 'fun_cauchit.stan'\n"
  } else if (any(links == "cloglog")) {
    str_add(out$fun) <- "  #include 'fun_cloglog.stan'\n"
  } else if (any(links == "softplus")) {
    str_add(out$fun) <- "  #include 'fun_softplus.stan'\n"
  } else if (any(links == "squareplus")) {
    str_add(out$fun) <- "  #include 'fun_squareplus.stan'\n"
  } else if (any(links == "softit")) {
    str_add(out$fun) <- "  #include 'fun_softit.stan'\n"
  } else if (any(links == "tan_half")) {
    str_add(out$fun) <- "  #include 'fun_tan_half.stan'\n"       # I added this just to test it
  }

you could have something along the lines of (a rough idea; alternatively have a function/variable that defines which link functions require external files and check against that)

for (link in links) {
  file <- paste0("fun_", link, ".stan")
  file <- file.path(system.file("chunks", package = "brms"), file)
  if (file.exists(file)) {
  str_add(out$fun) <- .....
}
venpopov commented 3 months ago

The intermediate solution for my usecase tuned out simple though - I added #include 'fun_tan_half.stan' to the stanvars function block I pass to brm with my custom family. I wasn't sure if it would work, but it did! So I didn't have to copy paste the functions separately :)