Map index of unconstrained parameter to constrained parameter

Hey all!
I am looking for a way to automatically discern which components of the unconstrained parameter vector map to which constrained parameters. The following function I wrote does the trick (it first “constrains” the zero vector of appropriate length. Then it sequentially changes one of the components of the zero vector to one, constrains the whole vector, and checks which of the output parameters have changed):

map_upar2par = function(stanfit) {
  k = rstan::get_num_upars(stanfit)
  v0 = numeric(k)
  l0 = rstan::constrain_pars(stanfit, v0)
  pars = names(l0)
  parmap = character(k)
  
  for (i in 1:k) {
    v1 = v0; v1[i] = 1
    l1 = rstan::constrain_pars(stanfit,v1)
    for (p in pars) {
      if (!dplyr::setequal(l0[[p]], l1[[p]])) {
        parmap[i] = p
        break
      }
    }
  }
  parmap
}

However, this feels like an awkward way to get to information that is probably also stored somewhere in the STAN object. But I couldn’t find any function that exposes this map, nor was I able to find it in any of the slots of the stan_model or stanfit objects.

Any help would be appreciated!

Best, Lukas

This information is not explicitly stored anywhere by Stan. If such a mapping is one-to-one, it will just be the identity, where the order in the unconstrained space is also the ordering in the constrained space. But if you have any constraints that change size (simplex, etc) it will be more complicated

Why are you interested in such a mapping?

But if you have any constraints that change size (simplex, etc) it will be more complicated

Exactly! That’s why I was wondering if there is an in-built way to check that mapping. Thanks for the info!

Why are you interested in such a mapping?

The need for it popped up in various situations, all of which had to do with marginal likelihood estimation / bridgesampling. For one, I was trying to figure out why for certain models, bridgesampling did not converge, and wanted to check if any of the unconstrained parameters exhibited extreme non-normality. That is a bit cumbersome without such a mapping.

Second, I am working on an idea for marginal likelihood estimation where different parameters get different proposal distributions. For this, I need this mapping to figure out which parts of the unconstrained parameter vector correspond to which constrained parameter.

I thought of another way to do this, but I’m not sure if RStan exposes the functions needed. Essentially, the Stan Model C++ object has two functions:

and

You could use these two to map between them. If you care about transformed parameters, there’s really no way to do it besides what you’re doing in the original post that I can think of.

These functions are exposed in BridgeStan if RStan doesn’t have them

Hi, @lukelikelihood and welcome to the Stan forums.

Stan guarantees that the unconstrained parameter vector is serialized to constrained parameters in the order of parameter declarations in the code. Each transform has a function that tells you how many unconstrained parameters for a given transform. The transforms are not one-to-one. For example, in the simplex, if you declare simplex[N], there are N - 1 unconstrained parameters and changing any of them changes all of the values in the resulting simplex. It’s more complex for a covariance matrix, where the unconstrained representation has {N \choose 2} + N parameters arranged as a lower-triangular matrix L with \exp() applied to the diagonal so it’s positive, so that the covariance matrix is then L \cdot L^\top, so that element (m, n) of the output depends rows m and n of L.

Given your situations, it would probably be easier to do what you want with a clean autodiff-only system like JAX, but that’s only in Python.

Thanks @Bob_Carpenter, happy to be here!

Okay, I think that my workaround will probably work fine enough for the things I want to do! Thanks for all the info!

Given your situations, it would probably be easier to do what you want with a clean autodiff-only system like JAX, but that’s only in Python.

Yes, I have thought about working with JAX as backend, but for now, STAN is the environment I’m most comfortable with!