Skip to content

Commit d1d2aa2

Browse files
author
Junpeng Lao
authored
Improve AR model (#3070)
As discussed in https://discourse.pymc.io/t/vectorized-autoregressive-model/1449/ improve AR implementation so that tensor parameter is allowed.
1 parent 06d4415 commit d1d2aa2

File tree

2 files changed

+39
-15
lines changed

2 files changed

+39
-15
lines changed

pymc3/distributions/timeseries.py

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ class AR(distribution.Continuous):
7878
Parameters
7979
----------
8080
rho : tensor
81-
Vector of autoregressive coefficients.
81+
Tensor of autoregressive coefficients. The first dimension is the p lag.
8282
sd : float
8383
Standard deviation of innovation (sd > 0). (only required if tau is not specified)
8484
tau : float
@@ -100,29 +100,30 @@ def __init__(self, rho, sd=None, tau=None,
100100

101101
self.mean = tt.as_tensor_variable(0.)
102102

103-
rho = tt.as_tensor_variable(rho, ndim=1)
103+
if isinstance(rho, list):
104+
p = len(rho)
105+
else:
106+
try:
107+
p = rho.shape.tag.test_value[0]
108+
except AttributeError:
109+
p = rho.shape[0]
110+
104111
if constant:
105-
self.p = rho.shape[0] - 1
112+
self.p = p - 1
106113
else:
107-
self.p = rho.shape[0]
114+
self.p = p
108115

109116
self.constant = constant
110-
self.rho = rho
117+
self.rho = rho = tt.as_tensor_variable(rho)
111118
self.init = init
112119

113120
def logp(self, value):
114-
115-
y = value[self.p:]
116-
results, _ = scan(lambda l, obs, p: obs[p - l:-l],
117-
outputs_info=None, sequences=[tt.arange(1, self.p + 1)],
118-
non_sequences=[value, self.p])
119-
x = tt.stack(results)
120-
121121
if self.constant:
122-
y = y - self.rho[0]
123-
eps = y - self.rho[1:].dot(x)
122+
x = tt.add(*[self.rho[i + 1] * value[self.p - (i + 1):-(i + 1)] for i in range(self.p)])
123+
eps = value[self.p:] - self.rho[0] - x
124124
else:
125-
eps = y - self.rho.dot(x)
125+
x = tt.add(*[self.rho[i] * value[self.p - (i + 1):-(i + 1)] for i in range(self.p)])
126+
eps = value[self.p:] - x
126127

127128
innov_like = Normal.dist(mu=0.0, tau=self.tau).logp(eps)
128129
init_like = self.init.logp(value[:self.p])

pymc3/tests/test_distributions_timeseries.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,29 @@ def test_AR():
3737
np.testing.assert_allclose(ar_like, reg_like)
3838

3939

40+
def test_AR_nd():
41+
# AR2 multidimensional
42+
p, T, n = 3, 100, 5
43+
beta_tp = np.random.randn(p, n)
44+
y_tp = np.random.randn(T, n)
45+
with Model() as t0:
46+
beta = Normal('beta', 0., 1.,
47+
shape=(p, n),
48+
testval=beta_tp)
49+
AR('y', beta, sd=1.0,
50+
shape=(T, n), testval=y_tp)
51+
52+
with Model() as t1:
53+
beta = Normal('beta', 0., 1.,
54+
shape=(p, n),
55+
testval=beta_tp)
56+
for i in range(n):
57+
AR('y_%d' % i, beta[:, i], sd=1.0,
58+
shape=T, testval=y_tp[:, i])
59+
60+
np.testing.assert_allclose(t0.logp(t0.test_point),
61+
t1.logp(t1.test_point))
62+
4063

4164
def test_GARCH11():
4265
# test data ~ N(0, 1)

0 commit comments

Comments
 (0)