Multi-level multi-variable model with arbitrary and updatable pdf

I am writing a simulated annealing app using pymc3 at each annealing step. Posteriors from each pymc3 run are used as priors for the next, and the “temperature” (assumed noise level) is reduced.

I am using a spline function to calculate my PDF from a uniform distribution over (0,1), and I update the splines after each run. My problem is that I am using several multi-level variables where the splines are distinct for each level, and I cannot figure out how to specify which spline to use for each variable level.

Below is a minimal example of what I am trying to do:

#Define Functions
import numpy as np
import pymc3 as pm
import theano as th
import theano.tensor as tt
import matplotlib.pyplot as plt

def cubic_spline(x, y):
  """
  Creates a spline between points. Interpolations are calculated with Δx from next smaller input point
  Parameters
  ----------
  x  : list of floats
  y  : list of floats

  Returns
  -------  
  list of list of floats
  """
  n = len(x) - 1
  h = [x[i+1]-x[i] for i in range(n)]
  al = [3*((y[i+1]-y[i])/h[i] - (y[i]-y[i-1])/h[i-1]) for i in range(1,n)]
  al.insert(0,0)
  
  l = [1] * (n+1)
  u = [0] * (n+1)
  z = [0] * (n+1)
  for i in range(1, n):
    l[i] = 2*(x[i+1]-x[i-1]) - h[i-1]*u[i-1]
    u[i] = h[i]/l[i]
    z[i] = (al[i] - h[i-1]*z[i-1])/l[i]

  b = [0] * (n+1)
  c = [0] * (n+1)
  d = [0] * (n+1)
  for i in range(n-1, -1, -1):    #for i in reversed(range(n)):
    c[i] = z[i] - u[i]*c[i+1]
    b[i] = (y[i+1]-y[i])/h[i] - h[i]*(c[i+1] + 2*c[i])/3
    d[i] = (c[i+1]-c[i])/(3*h[i])
  return [y, b, c, d, x]

def get_pdf(x,a):
    """
    Returns interpolated y value given x input, a is spline created with cubic_spline 
    """
    i=tt.cast(np.floor((x-a[4][0])/(a[4][1]-a[4][0])),'int64')
    y = th.shared(np.asarray(a[0]))
    b = th.shared(np.asarray(a[1]))
    c = th.shared(np.asarray(a[2]))
    d = th.shared(np.asarray(a[3]))
    xs = th.shared(np.asarray(a[4]))
    return y[i]+b[i]*(x-xs[i])+c[i]*(x-xs[i])**2+d[i]*(x-xs[i])**3 

def fx(x,q,a,p,ra):
    """
    Returns y values
    """
    return (a+x*p[q]+ra*np.random.random())

def get_pr0(y):
    """
    Returns initial guess PDF for pr[0] values from uniform inputs on (0,1)
    """
    return .01+3.*y

def get_pr1(y):
    """
    Returns initial guess PDF for pr[1] values from uniform inputs on (0,1)
    """
    return .01+10.*y

def get_am(y):
    """
    Returns initial guess PDF for am values from uniform inputs on (0,1)
    """
    return y

#Make Initial Splines
xi=np.linspace(0.,1.,100)
yi=get_am(xi)
sp0=cubic_spline(xi,yi)
sp1=cubic_spline(xi,get_pr0(xi))
sp2=cubic_spline(xi,get_pr1(xi))

spls=[sp0,sp1,sp2]

Tmpr=1
NSamples=1000
NJobs=1
Npts=10000

# Generate Dummy Data with constant intercept (am) and slope changing from ps[0] to ps[1] midway through dataset
xs=np.linspace(0,20,Npts)
ps=np.linspace(1.,2.,2,dtype=float)
qs=np.linspace(0,Npts-1,Npts, dtype=int)
for i in range(0,Npts):
    qs[i]=int(2.*i/Npts) # an array to index ps 

ys=np.zeros((Npts))
ys=fx(xs,qs,.3,ps,0.)

with pm.Model() as model:
    am = get_pdf(pm.Beta('am', alpha=1,beta=1),spls[0])
    pr = get_pdf(pm.Beta('pr', alpha=1,beta=1,shape=2),spls[1+qs])    
    eps = Tmpr
    mu=fx(xs,qs,am,pr,0.)
    y_pred = pm.Normal('y_pred', mu=mu, sd=eps, observed=ys)
    step = pm.NUTS()
    trace = pm.sample(NSamples,njobs=NJobs,tune=1000)

pm.traceplot(trace)
pm.autocorrplot(trace);
plt.show()

#Update splines for next iteration, using trace elements (code not shown)

Here I have attempted to index the spline using the indexing array qs, but, of course, I get an error trying to index a list with a numpy array, which doesn’t work, but the problem is that I don’t know what attribute to use to indicate which spline to assign to each parameter level.

This question is similar to Multi-level multi-variables model, in which junpenglao suggested using matrix multiplication shown here, but I’m not sure how I’d implement or update that iteratively for a cubic spline-defined PDF

1 Like

Hmmm, we have a doc which doing a similar thing (smoothing the posterior and use as prior): http://docs.pymc.io/notebooks/updating_priors.html Have you check it out?

Somehow it seems this notebook is not indexed on the website…