Skip to content

Commit a522680

Browse files
committed
Merge pull request #587 from pymc-devs/blocked_sampling
Implement non-blocked sampling for array based samplers.
2 parents 53aff99 + 215a77f commit a522680

19 files changed

+113
-42
lines changed

pymc/examples/ARM12_6.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,6 @@ def run(n=3000):
6363
step = HamiltonianMC(model.vars, h)
6464

6565
trace = sample(n, step, start)
66-
66+
6767
if __name__ == '__main__':
6868
run()

pymc/examples/ARM12_6uranium.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,4 @@ def run(n=3000):
6969

7070
trace = sample(n, step, start)
7171
if __name__ == '__main__':
72-
run()
72+
run()

pymc/examples/arbitrary_stochastic.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,8 @@ def run (n=3000):
1919

2020
start = model.test_point
2121
h = find_hessian(start)
22-
step = Metropolis(model.vars, h)
22+
step = Metropolis(model.vars, h, blocked=True)
2323
trace = sample(n, step, start)
2424

2525
if __name__ == "__main__":
2626
run()
27-

pymc/examples/banana.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,5 +63,3 @@ def run(n = 3000):
6363
pl.figure()
6464
extent = np.min(xs), np.max(xs), np.min(ys), np.max(ys)
6565
pl.imshow(post, extent=extent)
66-
67-

pymc/examples/disaster_model.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,5 +61,3 @@ def run(n=1000):
6161

6262
if __name__ == '__main__':
6363
run()
64-
65-

pymc/examples/factor_potential.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ def run(n = 3000):
1313
n = 50
1414
with model:
1515
trace = sample(n, step, start)
16+
1617
if __name__ == '__main__':
1718
run()
18-
19-

pymc/examples/glm_linear.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ def run(n=2000):
3131
import matplotlib.pyplot as plt
3232

3333
with model:
34-
trace = sample(n, Slice(model.vars))
34+
trace = sample(n, Slice())
3535

3636
plt.plot(x, y, 'x')
3737
glm.plot_posterior_predictive(trace)

pymc/examples/glm_robust.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def run(n=2000):
3737
import matplotlib.pyplot as plt
3838

3939
with model:
40-
trace = sample(n, Slice(model.vars))
40+
trace = sample(n, Slice())
4141

4242
plt.plot(x, y, 'x')
4343
glm.plot_posterior_predictive(trace)

pymc/examples/lasso_block_update.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
# <codecell>
4444

4545
with model:
46-
step1 = Metropolis([m1, m2])
46+
step1 = Metropolis([m1, m2], blocked=True)
4747

4848
step2 = Metropolis([s], proposal_dist=LaplaceProposal)
4949

@@ -67,5 +67,3 @@ def run(n=5000):
6767
# <codecell>
6868
if __name__ == '__main__':
6969
run()
70-
71-

pymc/examples/latent_occupancy.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,6 @@ def run(n=5000):
8585
step2 = BinaryMetropolis([z])
8686

8787
trace = sample(n, [step1, step2], start)
88-
88+
8989
if __name__ == '__main__':
9090
run()

pymc/examples/stochastic_volatility.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,9 @@ def hessian(point, nusd):
112112

113113
with model:
114114
step = NUTS(model.vars, hessian(start, 6))
115-
116-
117-
115+
116+
117+
118118
def run(n=2000):
119119
if n == "short":
120120
n = 50
@@ -136,7 +136,7 @@ def run(n=2000):
136136

137137
# figsize(12,6)
138138
traceplot(trace, model.vars[:-1])
139-
139+
140140
if __name__ == '__main__':
141141
run()
142142

pymc/step_methods/arraystep.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
from ..core import *
2+
from .compound import CompoundStep
3+
24
import numpy as np
35
from numpy.random import uniform
46
from numpy import log, isfinite
@@ -9,10 +11,44 @@
911

1012

1113
class ArrayStep(object):
12-
def __init__(self, vars, fs, allvars=False):
14+
def __new__(cls, *args, **kwargs):
15+
blocked = kwargs.get('blocked')
16+
if blocked is None:
17+
# Try to look up default value from class
18+
blocked = getattr(cls, 'default_blocked', True)
19+
kwargs['blocked'] = blocked
20+
21+
model = modelcontext(kwargs.get('model'))
22+
23+
# vars can either be first arg or a kwarg
24+
if 'vars' not in kwargs and len(args) >= 1:
25+
vars = args[0]
26+
args = args[1:]
27+
elif 'vars' in kwargs:
28+
vars = kwargs.pop('vars')
29+
else: # Assume all model variables
30+
vars = model.vars
31+
32+
if not blocked and len(vars) > 1:
33+
# In this case we create a separate sampler for each var
34+
# and append them to a CompoundStep
35+
steps = []
36+
for var in vars:
37+
step = super(ArrayStep, cls).__new__(cls)
38+
# If we don't return the instance we have to manually
39+
# call __init__
40+
step.__init__([var], *args, **kwargs)
41+
steps.append(step)
42+
43+
return CompoundStep(steps)
44+
else:
45+
return super(ArrayStep, cls).__new__(cls)
46+
47+
def __init__(self, vars, fs, allvars=False, blocked=True):
1348
self.ordering = ArrayOrdering(vars)
1449
self.fs = fs
1550
self.allvars = allvars
51+
self.blocked = blocked
1652

1753
def step(self, point):
1854
bij = DictToArrayBijection(self.ordering, point)

pymc/step_methods/hmc.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ def unif(step_size, elow=.85, ehigh=1.15):
2626

2727

2828
class HamiltonianMC(ArrayStep):
29-
def __init__(self, vars=None, scaling=None, step_scale=.25, path_length=2., is_cov=False, step_rand=unif, state=None, model=None):
29+
default_blocked = True
30+
31+
def __init__(self, vars=None, scaling=None, step_scale=.25, path_length=2., is_cov=False, step_rand=unif, state=None, model=None, **kwargs):
3032
"""
3133
Parameters
3234
----------
@@ -69,7 +71,7 @@ def __init__(self, vars=None, scaling=None, step_scale=.25, path_length=2., is_c
6971
state = SamplerHist()
7072
self.state = state
7173

72-
super(HamiltonianMC, self).__init__(vars, [model.fastlogp, model.fastdlogp(vars)])
74+
super(HamiltonianMC, self).__init__(vars, [model.fastlogp, model.fastdlogp(vars)], **kwargs)
7375

7476
def astep(self, q0, logp, dlogp):
7577
H = Hamiltonian(logp, dlogp, self.potential)

pymc/step_methods/metropolis.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,10 @@ class Metropolis(ArrayStep):
6565
Optional model for sampling step. Defaults to None (taken from context).
6666
6767
"""
68+
default_blocked = False
69+
6870
def __init__(self, vars=None, S=None, proposal_dist=NormalProposal, scaling=1.,
69-
tune=True, tune_interval=100, model=None):
71+
tune=True, tune_interval=100, model=None, **kwargs):
7072

7173
model = modelcontext(model)
7274

@@ -84,7 +86,7 @@ def __init__(self, vars=None, S=None, proposal_dist=NormalProposal, scaling=1.,
8486
# Determine type of variables
8587
self.discrete = np.array([v.dtype in discrete_types for v in vars])
8688

87-
super(Metropolis, self).__init__(vars, [model.fastlogp])
89+
super(Metropolis, self).__init__(vars, [model.fastlogp], **kwargs)
8890

8991
def astep(self, q0, logp):
9092

pymc/step_methods/nuts.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,14 @@ class NUTS(ArrayStep):
1717
Hoffman, Matthew D., & Gelman, Andrew. (2011).
1818
The No-U-Turn Sampler: Adaptively Setting Path Lengths in Hamiltonian Monte Carlo.
1919
"""
20+
default_blocked = True
2021
def __init__(self, vars=None, scaling=None, step_scale=0.25, is_cov=False, state=None,
2122
Emax=1000,
2223
target_accept=0.65,
2324
gamma=0.05,
2425
k=0.75,
2526
t0=10,
26-
model=None):
27+
model=None, **kwargs):
2728
"""
2829
Parameters
2930
----------
@@ -83,7 +84,7 @@ def __init__(self, vars=None, scaling=None, step_scale=0.25, is_cov=False, state
8384

8485

8586

86-
super(NUTS, self).__init__(vars, [model.fastlogp, model.fastdlogp(vars)])
87+
super(NUTS, self).__init__(vars, [model.fastlogp, model.fastdlogp(vars)], **kwargs)
8788

8889
def astep(self, q0, logp, dlogp):
8990
H = Hamiltonian(logp, dlogp, self.potential)

pymc/step_methods/slicer.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@
99

1010

1111
class Slice(ArrayStep):
12-
1312
"""Slice sampler"""
14-
def __init__(self, vars=None, w=1, tune=True, model=None):
13+
default_blocked = False
14+
def __init__(self, vars=None, w=1, tune=True, model=None, **kwargs):
1515

1616
model = modelcontext(model)
1717

@@ -23,7 +23,7 @@ def __init__(self, vars=None, w=1, tune=True, model=None):
2323
self.w_tune = []
2424
self.model = model
2525

26-
super(Slice, self).__init__(vars, [model.fastlogp])
26+
super(Slice, self).__init__(vars, [model.fastlogp], **kwargs)
2727

2828
def astep(self, q0, logp):
2929

pymc/tests/test_plots.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import matplotlib
22
matplotlib.use('Agg', warn=False)
33

4-
import numpy as np
4+
import numpy as np
55
from .checks import close_to
66

77
import pymc.plots
@@ -58,7 +58,7 @@ def test_multichain_plots():
5858

5959
autocorrplot(ptrace, vars=['switchpoint'])
6060

61-
def test_make_2d():
61+
def test_make_2d():
6262

6363
a = np.arange(4)
6464
close_to(pymc.plots.make_2d(a), a[:,None], 0)

pymc/tests/test_stats.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ def test_summary_0d_variable_model():
5656
tau = 1.3
5757
with Model() as model:
5858
x = Normal('x', mu, tau, testval=.1)
59-
step = Metropolis(model.vars, np.diag([1.]))
59+
step = Metropolis(model.vars, np.diag([1.]), blocked=True)
6060
trace = pm.sample(100, step=step)
6161
pm.summary(trace)
6262

@@ -66,7 +66,7 @@ def test_summary_1d_variable_model():
6666
tau = 1.3
6767
with Model() as model:
6868
x = Normal('x', mu, tau, shape=2, testval=[.1, .1])
69-
step = Metropolis(model.vars, np.diag([1.]))
69+
step = Metropolis(model.vars, np.diag([1.]), blocked=True)
7070
trace = pm.sample(100, step=step)
7171
pm.summary(trace)
7272

@@ -77,7 +77,7 @@ def test_summary_2d_variable_model():
7777
with Model() as model:
7878
x = Normal('x', mu, tau, shape=(2, 2),
7979
testval=np.tile(.1, (2, 2)))
80-
step = Metropolis(model.vars, np.diag([1.]))
80+
step = Metropolis(model.vars, np.diag([1.]), blocked=True)
8181
trace = pm.sample(100, step=step)
8282
pm.summary(trace)
8383

pymc/tests/test_step.py

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from .checks import *
2-
from .models import simple_model, mv_simple, mv_simple_discrete
2+
from .models import simple_model, mv_simple, mv_simple_discrete, simple_2model
33
from theano.tensor import constant
44
from scipy.stats.mstats import moment
55

@@ -13,15 +13,23 @@ def test_step_continuous():
1313
start, model, (mu, C) = mv_simple()
1414

1515
with model:
16-
hmc = pm.HamiltonianMC(scaling=C, is_cov=True)
17-
mh = pm.Metropolis(S=C,
18-
proposal_dist=pm.MultivariateNormalProposal)
16+
mh = pm.Metropolis()
1917
slicer = pm.Slice()
20-
nuts = pm.NUTS(scaling=C, is_cov=True)
21-
compound = pm.CompoundStep([hmc, mh])
18+
hmc = pm.HamiltonianMC(scaling=C, is_cov=True, blocked=False)
19+
nuts = pm.NUTS(scaling=C, is_cov=True, blocked=False)
20+
21+
mh_blocked = pm.Metropolis(S=C,
22+
proposal_dist=pm.MultivariateNormalProposal,
23+
blocked=True)
24+
slicer_blocked = pm.Slice(blocked=True)
25+
hmc_blocked = pm.HamiltonianMC(scaling=C, is_cov=True)
26+
nuts_blocked = pm.NUTS(scaling=C, is_cov=True)
2227

28+
compound = pm.CompoundStep([hmc_blocked, mh_blocked])
2329

24-
steps = [mh, hmc, slicer, nuts, compound]
30+
31+
steps = [slicer, hmc, nuts, mh_blocked, hmc_blocked,
32+
slicer_blocked, nuts_blocked, compound]
2533

2634
unc = np.diag(C) ** .5
2735
check = [('x', np.mean, mu, unc / 10.),
@@ -32,6 +40,36 @@ def test_step_continuous():
3240
for (var, stat, val, bound) in check:
3341
yield check_stat, repr(st), h, var, stat, val, bound
3442

43+
44+
def test_non_blocked():
45+
"""Test that samplers correctly create non-blocked compound steps.
46+
"""
47+
48+
start, model = simple_2model()
49+
50+
with model:
51+
# Metropolis and Slice are non-blocked by default
52+
mh = pm.Metropolis()
53+
assert isinstance(mh, pm.CompoundStep)
54+
slicer = pm.Slice()
55+
assert isinstance(slicer, pm.CompoundStep)
56+
hmc = pm.HamiltonianMC(blocked=False)
57+
assert isinstance(hmc, pm.CompoundStep)
58+
nuts = pm.NUTS(blocked=False)
59+
assert isinstance(nuts, pm.CompoundStep)
60+
61+
mh_blocked = pm.Metropolis(blocked=True)
62+
assert isinstance(mh_blocked, pm.Metropolis)
63+
slicer_blocked = pm.Slice(blocked=True)
64+
assert isinstance(slicer_blocked, pm.Slice)
65+
hmc_blocked = pm.HamiltonianMC()
66+
assert isinstance(hmc_blocked, pm.HamiltonianMC)
67+
nuts_blocked = pm.NUTS()
68+
assert isinstance(nuts_blocked, pm.NUTS)
69+
70+
compound = pm.CompoundStep([hmc_blocked, mh_blocked])
71+
72+
3573
def test_step_discrete():
3674
start, model, (mu, C) = mv_simple_discrete()
3775

0 commit comments

Comments
 (0)