def test_region_est_ellipsoid(self):
        """
        Tests that region_est_ellipsoid works.
        """

        dist = MultivariateNormalDistribution(self.MEAN, self.COV)
        u = ParticleDistribution(
            particle_locations = dist.sample(self.N_PARTICLES),
            particle_weights = np.ones(self.N_PARTICLES)/self.N_PARTICLES
        )

        # ask for a confidence level of 0.5
        A, c = u.region_est_ellipsoid(level=0.5)

        # center of ellipse should be the mean of the multinormal
        assert_almost_equal(np.round(c), self.MEAN, 1)

        # finally, the principal lengths of the ellipsoid
        # should be the same as COV
        _, QA, _ = np.linalg.svd(A)
        _, QC, _ = np.linalg.svd(self.COV)
        QA, QC = np.sqrt(QA), np.sqrt(QC)
        assert_almost_equal(
            QA / np.linalg.norm(QA),
            QC / np.linalg.norm(QC),
            1
        )
    def test_est_credible_region(self):
        """
        Tests that est_credible_region doesn't fail miserably
        """
        dist = MultivariateNormalDistribution(self.MEAN, self.COV)
        u = ParticleDistribution(
            particle_locations = dist.sample(self.N_PARTICLES),
            particle_weights = np.ones(self.N_PARTICLES)/self.N_PARTICLES
        )

        # first check that 0.95 confidence points consume 0.9 confidence points
        points1 = u.est_credible_region(level=0.95)
        points2 = u.est_credible_region(level=0.9)
        assert_almost_equal(
            np.sort(unique_rows(np.concatenate([points1, points2])), axis=0),
            np.sort(points1, axis=0)
        )

        # do the same thing with different slice
        points1 = u.est_credible_region(level=0.95, modelparam_slice=self.SLICE)
        points2 = u.est_credible_region(level=0.9, modelparam_slice=self.SLICE)
        assert_almost_equal(
            np.sort(unique_rows(np.concatenate([points1, points2])), axis=0),
            np.sort(points1, axis=0)
        )
    def test_region_est_hull(self):
        """
        Tests that test_region_est_hull works
        """
        dist = MultivariateNormalDistribution(self.MEAN, self.COV)
        u = ParticleDistribution(
            particle_locations = dist.sample(self.N_PARTICLES),
            particle_weights = np.ones(self.N_PARTICLES)/self.N_PARTICLES
        )

        faces, vertices = u.region_est_hull(level=0.95)

        # In this multinormal case, the convex hull surface
        # should be centered at MEAN
        assert_almost_equal(
            np.round(np.mean(vertices, axis=0)),
            np.round(self.MEAN)
        )

        # And a lower level should result in a smaller hull
        # and therefore smaller sample variance
        faces2, vertices2 = u.region_est_hull(level=0.2)
        assert_array_less(np.var(vertices2, axis=0), np.var(vertices, axis=0))
    def test_in_credible_region(self):
        """
        Tests that in_credible_region works.
        """

        dist = MultivariateNormalDistribution(self.MEAN, self.COV)
        u = ParticleDistribution(
            particle_locations = dist.sample(self.N_PARTICLES),
            particle_weights = np.ones(self.N_PARTICLES)/self.N_PARTICLES
        )

        # some points to test with
        test_points = np.random.multivariate_normal(self.MEAN, self.COV, self.N_PARTICLES)

        # method='pce'
        results = [
            u.in_credible_region(test_points, level=0.9, method='pce'),
            u.in_credible_region(test_points, level=0.84, method='pce'),
            u.in_credible_region(test_points, level=0.5, method='pce'),
        ]
        assert_almost_equal(
            np.array([np.mean(x.astype('float')) for x in results]),
            np.array([0.9, 0.84, 0.5]),
            3
        )

        # method='hpd-hull'
        results1 = [
            u.in_credible_region(test_points, level=0.9, method='hpd-hull'),
            u.in_credible_region(test_points, level=0.84, method='hpd-hull'),
            u.in_credible_region(test_points, level=0.5, method='hpd-hull'),
        ]
        assert_array_less(
            np.array([0.9, 0.84, 0.5]),
            np.array([np.mean(x.astype('float')) for x in results1])
        )

        # method='hpd-mvee'
        results2 = [
            u.in_credible_region(test_points, level=0.9, method='hpd-mvee'),
            u.in_credible_region(test_points, level=0.84, method='hpd-mvee'),
            u.in_credible_region(test_points, level=0.5, method='hpd-mvee'),
        ]
        assert_array_less(
            np.array([0.9, 0.84, 0.5]),
            np.array([np.mean(x.astype('float')) for x in results2])
        )

        # the mvee should be bigger than the convex hull.
        # this passes iff all points in the ellipses are
        # also in the hulls.
        assert_array_less(
            np.hstack([x.astype('float') for x in results1]),
            np.hstack([x.astype('float') for x in results2]) + 0.5
        )

        # check for no failures with slices.
        u.in_credible_region(test_points[:100,self.SLICE], level=0.9, method='pce', modelparam_slice=self.SLICE)
        u.in_credible_region(test_points[:100,self.SLICE], level=0.9, method='hpd-hull', modelparam_slice=self.SLICE)
        u.in_credible_region(test_points[:100,self.SLICE], level=0.9, method='hpd-mvee', modelparam_slice=self.SLICE)

        # check for no failures with single inputs
        assert(u.in_credible_region(test_points[0,:], level=0.9, method='pce').size == 1)
        assert(u.in_credible_region(test_points[0,:], level=0.9, method='hpd-hull').size == 1)
        assert(u.in_credible_region(test_points[0,:], level=0.9, method='hpd-mvee').size == 1)
Exemplo n.º 5
0
    def __call__(self,
                 model,
                 particle_dist,
                 n_particles=None,
                 precomputed_mean=None,
                 precomputed_cov=None):
        """
        Resample the particles according to algorithm given in
        [LW01]_.
        """

        # Possibly recompute moments, if not provided.
        if precomputed_mean is None:
            mean = particle_dist.est_mean()
        else:
            mean = precomputed_mean
        if precomputed_cov is None:
            cov = particle_dist.est_covariance_mtx()
        else:
            cov = precomputed_cov

        if n_particles is None:
            if self._default_n_particles is None:
                n_particles = particle_dist.n_particles
            else:
                n_particles = self._default_n_particles

        # parameters in the Liu and West algorithm
        a, h = self._a, self._h
        if la.norm(cov, 'fro') == 0:
            # The norm of the square root of S is literally zero, such that
            # the error estimated in the next step will not make sense.
            # We fix that by adding to the covariance a tiny bit of the
            # identity.
            warnings.warn(
                "Covariance has zero norm; adding in small covariance in "
                "resampler. Consider increasing n_particles to improve covariance "
                "estimates.", ResamplerWarning)
            cov = self._zero_cov_comp * np.eye(cov.shape[0])
        S, S_err = sqrtm_psd(cov)
        if not np.isfinite(S_err):
            raise ResamplerError(
                "Infinite error in computing the square root of the "
                "covariance matrix. Check that n_ess is not too small.")
        S = np.real(h * S)

        # Give shorter names to weights, locations, and nr. of random variables
        w = particle_dist.particle_weights
        l = particle_dist.particle_locations
        n_rvs = particle_dist.n_rvs

        new_locs = np.empty((n_particles, n_rvs))
        cumsum_weights = np.cumsum(w)

        idxs_to_resample = np.arange(n_particles, dtype=int)

        # Loop as long as there are any particles left to resample.
        n_iters = 0

        # Draw j with probability self.particle_weights[j].
        # We do this by drawing random variates uniformly on the interval
        # [0, 1], then see where they belong in the CDF.
        js = cumsum_weights.searchsorted(np.random.random(
            (idxs_to_resample.size, )),
                                         side='right')

        # Set mu_i to a x_j + (1 - a) mu.
        # FIXME This should use particle_dist.particle_mean
        mus = a * l[js, :] + (1 - a) * mean

        while idxs_to_resample.size and n_iters < self._maxiter:
            # Keep track of how many iterations we used.
            n_iters += 1

            # Draw x_i from N(mu_i, S).
            new_locs[idxs_to_resample, :] = mus + np.dot(
                S, self._kernel(n_rvs, mus.shape[0])).T

            # Now we remove from the list any valid models.
            # We write it out in a longer form than is strictly necessary so
            # that we can validate assertions as we go. This is helpful for
            # catching models that may not hold to the expected postconditions.
            resample_locs = new_locs[idxs_to_resample, :]
            if self._postselect:
                valid_mask = model.are_models_valid(resample_locs)
            else:
                valid_mask = np.ones((resample_locs.shape[0], ), dtype=bool)

            assert valid_mask.ndim == 1, "are_models_valid returned tensor, expected vector."

            n_invalid = np.sum(np.logical_not(valid_mask))

            if self._debug and n_invalid > 0:
                logger.debug(
                    "LW resampler found {} invalid particles; repeating.".
                    format(n_invalid))

            assert ((len(valid_mask.shape) == 1 or len(valid_mask.shape) == 2
                     and valid_mask.shape[-1] == 1)
                    and valid_mask.shape[0] == resample_locs.shape[0]), (
                        "are_models_valid returned wrong shape {} "
                        "for input of shape {}.").format(
                            valid_mask.shape, resample_locs.shape)

            idxs_to_resample = idxs_to_resample[np.nonzero(
                np.logical_not(valid_mask))[0]]

            # This may look a little weird, but it should delete the unused
            # elements of js, so that we don't need to reallocate.
            js = js[np.logical_not(valid_mask)]
            mus = mus[:idxs_to_resample.size, :]

        if idxs_to_resample.size:
            # We failed to force all models to be valid within maxiter attempts.
            # This means that we could be propagating out invalid models, and
            # so we should warn about that.
            warnings.warn(
                ("Liu-West resampling failed to find valid models for {} "
                 "particles within {} iterations.").format(
                     idxs_to_resample.size, self._maxiter), ResamplerWarning)

        if self._debug:
            logger.debug(
                "LW resampling completed in {} iterations.".format(n_iters))

        # Now we reset the weights to be uniform, letting the density of
        # particles represent the information that used to be stored in the
        # weights. This is done by SMCUpdater, and so we simply need to return
        # the new locations here.
        new_weights = np.ones((n_particles, )) / n_particles
        return ParticleDistribution(particle_locations=new_locs,
                                    particle_weights=new_weights)