sample_shape is a little ambiguous; Pyro needs to know what part of that shape is independent (part of
batch_shape), and what part is dependent (part of
event_shape). This information is used internally in many ways.
If you're ok with the samples being treated as dependent, you can
x = pyro.sample("x", dist.Normal(0., 1.).expand([n]).independent(1))
If the samples are really independent, then yes Pyro really does require a
pyro.iarange context (renamed to
pyro.plate in dev and the upcoming 0.3 release).
with pyro.plate("data", n):
x = pyro.sample("x", dist.Normal(0. 1.))
FWIW I find the
pyro.plate contexts are more maintainable as models grow, since you can paste code into larger plates to batch in multiple ways. Early on Pyro did lots of configuration via kwargs to sample statements (scaling, masking, batch_size, ...), but these thwarted modularity; instead we can now write
with pyro.scale(p), pyro.mask(m), pyro.plate("data", n):
x = custom_x_sampler()
custom_x_sampler() might be a function with multiple sample statements. By moving these effects from kwargs to context managers, we gain modularity.