Exemple #1
0
    def test_Inference(self):
        # ===== Distributions ===== #
        dist = Normal(0., 1.)
        mvn = Independent(Normal(torch.zeros(2), torch.ones(2)), 1)

        # ===== Define model ===== #
        linear = AffineProcess((f, g), (1., 0.25), dist, dist)
        model = LinearGaussianObservations(linear, scale=0.1)

        mv_linear = AffineProcess((fmvn, gmvn), (0.5, 0.25), mvn, mvn)
        mvnmodel = LinearGaussianObservations(mv_linear, torch.eye(2), scale=0.1)

        # ===== Test for multiple models ===== #
        priors = Exponential(1.), LogNormal(0., 1.)

        hidden1d = AffineProcess((f, g), priors, dist, dist)
        oned = LinearGaussianObservations(hidden1d, 1., scale=0.1)

        hidden2d = AffineProcess((fmvn, gmvn), priors, mvn, mvn)
        twod = LinearGaussianObservations(hidden2d, torch.eye(2), scale=0.1 * torch.ones(2))

        particles = 1000
        # ====== Run inference ===== #
        for trumod, model in [(model, oned), (mvnmodel, twod)]:
            x, y = trumod.sample_path(1000)

            algs = [
                (NESS, {'particles': particles, 'filter_': APF(model.copy(), 200)}),
                (NESS, {'particles': particles, 'filter_': UKF(model.copy())}),
                (SMC2, {'particles': particles, 'filter_': APF(model.copy(), 200)}),
                (SMC2FW, {'particles': particles, 'filter_': APF(model.copy(), 200)}),
                (NESSMC2, {'particles': particles, 'filter_': APF(model.copy(), 200)})
            ]

            for alg, props in algs:
                alg = alg(**props).initialize()

                alg = alg.fit(y)

                w = normalize(alg._w_rec if hasattr(alg, '_w_rec') else torch.ones(particles))

                tru_params = trumod.hidden.theta._cont + trumod.observable.theta._cont
                inf_params = alg.filter.ssm.hidden.theta._cont + alg.filter.ssm.observable.theta._cont

                for trup, p in zip(tru_params, inf_params):
                    if not p.trainable:
                        continue

                    kde = p.get_kde(weights=w)

                    transed = p.bijection.inv(trup)
                    densval = kde.logpdf(transed.numpy().reshape(-1, 1))
                    priorval = p.distr.log_prob(trup)

                    assert (densval > priorval.numpy()).all()
Exemple #2
0
    def test_BatchedParameter(self):
        norm = DistributionWrapper(Normal, loc=0.0, scale=1.0)
        shape = 1000, 100

        a = torch.ones((shape[0], 1))

        init = DistributionWrapper(Normal, loc=a, scale=1.0)
        linear = AffineProcess((f, g), (a, 1.0), init, norm)

        # ===== Initialize ===== #
        x = linear.i_sample(shape)

        self.assert_timeseries_sampling(100, linear, x, shape)
Exemple #3
0
    def test_UnscentedTransform2D(self):
        # ===== 2D model ===== #
        mat = torch.eye(2)
        scale = torch.diag(mat)

        norm = DistributionWrapper(Normal, loc=0.0, scale=1.0)
        mvn = DistributionWrapper(MultivariateNormal,
                                  loc=torch.zeros(2),
                                  covariance_matrix=torch.eye(2))

        mvnlinear = AffineProcess((fmvn, g), (mat, scale), mvn, mvn)
        mvnoblinear = AffineObservations((fomvn, gomvn), (1.0, ), norm)

        mvnmodel = StateSpaceModel(mvnlinear, mvnoblinear)

        # ===== Perform unscented transform ===== #
        uft = UnscentedFilterTransform(mvnmodel)
        res = uft.initialize(3000)
        p = uft.predict(res)
        c = uft.correct(torch.tensor(0.0), p, res)

        assert isinstance(
            c.x_dist(),
            MultivariateNormal) and c.x_dist().mean.shape == torch.Size(
                [3000, 2])
Exemple #4
0
def make_model(prob, dim=1):
    if prob:
        parameters = Prior(Exponential, rate=1.0), Prior(LogNormal,
                                                         loc=0.0,
                                                         scale=1.0)
    else:
        parameters = (0.99, 0.25) if dim == 1 else (0.5, 0.25)

    obs_param = dict()
    if dim == 1:
        dist = DistributionWrapper(Normal, loc=0.0, scale=1.0)

        obs_param["a"] = 1.0
        obs_param["scale"] = 0.1

        func = (f, g)
    else:
        dist = DistributionWrapper(lambda **u: Independent(Normal(**u), 1),
                                   loc=torch.zeros(2),
                                   scale=torch.ones(2))

        obs_param["a"] = torch.eye(2)
        obs_param["scale"] = 0.1 * torch.ones(2)

        func = (fmvn, gmvn)

    hidden = AffineProcess(func, parameters, dist, dist)

    return LinearGaussianObservations(hidden, **obs_param)
Exemple #5
0
    def test_LoadModule(self):
        def f(x_, alpha, sigma):
            return alpha * x_

        def g(x_, alpha, sigma):
            return sigma

        norm = DistributionWrapper(Normal, loc=0.0, scale=1.0)
        linear = AffineProcess((f, g), (1.0, 1.0), norm, norm)
        model = LinearGaussianObservations(linear, 1.0, 1.0)

        x, y = model.sample_path(100)

        filt = SISR(model, 200)
        res = filt.longfilter(y)

        filt2 = SISR(model, 300)

        filt2.load_state_dict(filt.state_dict())
        self.assertTrue(filt2.particles[0] == 200)

        res2 = FilterResult(filt2.initialize())
        res2.load_state_dict(res.state_dict())

        self.assertTrue((res2.filter_means == res.filter_means).all())
Exemple #6
0
    def test_MultiDimensional(self):
        mu = torch.zeros(2)
        scale = torch.ones_like(mu)

        shape = 1000, 100

        mvn = Independent(Normal(mu, scale), 1)
        mvn = AffineProcess((f, g), (1., 1.), mvn, mvn)

        # ===== Initialize ===== #
        x = mvn.i_sample(shape)

        # ===== Propagate ===== #
        num = 100
        samps = [x]
        for t in range(num):
            samps.append(mvn.propagate(samps[-1]))

        samps = torch.stack(samps)
        self.assertEqual(samps.size(), torch.Size([num + 1, *shape,
                                                   *mu.shape]))

        # ===== Sample path ===== #
        path = mvn.sample_path(num + 1, shape)
        self.assertEqual(samps.shape, path.shape)
Exemple #7
0
    def test_LinearNoBatch(self):
        norm = Normal(0., 1.)
        linear = AffineProcess((f, g), (1., 1.), norm, norm)

        # ===== Initialize ===== #
        x = linear.i_sample()

        # ===== Propagate ===== #
        num = 100
        samps = [x]
        for t in range(num):
            samps.append(linear.propagate(samps[-1]))

        samps = torch.stack(samps)
        self.assertEqual(samps.size(), torch.Size([num + 1]))

        # ===== Sample path ===== #
        path = linear.sample_path(num + 1)
        self.assertEqual(samps.shape, path.shape)
Exemple #8
0
    def test_UnscentedTransform1D(self):
        # ===== 1D model ===== #
        norm = Normal(0., 1.)
        linear = AffineProcess((f, g), (1., 1.), norm, norm)
        linearobs = AffineObservations((fo, go), (1., 1.), norm)
        model = StateSpaceModel(linear, linearobs)

        # ===== Perform unscented transform ===== #
        x = model.hidden.i_sample(shape=3000)

        ut = UnscentedTransform(model).initialize(x).construct(0.)

        assert isinstance(ut.x_dist, Normal)
Exemple #9
0
    def test_MultiDimensional(self):
        mu = torch.zeros(2)
        scale = torch.ones_like(mu)

        shape = 1000, 100

        mvn = DistributionWrapper(lambda **u: Independent(Normal(**u), 1), loc=mu, scale=scale)
        mvn = AffineProcess((f, g), (1.0, 1.0), mvn, mvn)

        # ===== Initialize ===== #
        x = mvn.i_sample(shape)

        # ===== Propagate ===== #
        self.assert_timeseries_sampling(100, mvn, x, shape, (*shape, 2))
Exemple #10
0
    def test_BatchedParameter(self):
        norm = Normal(0., 1.)
        shape = 1000, 100

        a = torch.ones((shape[0], 1))

        init = Normal(a, 1.)
        linear = AffineProcess((f, g), (a, 1.), init, norm)

        # ===== Initialize ===== #
        x = linear.i_sample(shape)

        # ===== Propagate ===== #
        num = 100
        samps = [x]
        for t in range(num):
            samps.append(linear.propagate(samps[-1]))

        samps = torch.stack(samps)
        self.assertEqual(samps.size(), torch.Size([num + 1, *shape]))

        # ===== Sample path ===== #
        path = linear.sample_path(num + 1, shape)
        self.assertEqual(samps.shape, path.shape)
Exemple #11
0
    def test_UnscentedTransform1D(self):
        # ===== 1D model ===== #
        norm = DistributionWrapper(Normal, loc=0.0, scale=1.0)
        linear = AffineProcess((f, g), (1.0, 1.0), norm, norm)
        linearobs = AffineObservations((fo, go), (1.0, 1.0), norm)
        model = StateSpaceModel(linear, linearobs)

        # ===== Perform unscented transform ===== #
        uft = UnscentedFilterTransform(model)
        res = uft.initialize(3000)
        p = uft.predict(res)
        c = uft.correct(torch.tensor(0.0), p, res)

        assert isinstance(
            c.x_dist(), Normal) and c.x_dist().mean.shape == torch.Size([3000])
Exemple #12
0
    def test_ParallellFiltersAndStability(self):
        x, y = self.model.sample_path(50)

        shape = 3000

        linear = AffineProcess((f, g), (1., 1.), self.norm, self.norm)
        self.model.hidden = linear

        filt = SISR(self.model, 1000).set_nparallel(shape).initialize().longfilter(y)

        filtermeans = filt.filtermeans

        x = filtermeans[:, :1]
        mape = ((x - filtermeans[:, 1:]) / x).abs()

        assert mape.median(0)[0].max() < 0.05
Exemple #13
0
    def test_ParallelUnscented(self):
        x, y = self.model.sample_path(50)

        shape = 30

        linear = AffineProcess((f, g), (1.0, 1.0), self.norm, self.norm)
        self.model.hidden = linear

        filt = SISR(self.model, 1000, proposal=prop.Unscented()).set_nparallel(shape)
        result = filt.longfilter(y)

        filtermeans = result.filter_means

        x = filtermeans[:, :1]
        mape = ((x - filtermeans[:, 1:]) / x).abs()

        assert mape.median(0)[0].max() < 0.05
Exemple #14
0
    def test_Sample(self):
        # ==== Hidden ==== #
        norm = DistributionWrapper(Normal, loc=0.0, scale=1.0)
        linear = AffineProcess((f, g), (1.0, 1.0), norm, norm)

        # ==== Observable ===== #
        obs = AffineObservations((fo, go), (1.0, 0.0), norm)

        # ===== Model ===== #
        mod = StateSpaceModel(linear, obs)

        # ===== Sample ===== #
        x, y = mod.sample_path(100)

        diff = ((x - y)**2).mean().sqrt()

        assert x.shape == y.shape and x.shape[0] == 100 and diff < 1e-3
Exemple #15
0
    def test_UnscentedTransform2D(self):
        # ===== 2D model ===== #
        mat = torch.eye(2)
        scale = torch.diag(mat)

        norm = Normal(0., 1.)
        mvn = MultivariateNormal(torch.zeros(2), torch.eye(2))
        mvnlinear = AffineProcess((fmvn, g), (mat, scale), mvn, mvn)
        mvnoblinear = AffineObservations((fomvn, gomvn), (1., ), norm)

        mvnmodel = StateSpaceModel(mvnlinear, mvnoblinear)

        # ===== Perform unscented transform ===== #
        x = mvnmodel.hidden.i_sample(shape=3000)

        ut = UnscentedTransform(mvnmodel).initialize(x).construct(0.)

        assert isinstance(ut.x_dist, MultivariateNormal) and isinstance(
            ut.y_dist, Normal)
        assert isinstance(ut.x_dist_indep, Independent)
Exemple #16
0
    def test_UnscentedTransform2D(self):
        # ===== 2D model ===== #
        mat = torch.eye(2)
        scale = torch.diag(mat)

        norm = Normal(0., 1.)
        mvn = MultivariateNormal(torch.zeros(2), torch.eye(2))
        mvnlinear = AffineProcess((fmvn, g), (mat, scale), mvn, mvn)
        mvnoblinear = AffineObservations((fomvn, gomvn), (1., ), norm)

        mvnmodel = StateSpaceModel(mvnlinear, mvnoblinear)

        # ===== Perform unscented transform ===== #
        uft = UnscentedFilterTransform(mvnmodel)
        res = uft.initialize(3000)
        p = uft.predict(res)
        c = uft.correct(0., p)

        assert isinstance(
            c.x_dist(),
            MultivariateNormal) and c.x_dist().mean.shape == torch.Size(
                [3000, 2])
Exemple #17
0
    def test_StateDict(self):
        # ===== Define model ===== #
        norm = Normal(0., 1.)
        linear = AffineProcess((f, g), (1., 1.), norm, norm)
        linearobs = AffineObservations((fo, go), (1., 1.), norm)
        model = StateSpaceModel(linear, linearobs)

        # ===== Define filter ===== #
        filt = SISR(model, 100).initialize()

        # ===== Get statedict ===== #
        sd = filt.state_dict()

        # ===== Verify that we don't save multiple instances ===== #
        assert '_model' in sd and '_model' not in sd['_proposal']

        newfilt = SISR(model, 1000).load_state_dict(sd)
        assert newfilt._w_old is not None and newfilt.ssm is newfilt._proposal._model

        # ===== Test same with UKF and verify that we save UT ===== #
        ukf = UKF(model).initialize()
        sd = ukf.state_dict()

        assert '_model' in sd and '_model' not in sd['_ut']
Exemple #18
0
class Tests(unittest.TestCase):
    # ===== Simple 1D model ===== #
    norm = Normal(0., 1.)
    linear = AffineProcess((f, g), (1., 1.), norm, norm)
    model = LinearGaussianObservations(linear, 1., 1.)

    # ===== Simple 2D model ===== #
    mvn = Independent(Normal(torch.zeros(2), torch.ones(2)), 1)
    mvn = AffineProcess((fmvn, gmvn), (0.5, 1.), mvn, mvn)
    a = torch.Tensor([1., 2.])

    mvnmodel = LinearGaussianObservations(mvn, a, 1.)

    def test_InitializeFilter(self):
        filt = SISR(self.model, 1000).initialize()

        assert filt._x_cur.shape == torch.Size([1000])

    def test_Filters(self):
        for model in [self.model, self.mvnmodel]:
            x, y = model.sample_path(500)

            for filter_, props in [
                (SISR, {'particles': 500}),
                (APF, {'particles': 500}),
                (UKF, {}),
                (SISR, {'particles': 500, 'proposal': Linearized(alpha=None)}),
                (APF, {'particles': 500, 'proposal': Linearized()}),
                (SISR, {'particles': 500, 'proposal': Unscented()})
            ]:
                filt = filter_(model, **props).initialize()

                filt = filt.longfilter(y)

                assert len(filt.s_mx) > 0

                filtmeans = filt.filtermeans.numpy()

                # ===== Run Kalman ===== #
                if model is self.model:
                    kf = pykalman.KalmanFilter(transition_matrices=1., observation_matrices=1.)
                else:
                    kf = pykalman.KalmanFilter(transition_matrices=[[0.5, 1 / 3], [0, 1.]], observation_matrices=[1, 2])

                filterestimates = kf.filter(y.numpy())

                if filtmeans.ndim < 2:
                    filtmeans = filtmeans[:, None]

                rel_error = np.median(np.abs((filtmeans - filterestimates[0]) / filterestimates[0]))

                ll = kf.loglikelihood(y.numpy())
                rel_ll_error = np.abs((ll - np.array(filt.s_ll).sum()) / ll)

                assert rel_error < 0.05 and rel_ll_error < 0.05

    def test_ParallellFiltersAndStability(self):
        x, y = self.model.sample_path(50)

        shape = 3000

        linear = AffineProcess((f, g), (1., 1.), self.norm, self.norm)
        self.model.hidden = linear

        filt = SISR(self.model, 1000).set_nparallel(shape).initialize().longfilter(y)

        filtermeans = filt.filtermeans

        x = filtermeans[:, :1]
        mape = ((x - filtermeans[:, 1:]) / x).abs()

        assert mape.median(0)[0].max() < 0.05

    def test_ParallelUnscented(self):
        x, y = self.model.sample_path(50)

        shape = 30

        linear = AffineProcess((f, g), (1., 1.), self.norm, self.norm)
        self.model.hidden = linear

        filt = SISR(self.model, 1000, proposal=Unscented()).set_nparallel(shape).initialize().longfilter(y)

        filtermeans = filt.filtermeans

        x = filtermeans[:, :1]
        mape = ((x - filtermeans[:, 1:]) / x).abs()

        assert mape.median(0)[0].max() < 0.05

    def test_SDE(self):
        def f(x, a, s):
            return -a * x

        def g(x, a, s):
            return s

        em = AffineEulerMaruyama((f, g), (0.02, 0.15), Normal(0., 1.), Normal(0., 1.), dt=1e-2, num_steps=10)
        model = LinearGaussianObservations(em, scale=1e-3)

        x, y = model.sample_path(500)

        with self.assertRaises(NotImplementedError):
            SISR(model, 200)

        for filt in [SISR(model, 500, proposal=Bootstrap()), UKF(model)]:
            filt = filt.initialize().longfilter(y)

            means = filt.filtermeans
            if isinstance(filt, UKF):
                means = means[:, 0]

            self.assertLess(torch.std(x - means), 5e-2)
Exemple #19
0
class Tests(unittest.TestCase):
    # ===== Simple 1D model ===== #
    norm = DistributionWrapper(Normal, loc=0.0, scale=1.0)
    linear = AffineProcess((f, g), (1.0, 1.0), norm, norm)
    model = LinearGaussianObservations(linear, 1.0, 1.0)

    # ===== Simple 2D model ===== #
    mvn = DistributionWrapper(lambda **u: Independent(Normal(**u), 1), loc=torch.zeros(2), scale=torch.ones(2))
    mvn = AffineProcess((fmvn, gmvn), (0.5, 1.0), mvn, mvn)
    a = torch.tensor([1.0, 2.0])

    mvnmodel = LinearGaussianObservations(mvn, a, 1.0)

    def test_InitializeFilter(self):
        state = SISR(self.model, 1000).initialize()

        assert state.x.shape == torch.Size([1000])

    def test_Filters(self):
        for model in [self.model, self.mvnmodel]:
            x, y = model.sample_path(500)

            for filter_type, props in [
                (SISR, {"particles": 500}),
                (APF, {"particles": 500}),
                (UKF, {}),
                (SISR, {"particles": 50, "proposal": prop.Unscented()}),
            ]:
                filt = filter_type(model, **props)
                result = filt.longfilter(y, record_states=True)

                filtmeans = result.filter_means.numpy()

                # ===== Run Kalman ===== #
                if model is self.model:
                    kf = pykalman.KalmanFilter(transition_matrices=1.0, observation_matrices=1.0)
                else:
                    kf = pykalman.KalmanFilter(
                        transition_matrices=[[0.5, 1 / 3], [0, 1.0]], observation_matrices=[1, 2]
                    )

                f_mean, _ = kf.filter(y.numpy())

                if model.hidden_ndim < 1 and not isinstance(filt, UKF):
                    f_mean = f_mean[:, 0]

                rel_error = np.median(np.abs((filtmeans - f_mean) / f_mean))

                ll = kf.loglikelihood(y.numpy())
                rel_ll_error = np.abs((ll - result.loglikelihood.numpy()) / ll)

                assert rel_error < 0.05 and rel_ll_error < 0.05

    def test_ParallellFiltersAndStability(self):
        x, y = self.model.sample_path(50)

        shape = 3000

        linear = AffineProcess((f, g), (1.0, 1.0), self.norm, self.norm)
        self.model.hidden = linear

        filt = SISR(self.model, 1000).set_nparallel(shape)
        result = filt.longfilter(y)

        filtermeans = result.filter_means

        x = filtermeans[:, :1]
        mape = ((x - filtermeans[:, 1:]) / x).abs()

        assert mape.median(0)[0].max() < 0.05

    def test_ParallelUnscented(self):
        x, y = self.model.sample_path(50)

        shape = 30

        linear = AffineProcess((f, g), (1.0, 1.0), self.norm, self.norm)
        self.model.hidden = linear

        filt = SISR(self.model, 1000, proposal=prop.Unscented()).set_nparallel(shape)
        result = filt.longfilter(y)

        filtermeans = result.filter_means

        x = filtermeans[:, :1]
        mape = ((x - filtermeans[:, 1:]) / x).abs()

        assert mape.median(0)[0].max() < 0.05

    def test_SDE(self):
        def f(x, a, s):
            return -a * x

        def g(x, a, s):
            return s

        dt = 1e-2
        norm = DistributionWrapper(Normal, loc=0.0, scale=sqrt(dt))

        em = AffineEulerMaruyama((f, g), (0.02, 0.15), norm, norm, dt=1e-2, num_steps=10)
        model = LinearGaussianObservations(em, scale=1e-3)

        x, y = model.sample_path(500)

        for filt in [SISR(model, 500, proposal=prop.Bootstrap()), UKF(model)]:
            result = filt.longfilter(y)

            means = result.filter_means
            if isinstance(filt, UKF):
                means = means[:, 0]

            self.assertLess(torch.std(x - means), 5e-2)
Exemple #20
0
def build_model():
    norm = DistributionWrapper(Normal, loc=0.0, scale=1.0)
    return AffineProcess((f, g), (1.0, 1.0), norm, norm)