Beispiel #1
0
def stable_model():
    zero = torch.zeros(2)
    a = pyro.sample("a", dist.Normal(0, 1))
    b = pyro.sample("b", dist.LogNormal(0, 1))
    c = pyro.sample("c", dist.Stable(1.5, 0.0, b, a))
    d = pyro.sample("d", dist.Stable(1.5, 0.0, b, 0.0), obs=a)
    e = pyro.sample("e", dist.Stable(1.5, 0.1, b, a))
    f = pyro.sample("f", dist.Stable(1.5, 0.1, b, 0.0), obs=a)
    g = pyro.sample("g", dist.Stable(1.5, zero, b, a).to_event(1))
    h = pyro.sample("h", dist.Stable(1.5, zero, b, 0).to_event(1), obs=a)
    i = pyro.sample(
        "i",
        dist.TransformedDistribution(dist.Stable(1.5, 0, b, a),
                                     dist.transforms.ExpTransform()),
    )
    j = pyro.sample(
        "j",
        dist.TransformedDistribution(dist.Stable(1.5, 0, b, a),
                                     dist.transforms.ExpTransform()),
        obs=a.exp(),
    )
    k = pyro.sample(
        "k",
        dist.TransformedDistribution(dist.Stable(
            1.5, zero, b, a), dist.transforms.ExpTransform()).to_event(1),
    )
    l = pyro.sample(
        "l",
        dist.TransformedDistribution(dist.Stable(
            1.5, zero, b, a), dist.transforms.ExpTransform()).to_event(1),
        obs=a.exp() + zero,
    )
    return a, b, c, d, e, f, g, h, i, j, k, l
Beispiel #2
0
    def model(self, zero_data, covariates):
        with pyro.plate("batch", len(zero_data), dim=-2):
            zero_data = pyro.subsample(zero_data, event_dim=1)
            covariates = pyro.subsample(covariates, event_dim=1)

            loc = zero_data[..., :1, :]
            scale = pyro.sample("scale", dist.LogNormal(loc, 1).to_event(1))

            with self.time_plate:
                jumps = pyro.sample("jumps", dist.Normal(0, scale).to_event(1))
            prediction = jumps.cumsum(-2)

            duration, obs_dim = zero_data.shape[-2:]
            noise_dist = dist.LinearHMM(
                dist.Stable(1.9, 0).expand([obs_dim]).to_event(1),
                torch.eye(obs_dim),
                dist.Stable(1.9, 0).expand([obs_dim]).to_event(1),
                torch.eye(obs_dim),
                dist.Stable(1.9, 0).expand([obs_dim]).to_event(1),
                duration=duration,
            )
            rep = StableReparam()
            with poutine.reparam(
                    config={"residual": LinearHMMReparam(rep, rep, rep)}):
                self.predict(noise_dist, prediction)
Beispiel #3
0
def test_additive(stability, skew0, skew1, scale0, scale1):
    num_samples = 10000
    d0 = dist.Stable(stability, skew0, scale0, coords="S")
    d1 = dist.Stable(stability, skew1, scale1, coords="S")
    expected = d0.sample([num_samples]) + d1.sample([num_samples])

    scale = (scale0**stability + scale1**stability)**(1 / stability)
    skew = ((skew0 * scale0**stability + skew1 * scale1**stability) /
            (scale0**stability + scale1**stability))
    d = dist.Stable(stability, skew, scale, coords="S")
    actual = d.sample([num_samples])

    assert ks_2samp(expected, actual).pvalue > 0.05
Beispiel #4
0
    def model():
        stability = pyro.sample("stability", dist.Uniform(1.0, 2.0))
        trans_skew = pyro.sample("trans_skew", dist.Uniform(-1.0, 1.0))
        obs_skew = pyro.sample("obs_skew", dist.Uniform(-1.0, 1.0))
        scale = pyro.sample("scale", dist.Gamma(3, 1))

        # We use separate plates because the .cumsum() op breaks independence.
        with pyro.plate("time1", len(data)):
            dz = pyro.sample("dz", dist.Stable(stability, trans_skew))
        z = dz.cumsum(-1)
        with pyro.plate("time2", len(data)):
            y = pyro.sample("y", dist.Stable(stability, obs_skew, scale, z))
            pyro.sample("x", dist.Poisson(y.abs()), obs=data)
Beispiel #5
0
def test_sample(alpha, beta):
    num_samples = 100
    d = dist.Stable(alpha, beta, coords="S")

    def sampler(size):
        # Temporarily increase radius to test hole-patching logic.
        # Scipy doesn't handle values of alpha very close to 1.
        try:
            old = pyro.distributions.stable.RADIUS
            pyro.distributions.stable.RADIUS = 0.02
            return d.sample([size])
        finally:
            pyro.distributions.stable.RADIUS = old

    def cdf(x):
        with warnings.catch_warnings(record=True) as w:
            warnings.simplefilter("always", category=IntegrationWarning)
            result = levy_stable.cdf(x, alpha, beta)
        # Scipy has only an experimental .cdf() function for alpha=1, beta!=0.
        # It sometimes passes and sometimes xfails.
        if w and alpha == 1 and beta != 0:
            pytest.xfail(reason="scipy.stats.levy_stable.cdf is unstable")
        return result

    assert kstest(sampler, cdf, N=num_samples).pvalue > 0.1
Beispiel #6
0
    def model(self, zero_data, covariates):
        duration,data_dim = zero_data.shape
        feature_dim = covariates.size(-1)
        drift_stability = pyro.sample("drift_stability", dist.Uniform(1, 2))
        drift_scale = pyro.sample("drift_scale", dist.LogNormal(-20, 5))
        with pyro.plate("item", data_dim, dim=-2):
            bias = pyro.sample("bias", dist.Normal(5.0, 10.0))
            with pyro.plate('features',feature_dim,dim=-1):
                weight = pyro.sample("weight", dist.Normal(0.0, 5))

            # We'll sample a time-global scale parameter outside the time plate,
            # then time-local iid noise inside the time plate.
            with self.time_plate:
                # We combine two different reparameterizers: the inner SymmetricStableReparam
                # is needed for the Stable site, and the outer LocScaleReparam is optional but
                # appears to improve inference.
                with poutine.reparam(config={"drift": LocScaleReparam()}):
                    with poutine.reparam(config={"drift": SymmetricStableReparam()}):
                        drift = pyro.sample("drift",
                                            dist.Stable(drift_stability, 0, drift_scale))

        motion = drift.cumsum(-1)  # A Brownian motion.

        # The prediction now includes three terms.
        regression = torch.matmul(covariates, weight[...,None])
        prediction = motion + bias + regression.sum(axis=-1)
        prediction = prediction.unsqueeze(-1).transpose(-1, -3)
        # Finally we can construct a noise distribution.
        # We will share parameters across all time series.
        obs_scale = pyro.sample("obs_scale", dist.LogNormal(-5, 5))
        noise_dist = dist.Normal(loc=0.0, scale=obs_scale.unsqueeze(-1))
        self.predict(noise_dist, prediction)
Beispiel #7
0
def test_sample(alpha, beta):
    num_samples = 100
    d = dist.Stable(alpha, beta)

    def sampler(size):
        # Temporarily increase radius to test hole-patching logic.
        # Scipy doesn't handle values of alpha very close to 1.
        try:
            old = pyro.distributions.stable.RADIUS
            pyro.distributions.stable.RADIUS = 0.02
            x = d.sample([size])
        finally:
            pyro.distributions.stable.RADIUS = old

        # Convert from Nolan's parametrization S^0 to scipy parametrization S.
        if alpha == 1:
            z = x
        else:
            z = x + beta * np.tan(np.pi / 2 * alpha)
        return z

    def cdf(x):
        with warnings.catch_warnings(record=True) as w:
            warnings.simplefilter("always", category=IntegrationWarning)
            result = levy_stable.cdf(x, alpha, beta)
        # Scipy has only an experimental .cdf() function for alpha=1, beta!=0.
        # It sometimes passes and sometimes xfails.
        if w and alpha == 1 and beta != 0:
            pytest.xfail(reason="scipy.stats.levy_stable.cdf is unstable")
        return result

    stat, pvalue = kstest(sampler, cdf, N=num_samples)
    assert pvalue > 0.1, pvalue
Beispiel #8
0
def test_mean(stability, skew, scale, coords):
    loc = torch.randn(10)
    d = dist.Stable(stability, skew, scale, loc, coords=coords)
    if stability <= 1:
        assert torch.isnan(d.mean).all()
    else:
        expected = d.sample((100000, )).mean(0)
        assert_close(d.mean, expected, atol=0.1)
Beispiel #9
0
def test_variance(stability, scale):
    skew = dist.Uniform(-1, 1).sample((10, ))
    loc = torch.randn(10)
    d = dist.Stable(stability, skew, scale, loc)
    if stability < 2:
        assert torch.isinf(d.variance).all()
    else:
        expected = d.sample((100000, )).var(0)
        assert_close(d.variance, expected, rtol=0.02)
Beispiel #10
0
def test_shape(sample_shape, batch_shape):
    stability = torch.empty(batch_shape).uniform_(0, 2).requires_grad_()
    skew = torch.empty(batch_shape).uniform_(-1, 1).requires_grad_()
    scale = torch.randn(batch_shape).exp().requires_grad_()
    loc = torch.randn(batch_shape).requires_grad_()

    d = dist.Stable(stability, skew, scale, loc)
    assert d.batch_shape == batch_shape

    x = d.rsample(sample_shape)
    assert x.shape == sample_shape + batch_shape

    x.sum().backward()
Beispiel #11
0
def test_sample_2(alpha, beta):
    num_samples = 10000

    d = dist.Stable(alpha, beta, coords="S")
    # Temporarily increase radius to test hole-patching logic.
    # Scipy doesn't handle values of alpha very close to 1.
    try:
        old = pyro.distributions.stable.RADIUS
        pyro.distributions.stable.RADIUS = 0.02
        actual = d.sample([num_samples])
    finally:
        pyro.distributions.stable.RADIUS = old
    actual = d.sample([num_samples])

    expected = levy_stable.rvs(alpha, beta, size=num_samples)

    assert ks_2samp(expected, actual).pvalue > 0.05
Beispiel #12
0
    def model(self, zero_data, covariates):
        duration, data_dim = zero_data.shape

        # Let's model each time series as a Levy stable process, and share process parameters
        # across time series. To do that in Pyro, we'll declare the shared random variables
        # outside of the "origin" plate:
        drift_stability = pyro.sample("drift_stability", dist.Uniform(1, 2))
        drift_scale = pyro.sample("drift_scale", dist.LogNormal(-20, 5))
        with pyro.plate("origin", data_dim, dim=-2):
            # Now inside of the origin plate we sample drift and seasonal components.
            # All the time series inside the "origin" plate are independent,
            # given the drift parameters above.
            with self.time_plate:
                # We combine two different reparameterizers: the inner SymmetricStableReparam
                # is needed for the Stable site, and the outer LocScaleReparam is optional but
                # appears to improve inference.
                with poutine.reparam(config={"drift": LocScaleReparam()}):
                    with poutine.reparam(config={"drift": SymmetricStableReparam()}):
                        drift = pyro.sample("drift",
                                            dist.Stable(drift_stability, 0, drift_scale))

            with pyro.plate("hour_of_week", 24 * 7, dim=-1):
                seasonal = pyro.sample("seasonal", dist.Normal(0, 5))

        # Now outside of the time plate we can perform time-dependent operations like
        # integrating over time. This allows us to create a motion with slow drift.
        seasonal = periodic_repeat(seasonal, duration, dim=-1)
        motion = drift.cumsum(dim=-1)  # A Levy stable motion to model shocks.
        prediction = motion + seasonal

        # Next we do some reshaping. Pyro's forecasting framework assumes all data is
        # multivariate of shape (duration, data_dim), but the above code uses an "origins"
        # plate that is left of the time_plate. Our prediction starts off with shape
        assert prediction.shape[-2:] == (data_dim, duration)
        # We need to swap those dimensions but keep the -2 dimension intact, in case Pyro
        # adds sample dimensions to the left of that.
        prediction = prediction.unsqueeze(-1).transpose(-1, -3)
        assert prediction.shape[-3:] == (1, duration, data_dim), prediction.shape

        # Finally we can construct a noise distribution.
        # We will share parameters across all time series.
        obs_scale = pyro.sample("obs_scale", dist.LogNormal(-5, 5))
        noise_dist = dist.Normal(0, obs_scale.unsqueeze(-1))
        self.predict(noise_dist, prediction)
Beispiel #13
0
    def model(self, zero_data, covariates):
        data_dim = zero_data.size(-1)
        feature_dim = covariates.size(-1)
        bias = pyro.sample("bias", dist.Normal(5.0, 10.0).expand((data_dim,)).to_event(1))
        weight = pyro.sample("weight", dist.Normal(0.0, 5).expand((feature_dim,)).to_event(1))

        # We'll sample a time-global scale parameter outside the time plate,
        # then time-local iid noise inside the time plate.
        drift_scale = pyro.sample("drift_scale",
                                  dist.LogNormal(0, 5.0).expand((1,)).to_event(1))
        with self.time_plate:
            # We'll use a reparameterizer to improve variational fit. The model would still be
            # correct if you removed this context manager, but the fit appears to be worse.
            with poutine.reparam(config={"drift": LocScaleReparam()}):
                drift = pyro.sample("drift", dist.Normal(zero_data.double(), drift_scale.double()).to_event(1))

        # After we sample the iid "drift" noise we can combine it in any time-dependent way.
        # It is important to keep everything inside the plate independent and apply dependent
        # transforms outside the plate.
        motion = drift.cumsum(-2)  # A Brownian motion.

        # The prediction now includes three terms.
        prediction = motion + bias + (weight * covariates).sum(-1, keepdim=True)
        assert prediction.shape[-2:] == zero_data.shape

        # The next part of the model creates a likelihood or noise distribution.
        # Again we'll be Bayesian and write this as a probabilistic program with
        # priors over parameters.
        stability = pyro.sample("noise_stability", dist.Uniform(1, 2).expand((1,)).to_event(1))
        skew = pyro.sample("noise_skew", dist.Uniform(-1, 1).expand((1,)).to_event(1))
        scale = pyro.sample("noise_scale", dist.LogNormal(-5, 5).expand((1,)).to_event(1))
        noise_dist = dist.Stable(stability, skew, scale)

        # We need to use a reparameterizer to handle the Stable distribution.
        # Note "residual" is the name of Pyro's internal sample site in self.predict().
        with poutine.reparam(config={"residual": StableReparam()}):
            self.predict(noise_dist, prediction)
Beispiel #14
0
 def model():
     stability = pyro.sample("stability", dist.Uniform(0., 2.))
     skew = pyro.sample("skew", dist.Uniform(-1., 1.))
     y = pyro.sample("z", dist.Stable(stability, skew))
     pyro.sample("x", dist.Poisson(y.abs()), obs=torch.tensor(1.))
Beispiel #15
0
def random_stable(shape, stability, skew=None):
    if skew is None:
        skew = dist.Uniform(-1, 1).sample(shape)
    scale = torch.rand(shape).exp()
    loc = torch.randn(shape)
    return dist.Stable(stability, skew, scale, loc)
Beispiel #16
0
 def model():
     with pyro.plate_stack("plates", shape):
         with pyro.plate("particles", 200000):
             return pyro.sample("x", dist.Stable(stability, 0, scale, loc))
Beispiel #17
0
 def model():
     return pyro.sample("x", dist.Stable(stability, skew))
Beispiel #18
0
 def model():
     with poutine.reparam(config={"x": Reparam()}):
         with pyro.plate("plate", 10):
             return pyro.sample("x", dist.Stable(1.5, 0))
Beispiel #19
0
def test_normal(loc, scale):
    num_samples = 100000
    expected = dist.Normal(loc, scale).sample([num_samples])
    actual = dist.Stable(2, 0, scale * 0.5**0.5, loc).sample([num_samples])
    assert_close(actual.mean(), expected.mean(), atol=0.01)
    assert_close(actual.std(), expected.std(), atol=0.01)
Beispiel #20
0
def random_stable(stability, skew_scale_loc_shape):
    skew = dist.Uniform(-1, 1).sample(skew_scale_loc_shape)
    scale = torch.rand(skew_scale_loc_shape).exp()
    loc = torch.randn(skew_scale_loc_shape)
    return dist.Stable(stability, skew, scale, loc)
Beispiel #21
0
 def model():
     with pyro.plate("particles", 20000):
         return pyro.sample("x", dist.Stable(stability, skew))