Let me integrate you out so that no one would notice you

An old fashioned mathematical technique might be to integrate out parameters from equations, and as more the equation is complicated as more it is difficult to get the analytical solution.

Basic idea

Consider a variable exchange $x \mapsto z$ that satisfies \begin{align} \int_{-\infty}^{\infty} f(x) dx \Longrightarrow \int_{-1}^{1} f\Bigl(x(z)\Bigr) x'(z) dz. \nonumber \end{align} An easy solution for that is \begin{align} z = 2 \left(\frac{1}{1+\exp(-x)} \right)-1 \Longleftrightarrow x = \log{\left ( \frac{1+z}{1-z} \right )} \nonumber \end{align} and its first order derivative is \begin{align} x'(z) = \frac{2}{1 - z^{2}} \nonumber \end{align} It is well known that \begin{align} \int_{-\infty}^{\infty} \frac{1}{\sqrt{2 \pi}} \exp\left(-\frac{x^2}{2} \right) dx = 1, \nonumber \end{align} and apply the variable exchange and the number we get by approximating 7-point Gauss rule with a 15-point Kronrod rule is: \begin{align} \int_{-1}^{1} \frac{1}{\sqrt{2 \pi}} \exp\left(-\frac{\left(\log{\left ( \frac{1+z}{1-z} \right )}\right)^2}{2} \right) \left|\frac{2}{1 - z^{2}}\right| dz = 0.99999692068548 \approx 1 \nonumber \end{align} Note that $1-0.99999692068548=3.07931451959398 \times 10^{-6}$.

Test model

\begin{align} y = a_i x + \epsilon_i \\ a_i \sim {\cal N}(a,\omega) \\ \epsilon_i \sim {\cal N}(0,\sigma) \nonumber \end{align} Likelihood is \begin{align} \prod_{i=1}^n{\cal N}_d(y \mid a_i x, \sigma){\cal N}_d(a_i \mid a x,\omega) \nonumber \end{align}

Analytical solution

Integrate out $a_i$ from the model \begin{align} p_i &= \int_{-\infty}^{\infty}{\cal N}_d(y \mid a_i x, \sigma){\cal N}_d(a_i \mid a x,\omega) d a_i \\ &= \int_{-\infty}^{\infty} \left[ \frac{1}{\sqrt{2 \pi} \sigma}\exp \left(-\frac{y - a_i x}{2 \sigma^2} \right) \frac{1}{\sqrt{2 \pi} \omega}\exp \left(-\frac{a_i - a}{2 \omega^2} \right) \right]d a_i \\ &=\frac{1}{\sqrt{2 \pi (\omega^2 x^2 + \sigma^2)}} \exp \left( -\frac{(y-ax)^2}{2(\omega^2 x^2 + \sigma^2)} \right) \nonumber \end{align}
> summary(fit1)$summary
              mean      se_mean         sd         2.5%          25%          50%
a        0.9888168 0.0006589843 0.03325222    0.9221687    0.9669033    0.9888208
omega    0.4188159 0.0021747104 0.07486459    0.2419583    0.3795231    0.4274141
sigma    0.5302711 0.0005498567 0.01996546    0.4930311    0.5166785    0.5294182
lp__  -530.7041447 0.0428310403 1.40084589 -534.4240874 -531.3030933 -530.2699704
               75%        97.5%    n_eff     Rhat
a        1.0112437    1.0524001 2546.192 1.002017
omega    0.4695590    0.5430313 1185.086 1.000445
sigma    0.5427861    0.5725930 1318.438 1.002972
lp__  -529.6919616 -529.2135198 1069.704 1.002579

Numerical solution

Compare the results with one using the following numerical integral. \begin{align} \int_{-\infty}^{\infty} f(x) dx \Longrightarrow \int_{-1}^{1} f\left(\log{\left ( \frac{1+z}{1-z} \right )}\right) \left|\frac{2}{1 - z^{2}}\right| dz. \nonumber \end{align}
> summary(fit2)$summary
              mean      se_mean         sd         2.5%          25%          50%
a        0.9895701 0.0007101089 0.03497257    0.9192264    0.9664499    0.9899563
omega    0.4267222 0.0013115691 0.06287198    0.3079255    0.3800736    0.4278155
sigma    0.5293555 0.0004180750 0.01877470    0.4938143    0.5165164    0.5290596
lp__  -877.2053140 0.0278415504 1.21027072 -880.2556886 -877.7230284 -876.8940771
               75%        97.5%    n_eff     Rhat
a        1.0127247    1.0576070 2425.521 1.000030
omega    0.4714495    0.5469434 2297.904 1.000609
sigma    0.5421843    0.5668702 2016.683 1.000074
lp__  -876.3328600 -875.8352797 1889.636 1.000688

Script

Data generation

set.seed(1234)

n = 1000
a = 1
omega = 0.5
sigma = 0.5

x    = runif(n,-1,1)
a_i  = rnorm(n,a,omega)
e_i  = rnorm(n,0,sigma)
y = x*a_i + e_i 

## 7-point Gauss rule with a 15-point Kronrod rule
 
X = c(
  -0.991455371120813, 
  -0.949107912342759,
  -0.864864423359769,
  -0.741531185599394,
  -0.586087235467691,
  -0.405845151377397,
  -0.207784955007898,
  0,
  0.207784955007898,
  0.405845151377397,
  0.586087235467691,
  0.741531185599394,
  0.864864423359769,
  0.949107912342759,
  0.991455371120813  
)
 
C = c(
  0.022935322010529,
  0.063092092629979,
  0.104790010322250,
  0.140653259715525,
  0.169004726639267,
  0.190350578064785,
  0.204432940075298,
  0.209482141084728,
  0.204432940075298,
  0.190350578064785,
  0.169004726639267,
  0.140653259715525,
  0.104790010322250,
  0.063092092629979,
  0.022935322010529
)

d = list(
    n = n,
    x = x,
    y = y,
    X = X,
    C = C
)

plot(x,y)

Model for analytical solution

model1 = '
    functions{
        real ll(real a, real sigma, real omega, real x, real y){
            real res;
            res = -0.5*log(pi())-0.5*(log(omega^2*x^2+sigma^2)) - 0.5*(y - a*x)^2/(omega^2*x^2+sigma^2);
            return res;
        }
    }

    data{
        int  n;
        real x[n];
        real y[n];
    }

    parameters{
        real a;
        real omega;
        real sigma;
    }

    model{
        for (i in 1:n)
            target += ll(a,sigma,omega,x[i],y[i]);
    }
'

StanModel1 <- stan_model(model_code = model1)

fit1 <- sampling(StanModel1
                ,data = d
)

Model for numerical integral

model2 = '
    functions{
        real pdf(real exz, real z, real a, real sigma, real omega, real x, real y){
            real res;
            res = 1/(sqrt(2*pi())*sigma)*exp(-(y-exz*x)^2/(2*sigma^2))*1/(sqrt(2*pi())*omega)*exp(-(exz-a)^2/(2*omega^2))*(2/(1-z^2));
            return res;
        }

        real int_pdf(vector X, vector C, real a, real sigma, real omega, real x, real y){
            real res;
            vector[15] Z;

            for (i in 1:15)
                Z[i] = log((1+X[i])/(1-X[i]));

            res = 0;
            for (i in 1:15)
                res += C[i]*pdf(Z[i],X[i],a,sigma,omega,x,y);
            return log(res);
        }

    }

    data{
        int  n;
        real x[n];
        real y[n];
        vector[15] X;
        vector[15] C;
    }

    parameters{
        real a;
        real omega;
        real sigma;
    }

    model{
        for (i in 1:n)
            target += int_pdf(X,C,a,sigma,omega,x[i],y[i]);
    }
'

StanModel2 <- stan_model(model_code = model2)

fit2 <- sampling(StanModel2
                ,data = d
)

Comments