I will be needing to generate a number of models, all variations on a theme. I can’t hand code them all due to the large number of possible combinations. So I’m asking about if it’s possible to compose models in PyMC?
It seems like it is, based on this issue in the repo #1553, but I couldn’t make much sense of it - probably because I’m still a beginner with Python. If it’s possible to compose models, I’d really appreciate a quick demo. Here’s a handy concrete example 
import numpy as np
import pymc3 as pm
import math
A = np.array([28, 34, 19, 41, 27, 54, 15, 67, 20, 54, 22, 49, 24, 78, 11, 25, 54, 69, 47, 33, 40, 31, 34, 25, 80, 14, 55])
DA = np.array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
B = np.array([30, 35, 25, 75, 50, 55, 35, 75, 55, 60, 25, 60, 35, 80, 30, 60, 80, 85, 50, 80, 55, 85, 50, 30, 85, 25, 75])
DB = np.array([179, 186, 53, 20, 21, 117, 13, 119, 7, 111, 136, 89, 29, 162, 7, 14, 30, 91, 160, 14, 62, 7, 30, 80, 157, 19, 61])
R_obs = np.array([0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0])
def cumulative_normal(x):
return 0.5 + 0.5 * pm.math.erf(x/pm.math.sqrt(2))
def hyperbolic(logk, reward, delay):
return reward / (1.0+pm.math.exp(logk)*delay)
def hyperboloid(logk, S, reward, delay):
return reward / (1.0+pm.math.exp(logk)*delay)**S
def choice_psychometric(α, ϵ, VA, VB):
return ϵ + (1.0-2.0*ϵ) * cumulative_normal((VB-VA)/α)
with pm.Model() as model_hyperbolic:
# discount function stuff ~~~~~~~~~~~~~~~~~~~~~~~~~~
# prior
logk = pm.Normal('logk', mu=math.log(1/50), sd=5)
# Value functions
VA = pm.Deterministic('VA', hyperbolic(logk, A, DA))
VB = pm.Deterministic('VB', hyperbolic(logk, B, DB))
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# link function stuff
alpha = pm.Exponential('alpha', lam=1)
epsilon = pm.Beta('epsilon', alpha=1.1, beta=10.9)
P_chooseB = pm.Deterministic('P_chooseB', choice_psychometric(alpha, epsilon, VA, VB))
# Likelihood of observations
R = pm.Bernoulli('R', p=P_chooseB, observed=R_obs)
with pm.Model() as model_hyperboloid:
# discount function stuff ~~~~~~~~~~~~~~~~~~~~~~~~~~
# prior
logk = pm.Normal('logk', mu=math.log(1/50), sd=5)
S = pm.Normal('S', 1, sd=0.001)
# Value functions
VA = pm.Deterministic('VA', hyperboloid(logk, S, A, DA))
VB = pm.Deterministic('VB', hyperboloid(logk, S, B, DB))
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# link function stuff
alpha = pm.Exponential('alpha', lam=1)
epsilon = pm.Beta('epsilon', alpha=1.1, beta=10.9)
P_chooseB = pm.Deterministic('P_chooseB', choice_psychometric(alpha, epsilon, VA, VB))
# Likelihood of observations
R = pm.Bernoulli('R', p=P_chooseB, observed=R_obs)
So here I have just two models which use either hyperbolic or hyperboloid deterministic functions. This is just a minimal example, for the real task I have there will be many discount functions (each with their own priors), and there will also be plug and play link functions.
First I thought of just injecting the desired function, but then realised this won’t work as each is associated with different numbers (and names) of parameters.
PS. On a separate note, I really like being able to define functions externally from the model. That way I can reuse those functions if I want to do visualisation using prior or posterior samples 